@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,194 @@
1
+ // feat-20260709 M2 / w5: query flat-column A/B perf ratio bench (AC-09, D-2/D-9).
2
+ //
3
+ // In-process ratio gate, NOT absolute wallclock (memory
4
+ // absolute-wallclock-perf-gate-flakes-use-in-process-ratio): the same
5
+ // process, same JIT tier, and same allocator time both sides, so the
6
+ // candidate/baseline ratio isolates the column-shape effect from host noise.
7
+ //
8
+ // Baseline: 10 per-axis f32 scalar columns (the pre-migration Transform
9
+ // shape, synthesized here so the gate survives the migration).
10
+ // Candidate: pos array<f32,3> / quat array<f32,4> / scale array<f32,3>
11
+ // (the post-migration shape), read via the query bundle's flat
12
+ // stride-N subarray -- `pos[i*3+a]`, zero per-call allocation
13
+ // (research Finding 5 adjudication table, allowed row).
14
+ //
15
+ // Gate: ratio = candidate_min / baseline_min <= 1.15 (N=10_000 entities,
16
+ // min-of-5 rounds, sides alternate per round to cancel warm-up drift).
17
+ // Expected ratio is ~1.0 or below (3/4-wide contiguous lanes have better
18
+ // locality than 10 scattered columns); 1.15 is anti-noise headroom, not a
19
+ // performance concession.
20
+ //
21
+ // Falsifiability (plan-strategy 5.4, one-time manual check, not in CI):
22
+ // swapping the candidate reader to the `world.get(e, C).pos` materializing
23
+ // path makes the ratio blow past the gate -- verified once by hand during
24
+ // w5 (see milestone report), then restored to the flat form below.
25
+ //
26
+ // skipIf escape hatch (D-2): default OFF. If a specific CI host proves
27
+ // noisy, gate on an env flag here (e.g. FORGEAX_SKIP_TRS_RATIO_BENCH) --
28
+ // deliberately not wired until real-world flake data demands it.
29
+
30
+ import { describe, expect, it } from 'vitest';
31
+ import { defineComponent } from '../component';
32
+ import { World } from '../world';
33
+
34
+ const N = 10_000;
35
+ const ROUNDS = 5;
36
+ const INNER_REPEATS = 300;
37
+ const RATIO_GATE = 1.15;
38
+
39
+ const BaselineTrs10 = defineComponent('W5_BaselineTrs10', {
40
+ posX: 'f32',
41
+ posY: 'f32',
42
+ posZ: 'f32',
43
+ quatX: 'f32',
44
+ quatY: 'f32',
45
+ quatZ: 'f32',
46
+ quatW: 'f32',
47
+ scaleX: 'f32',
48
+ scaleY: 'f32',
49
+ scaleZ: 'f32',
50
+ });
51
+
52
+ const CandidateTrsVec = defineComponent('W5_CandidateTrsVec', {
53
+ pos: { type: 'array<f32, 3>', default: new Float32Array(3) },
54
+ quat: { type: 'array<f32, 4>', default: new Float32Array([0, 0, 0, 1]) },
55
+ scale: { type: 'array<f32, 3>', default: new Float32Array([1, 1, 1]) },
56
+ });
57
+
58
+ function spawnBaselineWorld(): World {
59
+ const world = new World();
60
+ for (let i = 0; i < N; i++) {
61
+ world.spawn({
62
+ component: BaselineTrs10,
63
+ data: {
64
+ posX: i,
65
+ posY: i * 2,
66
+ posZ: i * 3,
67
+ quatX: 0,
68
+ quatY: 0,
69
+ quatZ: 0,
70
+ quatW: 1,
71
+ scaleX: 1,
72
+ scaleY: 1,
73
+ scaleZ: 1,
74
+ },
75
+ });
76
+ }
77
+ return world;
78
+ }
79
+
80
+ function spawnCandidateWorld(): World {
81
+ const world = new World();
82
+ for (let i = 0; i < N; i++) {
83
+ world.spawn({
84
+ component: CandidateTrsVec,
85
+ data: { pos: [i, i * 2, i * 3], quat: [0, 0, 0, 1], scale: [1, 1, 1] },
86
+ });
87
+ }
88
+ return world;
89
+ }
90
+
91
+ /** Sum all 10 TRS lanes over every entity via per-axis scalar columns. */
92
+ function traverseBaseline(world: World): number {
93
+ const query = world.query({ read: [BaselineTrs10] }).unwrap();
94
+ let sum = 0;
95
+ for (let rep = 0; rep < INNER_REPEATS; rep++) {
96
+ for (const span of query.spans().unwrap()) {
97
+ const b = span.get(BaselineTrs10);
98
+ const n = span.length;
99
+ for (let i = 0; i < n; i++) {
100
+ sum +=
101
+ (b.posX[i] as number) +
102
+ (b.posY[i] as number) +
103
+ (b.posZ[i] as number) +
104
+ (b.quatX[i] as number) +
105
+ (b.quatY[i] as number) +
106
+ (b.quatZ[i] as number) +
107
+ (b.quatW[i] as number) +
108
+ (b.scaleX[i] as number) +
109
+ (b.scaleY[i] as number) +
110
+ (b.scaleZ[i] as number);
111
+ }
112
+ }
113
+ }
114
+ return sum;
115
+ }
116
+
117
+ /** Sum all 10 TRS lanes over every entity via flat stride-N array columns. */
118
+ function traverseCandidate(world: World): number {
119
+ const query = world.query({ read: [CandidateTrsVec] }).unwrap();
120
+ let sum = 0;
121
+ for (let rep = 0; rep < INNER_REPEATS; rep++) {
122
+ for (const span of query.spans().unwrap()) {
123
+ const c = span.get(CandidateTrsVec);
124
+ const pos = c.pos;
125
+ const quat = c.quat;
126
+ const scale = c.scale;
127
+ const n = span.length;
128
+ for (let i = 0; i < n; i++) {
129
+ sum +=
130
+ (pos[i * 3] as number) +
131
+ (pos[i * 3 + 1] as number) +
132
+ (pos[i * 3 + 2] as number) +
133
+ (quat[i * 4] as number) +
134
+ (quat[i * 4 + 1] as number) +
135
+ (quat[i * 4 + 2] as number) +
136
+ (quat[i * 4 + 3] as number) +
137
+ (scale[i * 3] as number) +
138
+ (scale[i * 3 + 1] as number) +
139
+ (scale[i * 3 + 2] as number);
140
+ }
141
+ }
142
+ }
143
+ return sum;
144
+ }
145
+
146
+ describe('w5 -- TRS flat-column A/B perf ratio (AC-09)', () => {
147
+ it(`candidate/baseline traversal ratio stays <= ${RATIO_GATE} (min-of-${ROUNDS}, N=${N})`, () => {
148
+ const baselineWorld = spawnBaselineWorld();
149
+ const candidateWorld = spawnCandidateWorld();
150
+
151
+ // Warm-up: bring both traversal fns to the same JIT tier before timing.
152
+ let sink = 0;
153
+ sink += traverseBaseline(baselineWorld);
154
+ sink += traverseCandidate(candidateWorld);
155
+
156
+ const baselineTimes: number[] = [];
157
+ const candidateTimes: number[] = [];
158
+
159
+ for (let round = 0; round < ROUNDS; round++) {
160
+ // Alternate side order per round to cancel residual warm-up drift.
161
+ const baselineFirst = round % 2 === 0;
162
+ if (baselineFirst) {
163
+ const b0 = performance.now();
164
+ sink += traverseBaseline(baselineWorld);
165
+ baselineTimes.push(performance.now() - b0);
166
+ const c0 = performance.now();
167
+ sink += traverseCandidate(candidateWorld);
168
+ candidateTimes.push(performance.now() - c0);
169
+ } else {
170
+ const c0 = performance.now();
171
+ sink += traverseCandidate(candidateWorld);
172
+ candidateTimes.push(performance.now() - c0);
173
+ const b0 = performance.now();
174
+ sink += traverseBaseline(baselineWorld);
175
+ baselineTimes.push(performance.now() - b0);
176
+ }
177
+ }
178
+
179
+ // Both sides sum identical values -- equality doubles as a correctness
180
+ // check and keeps the traversal loops from being dead-code-eliminated.
181
+ expect(sink).toBeGreaterThan(0);
182
+
183
+ const baselineMin = Math.min(...baselineTimes);
184
+ const candidateMin = Math.min(...candidateTimes);
185
+ const ratio = candidateMin / baselineMin;
186
+
187
+ // biome-ignore lint/suspicious/noConsole: surface the measured ratio in test output for AC-09 review
188
+ console.info(
189
+ `[w5 perf ratio] baseline_min=${baselineMin.toFixed(3)}ms candidate_min=${candidateMin.toFixed(3)}ms ratio=${ratio.toFixed(3)}`,
190
+ );
191
+
192
+ expect(ratio).toBeLessThanOrEqual(RATIO_GATE);
193
+ });
194
+ });
@@ -0,0 +1,196 @@
1
+ import { describe, expect, expectTypeOf, it, vi } from 'vitest';
2
+ import { componentId, defineComponent } from '../component';
3
+ import type { EntityHandle } from '../entity-handle';
4
+ import {
5
+ QueryDataRequiresFieldsError,
6
+ QueryDescriptorConflictError,
7
+ QueryIterationActiveError,
8
+ QueryIterationInvalidatedError,
9
+ } from '../errors';
10
+ import type { QueryRow } from '../query/query';
11
+ import type { Table } from '../storage/table';
12
+ import { World } from '../world';
13
+
14
+ import { worldInternal } from '../world-internal';
15
+
16
+ const Position = defineComponent('QueryPosition', { x: 'f32', y: 'f32' });
17
+ const Velocity = defineComponent('QueryVelocity', { x: 'f32', y: 'f32' });
18
+ const Selected = defineComponent('QuerySelected', {});
19
+
20
+ function createWorld(): { world: World; first: EntityHandle; second: EntityHandle } {
21
+ const world = new World();
22
+ const first = world
23
+ .spawn(
24
+ { component: Position, data: { x: 1, y: 2 } },
25
+ { component: Velocity, data: { x: 3, y: 4 } },
26
+ { component: Selected, data: {} },
27
+ )
28
+ .unwrap();
29
+ const second = world.spawn({ component: Position, data: { x: 5, y: 6 } }).unwrap();
30
+ return { world, first, second };
31
+ }
32
+
33
+ describe('executable Query', () => {
34
+ it('validates access roles and tag data at creation', () => {
35
+ const world = new World();
36
+ const conflict = world.query({ read: [Position], write: [Position] });
37
+ expect(conflict.ok).toBe(false);
38
+ if (!conflict.ok) {
39
+ expect(conflict.error).toBeInstanceOf(QueryDescriptorConflictError);
40
+ expect(conflict.error.detail).toEqual({
41
+ componentName: Position.name,
42
+ roles: ['read', 'write'],
43
+ });
44
+ }
45
+
46
+ const tagData = world.query({ read: [Selected] });
47
+ expect(tagData.ok).toBe(false);
48
+ if (!tagData.ok) expect(tagData.error).toBeInstanceOf(QueryDataRequiresFieldsError);
49
+ });
50
+
51
+ it('iterates each matching entity once and exposes optional data precisely', () => {
52
+ const { world, first, second } = createWorld();
53
+ const query = world
54
+ .query({ read: [Position], optional: [Velocity], with: [Selected] })
55
+ .unwrap();
56
+ const seen: number[] = [];
57
+ for (const row of query) {
58
+ seen.push(row.entity);
59
+ expect(row.has(Position)).toBe(true);
60
+ expect(row.has(Velocity)).toBe(true);
61
+ expect(row.get(Position).x).toBe(1);
62
+ expect(row.get(Velocity)?.x).toBe(3);
63
+ }
64
+ expect(seen).toEqual([first]);
65
+ expect(seen).not.toContain(second);
66
+ });
67
+
68
+ it('addresses one matching entity without scanning the query tables', () => {
69
+ const { world, first, second } = createWorld();
70
+ const query = world
71
+ .query({ read: [Position], optional: [Velocity], with: [Selected] })
72
+ .unwrap();
73
+
74
+ const row = query.at(first);
75
+ expect(row?.entity).toBe(first);
76
+ expect(row?.get(Position)?.x).toBe(1);
77
+ expect(query.at(second)).toBeUndefined();
78
+ });
79
+
80
+ it('checks optional presence without materialising a component row', () => {
81
+ const { world } = createWorld();
82
+ const query = world.query({ read: [Position], optional: [Velocity] }).unwrap();
83
+ const readRowSpy = vi.spyOn(world[worldInternal], 'getQueryRow');
84
+
85
+ const presence: boolean[] = [];
86
+ for (const row of query) presence.push(row.has(Velocity));
87
+ expect(presence).toEqual([true, false]);
88
+ expect(readRowSpy).not.toHaveBeenCalled();
89
+ });
90
+
91
+ it('row.mut marks one epoch and writes through the component owner', () => {
92
+ const { world, first } = createWorld();
93
+ const query = world.query({ write: [Position], with: [Selected] }).unwrap();
94
+ const before = world[worldInternal].getMutationEpoch();
95
+ for (const row of query) {
96
+ const position = row.mut(Position);
97
+ position.x += 10;
98
+ position.y = 20;
99
+ }
100
+ expect(world[worldInternal].getMutationEpoch()).toBe(before + 1);
101
+ expect(world.get(first, Position).unwrap()).toMatchObject({ x: 11, y: 20 });
102
+ });
103
+
104
+ it('spans are zero-copy and span.mut marks the whole range once', () => {
105
+ const { world, first, second } = createWorld();
106
+ const query = world.query({ write: [Position] }).unwrap();
107
+ const spans = query.spans().unwrap();
108
+ const before = world[worldInternal].getMutationEpoch();
109
+ const buffers = new Set(
110
+ world[worldInternal].getGraph().tables.flatMap((table: Table) => {
111
+ const buffer = table.storage.get(componentId(Position))?.fields.get('x')?.view.buffer;
112
+ return buffer === undefined ? [] : [buffer];
113
+ }),
114
+ );
115
+ let count = 0;
116
+ let spanCount = 0;
117
+ let nextX = 42;
118
+ for (const span of spans) {
119
+ spanCount += 1;
120
+ const positions = span.mut(Position);
121
+ count += span.length;
122
+ for (let index = 0; index < span.length; index++) positions.x[index] = nextX++;
123
+ expect(buffers.has(positions.x.buffer)).toBe(true);
124
+ }
125
+ expect(count).toBe(2);
126
+ expect(world[worldInternal].getMutationEpoch()).toBe(before + spanCount);
127
+ expect(world.get(first, Position).unwrap().x).toBe(42);
128
+ expect(world.get(second, Position).unwrap().x).toBe(43);
129
+ });
130
+
131
+ it('exposes packed readonly entity identities on each span', () => {
132
+ const { world, first, second } = createWorld();
133
+ const query = world.query({ read: [Position] }).unwrap();
134
+ const spans = [...query.spans().unwrap()];
135
+
136
+ expect(spans.reduce((count, span) => count + span.length, 0)).toBe(2);
137
+ expect(spans.flatMap((span) => Array.from(span.entities))).toEqual([first, second]);
138
+ expect(spans[0]?.get(Position).x[0]).toBe(1);
139
+ });
140
+
141
+ it('returns structured span capability failures', () => {
142
+ const world = new World();
143
+ const optional = world
144
+ .query({ read: [Position], optional: [Velocity] })
145
+ .unwrap()
146
+ .spans();
147
+ expect(!optional.ok && optional.error.detail.reason).toBe('optional-data');
148
+
149
+ const changed = world
150
+ .query({ read: [Position], changed: [Position] })
151
+ .unwrap()
152
+ .spans();
153
+ expect(!changed.ok && changed.error.detail.reason).toBe('row-change-filter');
154
+ });
155
+
156
+ it('does not commit observation after partial iteration', () => {
157
+ const { world, first, second } = createWorld();
158
+ const query = world.query({ read: [Position], changed: [Position] }).unwrap();
159
+ const iterator = query[Symbol.iterator]();
160
+ expect(iterator.next().done).toBe(false);
161
+ iterator.return?.();
162
+
163
+ const seen: EntityHandle[] = [];
164
+ for (const row of query) seen.push(row.entity);
165
+ expect(seen).toEqual([first, second]);
166
+ });
167
+
168
+ it('keeps same-frame late writes for the next observation', () => {
169
+ const { world, first } = createWorld();
170
+ const query = world.query({ read: [Position], changed: [Position] }).unwrap();
171
+ expect([...query].length).toBe(2);
172
+ world.set(first, Position, { x: 9 }).unwrap();
173
+ const seen: EntityHandle[] = [];
174
+ for (const row of query) seen.push(row.entity);
175
+ expect(seen).toEqual([first]);
176
+ });
177
+
178
+ it('fails fast on reentry and synchronous structural invalidation', () => {
179
+ const { world } = createWorld();
180
+ const query = world.query({ read: [Position] }).unwrap();
181
+ const iterator = query[Symbol.iterator]();
182
+ expect(iterator.next().done).toBe(false);
183
+ expect(() => query[Symbol.iterator]()).toThrow(QueryIterationActiveError);
184
+ iterator.return?.();
185
+
186
+ const invalidated = query[Symbol.iterator]();
187
+ expect(invalidated.next().done).toBe(false);
188
+ world.spawn({ component: Position, data: { x: 0, y: 0 } }).unwrap();
189
+ expect(() => invalidated.next()).toThrow(QueryIterationInvalidatedError);
190
+ });
191
+
192
+ it('infers row access without casts', () => {
193
+ type Row = QueryRow<readonly [typeof Position], readonly [typeof Velocity], readonly []>;
194
+ expectTypeOf<Row['entity']>().toBeNumber();
195
+ });
196
+ });
@@ -0,0 +1,126 @@
1
+ import os from 'node:os';
2
+ import { describe, expect, it } from 'vitest';
3
+ import type { EntityHandle } from '../entity-handle';
4
+ import { defineRelationship } from '../relationship-index';
5
+ import { World } from '../world';
6
+
7
+ const SAMPLE_ROUNDS = 7;
8
+ const PARENT_COUNT = 8;
9
+ const CHILD_COUNT = 512;
10
+ const pair = defineRelationship({
11
+ sourceName: 'PerfWorldRelationshipSource',
12
+ sourceField: 'target',
13
+ targetName: 'PerfWorldRelationshipTargets',
14
+ targetField: 'sources',
15
+ });
16
+
17
+ type SampleStats = {
18
+ readonly medianMs: number;
19
+ readonly p95Ms: number;
20
+ readonly ci95Ms: readonly [number, number];
21
+ readonly sampleCount: number;
22
+ };
23
+
24
+ function percentile(samples: readonly number[], quantile: number): number {
25
+ const sorted = [...samples].sort((a, b) => a - b);
26
+ return sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * quantile) - 1)] ?? 0;
27
+ }
28
+
29
+ function summarize(samples: readonly number[]): SampleStats {
30
+ const mean = samples.reduce((sum, sample) => sum + sample, 0) / samples.length;
31
+ const variance =
32
+ samples.reduce((sum, sample) => sum + (sample - mean) ** 2, 0) /
33
+ Math.max(1, samples.length - 1);
34
+ const margin = 1.96 * Math.sqrt(variance / samples.length);
35
+ return {
36
+ medianMs: percentile(samples, 0.5),
37
+ p95Ms: percentile(samples, 0.95),
38
+ ci95Ms: [mean - margin, mean + margin],
39
+ sampleCount: samples.length,
40
+ };
41
+ }
42
+
43
+ function makeFixture(): {
44
+ world: World;
45
+ parents: readonly EntityHandle[];
46
+ children: readonly EntityHandle[];
47
+ } {
48
+ const world = new World();
49
+ const parents = Array.from({ length: PARENT_COUNT }, () => world.spawn().unwrap());
50
+ const children: EntityHandle[] = [];
51
+ for (let index = 0; index < CHILD_COUNT; index += 1) {
52
+ const child = world.spawn().unwrap();
53
+ const parent = parents[index % PARENT_COUNT];
54
+ if (parent === undefined) throw new Error('relationship benchmark parent missing');
55
+ world.addChild(parent, child, pair.source, { target: parent }).unwrap();
56
+ children.push(child);
57
+ }
58
+ return { world, parents, children };
59
+ }
60
+
61
+ function runWorldWorkload(mode: 'baseline' | 'candidate'): number {
62
+ const { world, parents, children } = makeFixture();
63
+ for (let index = 0; index < children.length; index += 1) {
64
+ const child = children[index];
65
+ const nextParent = parents[(index + 1) % PARENT_COUNT];
66
+ if (child === undefined || nextParent === undefined)
67
+ throw new Error('benchmark entity missing');
68
+ world.reparent(child, nextParent, pair.source, { target: nextParent }).unwrap();
69
+ world.removeChild(nextParent, child, pair.source).unwrap();
70
+ world.addChild(nextParent, child, pair.source, { target: nextParent }).unwrap();
71
+ }
72
+
73
+ let count = 0;
74
+ if (mode === 'candidate') {
75
+ for (const parent of parents) count += world.get(parent, pair.target).unwrap().sources.length;
76
+ } else {
77
+ const sources = world.query({ read: [pair.source] }).unwrap();
78
+ for (const parent of parents) {
79
+ for (const row of sources) {
80
+ if (row.get(pair.source).target === parent) count += 1;
81
+ }
82
+ }
83
+ }
84
+ return count;
85
+ }
86
+
87
+ function measure(mode: 'baseline' | 'candidate'): SampleStats {
88
+ const samples: number[] = [];
89
+ for (let round = 0; round < SAMPLE_ROUNDS; round += 1) {
90
+ const start = performance.now();
91
+ const count = runWorldWorkload(mode);
92
+ samples.push(performance.now() - start);
93
+ if (count !== CHILD_COUNT) throw new Error(`relationship workload count mismatch: ${count}`);
94
+ }
95
+ return summarize(samples);
96
+ }
97
+
98
+ describe('M2 relationship World workload', () => {
99
+ it('collects same-semantics baseline/candidate attach-detach-reparent-read evidence', () => {
100
+ const baseline = measure('baseline');
101
+ const candidate = measure('candidate');
102
+ const environment = {
103
+ node: process.version,
104
+ platform: process.platform,
105
+ arch: process.arch,
106
+ cpu: os.cpus()[0]?.model,
107
+ };
108
+ const p95Stop = candidate.p95Ms > baseline.p95Ms * 1.05;
109
+ // biome-ignore lint/suspicious/noConsole: benchmark emits machine-readable evidence
110
+ console.info(
111
+ JSON.stringify({
112
+ schemaVersion: 1,
113
+ workload: 'world-relationship-attach-detach-reparent-read',
114
+ baseline,
115
+ candidate,
116
+ environment,
117
+ workloadShape: { parents: PARENT_COUNT, children: CHILD_COUNT, rounds: SAMPLE_ROUNDS },
118
+ stopRule: 'candidate p95 > baseline p95 * 1.05 requires human decision',
119
+ p95Stop,
120
+ }),
121
+ );
122
+ expect(baseline.sampleCount).toBe(SAMPLE_ROUNDS);
123
+ expect(candidate.sampleCount).toBe(SAMPLE_ROUNDS);
124
+ expect(p95Stop).toBe(false);
125
+ });
126
+ });
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { componentSchema } from '../component';
3
+ import { componentDefinition } from '../component-schema';
4
+ import { defineRelationship, RelationshipIndex, relationshipRole } from '../relationship-index';
5
+ import { World } from '../world';
6
+
7
+ describe('relationship index', () => {
8
+ it('keeps source writable and target materialized/read-only by role', () => {
9
+ const pair = defineRelationship({
10
+ sourceName: 'IndexSource',
11
+ sourceField: 'target',
12
+ targetName: 'IndexTargets',
13
+ targetField: 'sources',
14
+ });
15
+ expect(relationshipRole(pair.source)?.kind).toBe('source');
16
+ expect(relationshipRole(pair.target)?.kind).toBe('target');
17
+ expect(componentDefinition(pair.target).fields.sources?.transient).toBe(true);
18
+ expect(componentSchema(pair.target).sources).toBe('array<entity>');
19
+ });
20
+
21
+ it('stores only source slots; World owns the materialized target array', () => {
22
+ const index = new RelationshipIndex();
23
+ const parentA = 1 as never;
24
+ const parentB = 2 as never;
25
+ const first = 11 as never;
26
+ const second = 12 as never;
27
+ index.attach(first, parentA, 0);
28
+ index.attach(second, parentA, 1);
29
+ expect(index.slotOf(second)).toBe(1);
30
+ expect(index.detach(first)).toBe(true);
31
+ index.updateSlot(second, parentA, 0);
32
+ expect(index.slotOf(second)).toBe(0);
33
+ index.reparent(second, parentB, 0);
34
+ expect(index.targetOf(second)).toBe(parentB);
35
+ expect(index.epoch).toBeGreaterThan(0);
36
+ });
37
+
38
+ it('recovers only from the supplied R source records', () => {
39
+ const index = new RelationshipIndex();
40
+ index.recover([
41
+ [11 as never, 1 as never],
42
+ [12 as never, 1 as never],
43
+ ]);
44
+ expect(index.slotOf(11 as never)).toBe(0);
45
+ expect(index.slotOf(12 as never)).toBe(1);
46
+ expect(index.targetOf(12 as never)).toBe(1);
47
+ });
48
+
49
+ it('materializes the target and rejects direct target writes', () => {
50
+ const pair = defineRelationship({
51
+ sourceName: 'WorldSource',
52
+ sourceField: 'target',
53
+ targetName: 'WorldTargets',
54
+ targetField: 'sources',
55
+ });
56
+ const world = new World();
57
+ const target = world.spawn().unwrap();
58
+ const source = world.spawn({ component: pair.source, data: { target } }).unwrap();
59
+ expect(world.get(target, pair.target).unwrap().sources).toContain(source);
60
+ // @ts-expect-error relationship targets are read-only projections.
61
+ expect(world.set(target, pair.target, { sources: [] }).ok).toBe(false);
62
+ // @ts-expect-error relationship targets are read-only projections.
63
+ expect(world.removeComponent(target, pair.target).ok).toBe(false);
64
+ // @ts-expect-error relationship targets are read-only projections.
65
+ expect(world.addComponent(target, { component: pair.target, data: { sources: [] } }).ok).toBe(
66
+ false,
67
+ );
68
+ world.spawn({ component: pair.target, data: { sources: [] } });
69
+ });
70
+ });
@@ -0,0 +1,71 @@
1
+ import { describe, expectTypeOf, it } from 'vitest';
2
+ import {
3
+ defineComponent,
4
+ type FieldValueType,
5
+ type ScalarFieldType,
6
+ type SchemaFieldType,
7
+ type SchemaOf,
8
+ type ShapeOf,
9
+ } from '../component';
10
+
11
+ const expectedScalarTypes = [
12
+ 'f32',
13
+ 'f64',
14
+ 'i32',
15
+ 'u32',
16
+ 'i16',
17
+ 'u16',
18
+ 'i8',
19
+ 'u8',
20
+ 'bool',
21
+ 'enum',
22
+ 'ref',
23
+ ] as const satisfies readonly ScalarFieldType[];
24
+ type ExpectedScalarFieldType = (typeof expectedScalarTypes)[number];
25
+
26
+ const ScalarFields = defineComponent('ScalarFieldTypeOwnerProof', {
27
+ f32: 'f32',
28
+ f64: 'f64',
29
+ i32: 'i32',
30
+ u32: 'u32',
31
+ i16: 'i16',
32
+ u16: 'u16',
33
+ i8: 'i8',
34
+ u8: 'u8',
35
+ bool: 'bool',
36
+ enum: 'enum',
37
+ ref: 'ref',
38
+ });
39
+
40
+ describe('ScalarFieldType owner', () => {
41
+ it('keeps the exact eleven-key scalar vocabulary', () => {
42
+ expectTypeOf<ScalarFieldType>().toEqualTypeOf<ExpectedScalarFieldType>();
43
+ expectTypeOf<ExpectedScalarFieldType>().toEqualTypeOf<ScalarFieldType>();
44
+ expectTypeOf(expectedScalarTypes).toMatchTypeOf<readonly SchemaFieldType[]>();
45
+
46
+ // @ts-expect-error unknown schema keywords remain outside the scalar union.
47
+ const invalid: ScalarFieldType = 'scalar-not-real';
48
+ void invalid;
49
+ });
50
+
51
+ it('preserves scalar schema acceptance and value declarations', () => {
52
+ type Shape = ShapeOf<SchemaOf<typeof ScalarFields>>;
53
+ expectTypeOf<Shape>().toEqualTypeOf<{
54
+ readonly f32: number;
55
+ readonly f64: number;
56
+ readonly i32: number;
57
+ readonly u32: number;
58
+ readonly i16: number;
59
+ readonly u16: number;
60
+ readonly i8: number;
61
+ readonly u8: number;
62
+ readonly bool: boolean;
63
+ readonly enum: number;
64
+ readonly ref: number;
65
+ }>();
66
+
67
+ expectTypeOf<FieldValueType<'f32'>>().toEqualTypeOf<number>();
68
+ expectTypeOf<FieldValueType<'bool'>>().toEqualTypeOf<boolean>();
69
+ expectTypeOf<FieldValueType<'ref'>>().toEqualTypeOf<number>();
70
+ });
71
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { FixedUpdate, Update } from '../schedule-token';
3
+ import { World } from '../world';
4
+
5
+ function runTrace(deltas: readonly number[]): string[] {
6
+ const world = new World();
7
+ const trace: string[] = [];
8
+ world.addSystem(Update, {
9
+ name: 'before-fixed',
10
+ queries: [],
11
+ before: [FixedUpdate],
12
+ fn: () => trace.push('before'),
13
+ });
14
+ world.addSystem(FixedUpdate, { name: 'fixed', queries: [], fn: () => trace.push('fixed') });
15
+ world.addSystem(Update, {
16
+ name: 'after-fixed',
17
+ queries: [],
18
+ after: [FixedUpdate],
19
+ fn: () => trace.push('after'),
20
+ });
21
+
22
+ for (const delta of deltas) expect(world.update(delta).ok).toBe(true);
23
+ return trace;
24
+ }
25
+
26
+ describe('schedule ordering determinism', () => {
27
+ it('produces the same trace for the same explicit delta sequence', () => {
28
+ const deltas = [0, 1 / 120, 1 / 120, 1 / 60, 0.01];
29
+
30
+ expect(runTrace(deltas)).toEqual(runTrace(deltas));
31
+ });
32
+
33
+ it('preserves before and after ordering around fixed runs', () => {
34
+ expect(runTrace([1 / 60])).toEqual(['before', 'fixed', 'after']);
35
+ });
36
+ });