@oasys/oecs 0.5.4 → 0.6.0

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 (510) hide show
  1. package/CHANGELOG.md +1223 -266
  2. package/README.md +217 -112
  3. package/dist/core/ecs/access_check.d.cts +29 -29
  4. package/dist/core/ecs/access_check.d.ts +29 -29
  5. package/dist/core/ecs/access_check.d.ts.map +1 -1
  6. package/dist/core/ecs/archetype.d.cts +217 -209
  7. package/dist/core/ecs/archetype.d.ts +217 -209
  8. package/dist/core/ecs/archetype.d.ts.map +1 -1
  9. package/dist/core/ecs/archetype_closure.d.cts +23 -0
  10. package/dist/core/ecs/archetype_closure.d.ts +23 -0
  11. package/dist/core/ecs/archetype_closure.d.ts.map +1 -0
  12. package/dist/core/ecs/archetype_graph.d.cts +33 -33
  13. package/dist/core/ecs/archetype_graph.d.ts +33 -33
  14. package/dist/core/ecs/archetype_graph.d.ts.map +1 -1
  15. package/dist/core/ecs/archetype_types.d.cts +55 -0
  16. package/dist/core/ecs/archetype_types.d.ts +55 -0
  17. package/dist/core/ecs/archetype_types.d.ts.map +1 -0
  18. package/dist/core/ecs/change_feed.d.cts +53 -0
  19. package/dist/core/ecs/change_feed.d.ts +53 -0
  20. package/dist/core/ecs/change_feed.d.ts.map +1 -0
  21. package/dist/core/ecs/changed_query.d.cts +36 -0
  22. package/dist/core/ecs/changed_query.d.ts +36 -0
  23. package/dist/core/ecs/changed_query.d.ts.map +1 -0
  24. package/dist/core/ecs/chunk_columns.d.cts +49 -0
  25. package/dist/core/ecs/chunk_columns.d.ts +49 -0
  26. package/dist/core/ecs/chunk_columns.d.ts.map +1 -0
  27. package/dist/core/ecs/command_log.d.cts +26 -26
  28. package/dist/core/ecs/command_log.d.ts +26 -26
  29. package/dist/core/ecs/command_log.d.ts.map +1 -1
  30. package/dist/core/ecs/component.d.cts +43 -42
  31. package/dist/core/ecs/component.d.ts +43 -42
  32. package/dist/core/ecs/component.d.ts.map +1 -1
  33. package/dist/core/ecs/component_registry.d.cts +53 -0
  34. package/dist/core/ecs/component_registry.d.ts +53 -0
  35. package/dist/core/ecs/component_registry.d.ts.map +1 -0
  36. package/dist/core/ecs/compute_backend.d.cts +18 -7
  37. package/dist/core/ecs/compute_backend.d.ts +18 -7
  38. package/dist/core/ecs/compute_backend.d.ts.map +1 -1
  39. package/dist/core/ecs/deferred_commands.d.cts +15 -14
  40. package/dist/core/ecs/deferred_commands.d.ts +15 -14
  41. package/dist/core/ecs/deferred_commands.d.ts.map +1 -1
  42. package/dist/core/ecs/dispatch_trace.d.cts +32 -31
  43. package/dist/core/ecs/dispatch_trace.d.ts +32 -31
  44. package/dist/core/ecs/dispatch_trace.d.ts.map +1 -1
  45. package/dist/core/ecs/ecs.d.cts +289 -270
  46. package/dist/core/ecs/ecs.d.ts +289 -270
  47. package/dist/core/ecs/ecs.d.ts.map +1 -1
  48. package/dist/core/ecs/ecs_memory.d.cts +134 -136
  49. package/dist/core/ecs/ecs_memory.d.ts +134 -136
  50. package/dist/core/ecs/ecs_memory.d.ts.map +1 -1
  51. package/dist/core/ecs/ecs_options.d.cts +89 -0
  52. package/dist/core/ecs/ecs_options.d.ts +89 -0
  53. package/dist/core/ecs/ecs_options.d.ts.map +1 -0
  54. package/dist/core/ecs/entity.d.cts +4 -4
  55. package/dist/core/ecs/entity.d.ts +4 -4
  56. package/dist/core/ecs/entity_allocator.d.cts +20 -18
  57. package/dist/core/ecs/entity_allocator.d.ts +20 -18
  58. package/dist/core/ecs/entity_allocator.d.ts.map +1 -1
  59. package/dist/core/ecs/event.d.cts +36 -22
  60. package/dist/core/ecs/event.d.ts +36 -22
  61. package/dist/core/ecs/event.d.ts.map +1 -1
  62. package/dist/core/ecs/facades.d.cts +12 -126
  63. package/dist/core/ecs/facades.d.ts +12 -126
  64. package/dist/core/ecs/facades.d.ts.map +1 -1
  65. package/dist/core/ecs/frame_stepper.d.cts +12 -12
  66. package/dist/core/ecs/frame_stepper.d.ts +12 -12
  67. package/dist/core/ecs/frame_stepper.d.ts.map +1 -1
  68. package/dist/core/ecs/frame_trace.d.cts +32 -32
  69. package/dist/core/ecs/frame_trace.d.ts +32 -32
  70. package/dist/core/ecs/frame_trace.d.ts.map +1 -1
  71. package/dist/core/ecs/host_commands.d.cts +103 -85
  72. package/dist/core/ecs/host_commands.d.ts +103 -85
  73. package/dist/core/ecs/host_commands.d.ts.map +1 -1
  74. package/dist/core/ecs/index.d.cts +21 -11
  75. package/dist/core/ecs/index.d.ts +21 -11
  76. package/dist/core/ecs/index.d.ts.map +1 -1
  77. package/dist/core/ecs/observer.d.cts +39 -120
  78. package/dist/core/ecs/observer.d.ts +39 -120
  79. package/dist/core/ecs/observer.d.ts.map +1 -1
  80. package/dist/core/ecs/phase.d.cts +79 -0
  81. package/dist/core/ecs/phase.d.ts +79 -0
  82. package/dist/core/ecs/phase.d.ts.map +1 -0
  83. package/dist/core/ecs/plugin.d.cts +125 -0
  84. package/dist/core/ecs/plugin.d.ts +125 -0
  85. package/dist/core/ecs/plugin.d.ts.map +1 -0
  86. package/dist/core/ecs/plugin_slots.d.cts +26 -0
  87. package/dist/core/ecs/plugin_slots.d.ts +26 -0
  88. package/dist/core/ecs/plugin_slots.d.ts.map +1 -0
  89. package/dist/core/ecs/query.d.cts +208 -578
  90. package/dist/core/ecs/query.d.ts +208 -578
  91. package/dist/core/ecs/query.d.ts.map +1 -1
  92. package/dist/core/ecs/query_cache.d.cts +130 -0
  93. package/dist/core/ecs/query_cache.d.ts +130 -0
  94. package/dist/core/ecs/query_cache.d.ts.map +1 -0
  95. package/dist/core/ecs/query_registry.d.cts +47 -0
  96. package/dist/core/ecs/query_registry.d.ts +47 -0
  97. package/dist/core/ecs/query_registry.d.ts.map +1 -0
  98. package/dist/core/ecs/query_terms.d.cts +126 -0
  99. package/dist/core/ecs/query_terms.d.ts +126 -0
  100. package/dist/core/ecs/query_terms.d.ts.map +1 -0
  101. package/dist/core/ecs/ref.d.cts +100 -39
  102. package/dist/core/ecs/ref.d.ts +100 -39
  103. package/dist/core/ecs/ref.d.ts.map +1 -1
  104. package/dist/core/ecs/relation.d.cts +101 -182
  105. package/dist/core/ecs/relation.d.ts +101 -182
  106. package/dist/core/ecs/relation.d.ts.map +1 -1
  107. package/dist/core/ecs/resource.d.cts +13 -13
  108. package/dist/core/ecs/resource.d.ts +13 -13
  109. package/dist/core/ecs/resource_registry.d.cts +5 -5
  110. package/dist/core/ecs/resource_registry.d.ts +5 -5
  111. package/dist/core/ecs/resource_registry.d.ts.map +1 -1
  112. package/dist/core/ecs/row_kinds.d.cts +24 -0
  113. package/dist/core/ecs/row_kinds.d.ts +24 -0
  114. package/dist/core/ecs/row_kinds.d.ts.map +1 -0
  115. package/dist/core/ecs/run_condition.d.cts +23 -23
  116. package/dist/core/ecs/run_condition.d.ts +23 -23
  117. package/dist/core/ecs/run_condition.d.ts.map +1 -1
  118. package/dist/core/ecs/schedule.d.cts +91 -120
  119. package/dist/core/ecs/schedule.d.ts +91 -120
  120. package/dist/core/ecs/schedule.d.ts.map +1 -1
  121. package/dist/core/ecs/schedule_plan.d.cts +33 -0
  122. package/dist/core/ecs/schedule_plan.d.ts +33 -0
  123. package/dist/core/ecs/schedule_plan.d.ts.map +1 -0
  124. package/dist/core/ecs/snapshot.d.cts +77 -0
  125. package/dist/core/ecs/snapshot.d.ts +77 -0
  126. package/dist/core/ecs/snapshot.d.ts.map +1 -0
  127. package/dist/core/ecs/snapshot_mount.d.cts +42 -0
  128. package/dist/core/ecs/snapshot_mount.d.ts +42 -0
  129. package/dist/core/ecs/snapshot_mount.d.ts.map +1 -0
  130. package/dist/core/ecs/sparse_store.d.cts +108 -51
  131. package/dist/core/ecs/sparse_store.d.ts +108 -51
  132. package/dist/core/ecs/sparse_store.d.ts.map +1 -1
  133. package/dist/core/ecs/store.d.cts +527 -473
  134. package/dist/core/ecs/store.d.ts +527 -473
  135. package/dist/core/ecs/store.d.ts.map +1 -1
  136. package/dist/core/ecs/store_layout_listener.d.cts +6 -6
  137. package/dist/core/ecs/store_layout_listener.d.ts +6 -6
  138. package/dist/core/ecs/store_types.d.cts +122 -0
  139. package/dist/core/ecs/store_types.d.ts +122 -0
  140. package/dist/core/ecs/store_types.d.ts.map +1 -0
  141. package/dist/core/ecs/system.d.cts +146 -61
  142. package/dist/core/ecs/system.d.ts +146 -61
  143. package/dist/core/ecs/system.d.ts.map +1 -1
  144. package/dist/core/ecs/system_context.d.cts +296 -0
  145. package/dist/core/ecs/system_context.d.ts +296 -0
  146. package/dist/core/ecs/system_context.d.ts.map +1 -0
  147. package/dist/core/ecs/system_set.d.cts +48 -0
  148. package/dist/core/ecs/system_set.d.ts +48 -0
  149. package/dist/core/ecs/system_set.d.ts.map +1 -0
  150. package/dist/core/ecs/utils/arrays.d.cts +15 -1
  151. package/dist/core/ecs/utils/arrays.d.ts +15 -1
  152. package/dist/core/ecs/utils/arrays.d.ts.map +1 -1
  153. package/dist/core/ecs/utils/error.d.cts +82 -6
  154. package/dist/core/ecs/utils/error.d.ts +82 -6
  155. package/dist/core/ecs/utils/error.d.ts.map +1 -1
  156. package/dist/core/ecs/utils/plugin_error.d.cts +10 -0
  157. package/dist/core/ecs/utils/plugin_error.d.ts +10 -0
  158. package/dist/core/ecs/utils/plugin_error.d.ts.map +1 -0
  159. package/dist/core/restore_errors.d.cts +30 -0
  160. package/dist/core/restore_errors.d.ts +30 -0
  161. package/dist/core/restore_errors.d.ts.map +1 -0
  162. package/dist/core/store/action_ring.d.cts +44 -44
  163. package/dist/core/store/action_ring.d.ts +44 -44
  164. package/dist/core/store/action_ring.d.ts.map +1 -1
  165. package/dist/core/store/allocator.d.cts +149 -95
  166. package/dist/core/store/allocator.d.ts +149 -95
  167. package/dist/core/store/allocator.d.ts.map +1 -1
  168. package/dist/core/store/buffer_backed_column.d.cts +17 -17
  169. package/dist/core/store/buffer_backed_column.d.ts +17 -17
  170. package/dist/core/store/buffer_backed_column.d.ts.map +1 -1
  171. package/dist/core/store/column_store.d.cts +124 -86
  172. package/dist/core/store/column_store.d.ts +124 -86
  173. package/dist/core/store/column_store.d.ts.map +1 -1
  174. package/dist/core/store/command_dispatch.d.cts +14 -15
  175. package/dist/core/store/command_dispatch.d.ts +14 -15
  176. package/dist/core/store/command_dispatch.d.ts.map +1 -1
  177. package/dist/core/store/command_ring.d.cts +36 -39
  178. package/dist/core/store/command_ring.d.ts +36 -39
  179. package/dist/core/store/command_ring.d.ts.map +1 -1
  180. package/dist/core/store/descriptor.d.cts +34 -17
  181. package/dist/core/store/descriptor.d.ts +34 -17
  182. package/dist/core/store/descriptor.d.ts.map +1 -1
  183. package/dist/core/store/entity_index.d.cts +34 -35
  184. package/dist/core/store/entity_index.d.ts +34 -35
  185. package/dist/core/store/entity_index.d.ts.map +1 -1
  186. package/dist/core/store/event_ring.d.cts +36 -38
  187. package/dist/core/store/event_ring.d.ts +36 -38
  188. package/dist/core/store/event_ring.d.ts.map +1 -1
  189. package/dist/core/store/extend.d.cts +13 -7
  190. package/dist/core/store/extend.d.ts +13 -7
  191. package/dist/core/store/extend.d.ts.map +1 -1
  192. package/dist/core/store/grow.d.cts +2 -2
  193. package/dist/core/store/grow.d.ts +2 -2
  194. package/dist/core/store/grow.d.ts.map +1 -1
  195. package/dist/core/store/header.d.cts +31 -27
  196. package/dist/core/store/header.d.ts +31 -27
  197. package/dist/core/store/header.d.ts.map +1 -1
  198. package/dist/core/store/index.d.cts +10 -10
  199. package/dist/core/store/index.d.ts +10 -10
  200. package/dist/core/store/index.d.ts.map +1 -1
  201. package/dist/core/store/layout_ops.d.cts +71 -62
  202. package/dist/core/store/layout_ops.d.ts +71 -62
  203. package/dist/core/store/layout_ops.d.ts.map +1 -1
  204. package/dist/core/store/region_table.d.cts +28 -21
  205. package/dist/core/store/region_table.d.ts +28 -21
  206. package/dist/core/store/region_table.d.ts.map +1 -1
  207. package/dist/core/store/snapshot.d.cts +31 -16
  208. package/dist/core/store/snapshot.d.ts +31 -16
  209. package/dist/core/store/snapshot.d.ts.map +1 -1
  210. package/dist/core/store/state_hash.d.cts +12 -12
  211. package/dist/core/store/state_hash.d.ts +12 -12
  212. package/dist/core/store/state_hash.d.ts.map +1 -1
  213. package/dist/core/store/store_regions.d.cts +10 -10
  214. package/dist/core/store/store_regions.d.ts +10 -10
  215. package/dist/core/store/store_regions.d.ts.map +1 -1
  216. package/dist/core/store/vendored_abi/abi.d.cts +27 -2
  217. package/dist/core/store/vendored_abi/abi.d.ts +27 -2
  218. package/dist/core/store/vendored_abi/abi.d.ts.map +1 -1
  219. package/dist/host_commands-B2KMx8O2.cjs +1 -0
  220. package/dist/host_commands-ClVPvhSa.cjs +2 -0
  221. package/dist/host_commands-CwxVQS1z.js +1524 -0
  222. package/dist/host_commands-nJvTy88E.js +1667 -0
  223. package/dist/index.cjs +1 -1
  224. package/dist/index.d.cts +15 -10
  225. package/dist/index.d.ts +15 -10
  226. package/dist/index.d.ts.map +1 -1
  227. package/dist/index.development.cjs +1 -1
  228. package/dist/index.development.js +5804 -5203
  229. package/dist/index.js +5323 -4764
  230. package/dist/internal.cjs +2 -2
  231. package/dist/internal.d.cts +5 -2
  232. package/dist/internal.d.ts +5 -2
  233. package/dist/internal.d.ts.map +1 -1
  234. package/dist/internal.development.cjs +1 -1
  235. package/dist/internal.development.js +70 -20
  236. package/dist/internal.js +138 -83
  237. package/dist/plugins/abi-D3V-QUSf.cjs +1 -0
  238. package/dist/plugins/abi-NP8pNxpd.js +42 -0
  239. package/dist/plugins/arrays-Dr_1hCjL.js +28 -0
  240. package/dist/plugins/arrays-Dt-Joc4i.cjs +1 -0
  241. package/dist/plugins/assertions-gtDVfbnQ.js +6 -0
  242. package/dist/plugins/assertions-z5GQd_bB.cjs +1 -0
  243. package/dist/{extensions → plugins}/editor/editor.d.cts +54 -54
  244. package/dist/{extensions → plugins}/editor/editor.d.ts +54 -54
  245. package/dist/plugins/editor/editor.d.ts.map +1 -0
  246. package/dist/{extensions → plugins}/editor/field_handle.d.cts +12 -12
  247. package/dist/{extensions → plugins}/editor/field_handle.d.ts +12 -12
  248. package/dist/plugins/editor/field_handle.d.ts.map +1 -0
  249. package/dist/plugins/editor/index.d.cts +22 -0
  250. package/dist/plugins/editor/index.d.ts +22 -0
  251. package/dist/plugins/editor/index.d.ts.map +1 -0
  252. package/dist/plugins/editor.cjs +1 -0
  253. package/dist/plugins/editor.d.cts +1 -0
  254. package/dist/plugins/editor.d.ts +1 -0
  255. package/dist/plugins/editor.development.cjs +1 -0
  256. package/dist/{extensions/editor/index.development.js → plugins/editor.development.js} +81 -64
  257. package/dist/{extensions/editor/index.js → plugins/editor.js} +81 -64
  258. package/dist/plugins/events/event_channel.d.cts +13 -0
  259. package/dist/plugins/events/event_channel.d.ts +13 -0
  260. package/dist/plugins/events/event_channel.d.ts.map +1 -0
  261. package/dist/plugins/events/event_registry.d.cts +22 -0
  262. package/dist/plugins/events/event_registry.d.ts +22 -0
  263. package/dist/plugins/events/event_registry.d.ts.map +1 -0
  264. package/dist/plugins/events/facade.d.cts +26 -0
  265. package/dist/plugins/events/facade.d.ts +26 -0
  266. package/dist/plugins/events/facade.d.ts.map +1 -0
  267. package/dist/plugins/events/index.d.cts +10 -0
  268. package/dist/plugins/events/index.d.ts +10 -0
  269. package/dist/plugins/events/index.d.ts.map +1 -0
  270. package/dist/plugins/events.cjs +1 -0
  271. package/dist/plugins/events.d.cts +1 -0
  272. package/dist/plugins/events.d.ts +1 -0
  273. package/dist/plugins/events.development.cjs +1 -0
  274. package/dist/plugins/events.development.js +174 -0
  275. package/dist/plugins/events.js +159 -0
  276. package/dist/plugins/observers/index.d.cts +23 -0
  277. package/dist/plugins/observers/index.d.ts +23 -0
  278. package/dist/plugins/observers/index.d.ts.map +1 -0
  279. package/dist/plugins/observers/observer_registry.d.cts +101 -0
  280. package/dist/plugins/observers/observer_registry.d.ts +101 -0
  281. package/dist/plugins/observers/observer_registry.d.ts.map +1 -0
  282. package/dist/plugins/observers.cjs +1 -0
  283. package/dist/plugins/observers.d.cts +1 -0
  284. package/dist/plugins/observers.d.ts +1 -0
  285. package/dist/plugins/observers.development.cjs +1 -0
  286. package/dist/plugins/observers.development.js +431 -0
  287. package/dist/plugins/observers.js +403 -0
  288. package/dist/plugins/relation_store-BsoegrZh.js +313 -0
  289. package/dist/plugins/relation_store-DQKSnxsm.cjs +1 -0
  290. package/dist/plugins/relation_store-RBhpcE28.cjs +1 -0
  291. package/dist/plugins/relation_store-RqW5Snx6.js +328 -0
  292. package/dist/plugins/relations/builtin_relations.d.cts +48 -0
  293. package/dist/plugins/relations/builtin_relations.d.ts +48 -0
  294. package/dist/plugins/relations/builtin_relations.d.ts.map +1 -0
  295. package/dist/plugins/relations/facade.d.cts +73 -0
  296. package/dist/plugins/relations/facade.d.ts +73 -0
  297. package/dist/plugins/relations/facade.d.ts.map +1 -0
  298. package/dist/plugins/relations/index.d.cts +11 -0
  299. package/dist/plugins/relations/index.d.ts +11 -0
  300. package/dist/plugins/relations/index.d.ts.map +1 -0
  301. package/dist/{core/ecs → plugins/relations}/relation_service.d.cts +85 -97
  302. package/dist/{core/ecs → plugins/relations}/relation_service.d.ts +85 -97
  303. package/dist/plugins/relations/relation_service.d.ts.map +1 -0
  304. package/dist/plugins/relations/relation_store.d.cts +177 -0
  305. package/dist/plugins/relations/relation_store.d.ts +177 -0
  306. package/dist/plugins/relations/relation_store.d.ts.map +1 -0
  307. package/dist/plugins/relations.cjs +1 -0
  308. package/dist/plugins/relations.d.cts +1 -0
  309. package/dist/plugins/relations.d.ts +1 -0
  310. package/dist/plugins/relations.development.cjs +1 -0
  311. package/dist/plugins/relations.development.js +603 -0
  312. package/dist/plugins/relations.js +553 -0
  313. package/dist/plugins/snapshots/index.d.cts +33 -0
  314. package/dist/plugins/snapshots/index.d.ts +33 -0
  315. package/dist/plugins/snapshots/index.d.ts.map +1 -0
  316. package/dist/plugins/snapshots/resume.d.cts +57 -0
  317. package/dist/plugins/snapshots/resume.d.ts +57 -0
  318. package/dist/plugins/snapshots/resume.d.ts.map +1 -0
  319. package/dist/plugins/snapshots/snapshot_service.d.cts +59 -0
  320. package/dist/plugins/snapshots/snapshot_service.d.ts +59 -0
  321. package/dist/plugins/snapshots/snapshot_service.d.ts.map +1 -0
  322. package/dist/plugins/snapshots.cjs +1 -0
  323. package/dist/plugins/snapshots.d.cts +1 -0
  324. package/dist/plugins/snapshots.d.ts +1 -0
  325. package/dist/plugins/snapshots.development.cjs +1 -0
  326. package/dist/plugins/snapshots.development.js +737 -0
  327. package/dist/plugins/snapshots.js +737 -0
  328. package/dist/plugins/solid/index.d.cts +8 -0
  329. package/dist/plugins/solid/index.d.ts +8 -0
  330. package/dist/plugins/solid/index.d.ts.map +1 -0
  331. package/dist/plugins/solid/solid.d.cts +97 -0
  332. package/dist/plugins/solid/solid.d.ts +97 -0
  333. package/dist/plugins/solid/solid.d.ts.map +1 -0
  334. package/dist/plugins/solid.cjs +1 -0
  335. package/dist/plugins/solid.d.cts +1 -0
  336. package/dist/plugins/solid.d.ts +1 -0
  337. package/dist/plugins/solid.development.cjs +1 -0
  338. package/dist/plugins/solid.development.js +319 -0
  339. package/dist/plugins/solid.js +319 -0
  340. package/dist/plugins/workers/index.d.cts +75 -0
  341. package/dist/plugins/workers/index.d.ts +75 -0
  342. package/dist/plugins/workers/index.d.ts.map +1 -0
  343. package/dist/plugins/workers/node_threads.d.cts +25 -0
  344. package/dist/plugins/workers/node_threads.d.ts +25 -0
  345. package/dist/plugins/workers/node_threads.d.ts.map +1 -0
  346. package/dist/plugins/workers/plan.d.cts +61 -0
  347. package/dist/plugins/workers/plan.d.ts +61 -0
  348. package/dist/plugins/workers/plan.d.ts.map +1 -0
  349. package/dist/plugins/workers/pool.d.cts +144 -0
  350. package/dist/plugins/workers/pool.d.ts +144 -0
  351. package/dist/plugins/workers/pool.d.ts.map +1 -0
  352. package/dist/plugins/workers/protocol.d.cts +112 -0
  353. package/dist/plugins/workers/protocol.d.ts +112 -0
  354. package/dist/plugins/workers/protocol.d.ts.map +1 -0
  355. package/dist/plugins/workers/worker_bind.d.cts +64 -0
  356. package/dist/plugins/workers/worker_bind.d.ts +64 -0
  357. package/dist/plugins/workers/worker_bind.d.ts.map +1 -0
  358. package/dist/plugins/workers/worker_loop.d.cts +14 -0
  359. package/dist/plugins/workers/worker_loop.d.ts +14 -0
  360. package/dist/plugins/workers/worker_loop.d.ts.map +1 -0
  361. package/dist/plugins/workers.cjs +1 -0
  362. package/dist/plugins/workers.d.cts +1 -0
  363. package/dist/plugins/workers.d.ts +1 -0
  364. package/dist/plugins/workers.development.cjs +1 -0
  365. package/dist/plugins/workers.development.js +596 -0
  366. package/dist/plugins/workers.js +474 -0
  367. package/dist/primitives.cjs +1 -1
  368. package/dist/primitives.d.cts +4 -4
  369. package/dist/primitives.d.ts +4 -4
  370. package/dist/primitives.development.cjs +1 -1
  371. package/dist/primitives.development.js +66 -22
  372. package/dist/primitives.js +66 -22
  373. package/dist/shared-BKl12xF1.js +132 -0
  374. package/dist/shared-Dk-pACZT.cjs +1 -0
  375. package/dist/shared.cjs +1 -1
  376. package/dist/shared.d.cts +7 -7
  377. package/dist/shared.d.ts +7 -7
  378. package/dist/shared.d.ts.map +1 -1
  379. package/dist/shared.development.cjs +1 -1
  380. package/dist/shared.development.js +4 -3
  381. package/dist/shared.js +4 -3
  382. package/dist/topological_sort-Cc_yXqPT.js +346 -0
  383. package/dist/topological_sort-vp1imU6D.cjs +1 -0
  384. package/dist/type_primitives/assertions.d.cts +12 -16
  385. package/dist/type_primitives/assertions.d.ts +12 -16
  386. package/dist/type_primitives/assertions.d.ts.map +1 -1
  387. package/dist/type_primitives/binary_heap/binary_heap.d.cts +2 -6
  388. package/dist/type_primitives/binary_heap/binary_heap.d.ts +2 -6
  389. package/dist/type_primitives/binary_heap/binary_heap.d.ts.map +1 -1
  390. package/dist/type_primitives/bitset/bitset.d.cts +7 -11
  391. package/dist/type_primitives/bitset/bitset.d.ts +7 -11
  392. package/dist/type_primitives/bitset/bitset.d.ts.map +1 -1
  393. package/dist/type_primitives/brand.d.cts +2 -7
  394. package/dist/type_primitives/brand.d.ts +2 -7
  395. package/dist/type_primitives/brand.d.ts.map +1 -1
  396. package/dist/type_primitives/error.d.cts +1 -2
  397. package/dist/type_primitives/error.d.ts +1 -2
  398. package/dist/type_primitives/error.d.ts.map +1 -1
  399. package/dist/type_primitives/index.d.cts +1 -1
  400. package/dist/type_primitives/index.d.ts +1 -1
  401. package/dist/type_primitives/sparse_map/sparse_map.d.cts +3 -7
  402. package/dist/type_primitives/sparse_map/sparse_map.d.ts +3 -7
  403. package/dist/type_primitives/sparse_map/sparse_map.d.ts.map +1 -1
  404. package/dist/type_primitives/sparse_set/sparse_set.d.cts +2 -6
  405. package/dist/type_primitives/sparse_set/sparse_set.d.ts +2 -6
  406. package/dist/type_primitives/sparse_set/sparse_set.d.ts.map +1 -1
  407. package/dist/type_primitives/topological_sort/topological_sort.d.cts +1 -5
  408. package/dist/type_primitives/topological_sort/topological_sort.d.ts +1 -5
  409. package/dist/type_primitives/topological_sort/topological_sort.d.ts.map +1 -1
  410. package/dist/type_primitives/typed_arrays/typed_arrays.d.cts +16 -15
  411. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +16 -15
  412. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -1
  413. package/dist/utils/error.d.ts.map +1 -1
  414. package/dist/version.d.cts +3 -3
  415. package/dist/version.d.ts +3 -3
  416. package/dist/worker.cjs +1 -0
  417. package/dist/worker.d.cts +17 -0
  418. package/dist/worker.d.ts +17 -0
  419. package/dist/worker.d.ts.map +1 -0
  420. package/dist/worker.development.cjs +1 -0
  421. package/dist/worker.development.js +253 -0
  422. package/dist/worker.js +253 -0
  423. package/package.json +363 -191
  424. package/dist/core/ecs/builtin_relations.d.cts +0 -43
  425. package/dist/core/ecs/builtin_relations.d.ts +0 -43
  426. package/dist/core/ecs/builtin_relations.d.ts.map +0 -1
  427. package/dist/core/ecs/event_registry.d.cts +0 -22
  428. package/dist/core/ecs/event_registry.d.ts +0 -22
  429. package/dist/core/ecs/event_registry.d.ts.map +0 -1
  430. package/dist/core/ecs/relation_service.d.ts.map +0 -1
  431. package/dist/core/ecs/resume.d.cts +0 -85
  432. package/dist/core/ecs/resume.d.ts +0 -85
  433. package/dist/core/ecs/resume.d.ts.map +0 -1
  434. package/dist/core/ecs/snapshot_service.d.cts +0 -94
  435. package/dist/core/ecs/snapshot_service.d.ts +0 -94
  436. package/dist/core/ecs/snapshot_service.d.ts.map +0 -1
  437. package/dist/extensions/editor/editor.d.ts.map +0 -1
  438. package/dist/extensions/editor/field_handle.d.ts.map +0 -1
  439. package/dist/extensions/editor/index.cjs +0 -1
  440. package/dist/extensions/editor/index.d.cts +0 -21
  441. package/dist/extensions/editor/index.d.ts +0 -21
  442. package/dist/extensions/editor/index.d.ts.map +0 -1
  443. package/dist/extensions/editor/index.development.cjs +0 -1
  444. package/dist/extensions/reactive/ecs_sync.d.cts +0 -214
  445. package/dist/extensions/reactive/ecs_sync.d.ts +0 -214
  446. package/dist/extensions/reactive/ecs_sync.d.ts.map +0 -1
  447. package/dist/extensions/reactive/index.cjs +0 -1
  448. package/dist/extensions/reactive/index.d.cts +0 -23
  449. package/dist/extensions/reactive/index.d.ts +0 -23
  450. package/dist/extensions/reactive/index.d.ts.map +0 -1
  451. package/dist/extensions/reactive/index.development.cjs +0 -1
  452. package/dist/extensions/reactive/index.development.js +0 -215
  453. package/dist/extensions/reactive/index.js +0 -215
  454. package/dist/extensions/solid/index.cjs +0 -1
  455. package/dist/extensions/solid/index.d.cts +0 -6
  456. package/dist/extensions/solid/index.d.ts +0 -6
  457. package/dist/extensions/solid/index.d.ts.map +0 -1
  458. package/dist/extensions/solid/index.development.cjs +0 -1
  459. package/dist/extensions/solid/index.development.js +0 -40
  460. package/dist/extensions/solid/index.js +0 -36
  461. package/dist/extensions/solid/kernel_solid.d.cts +0 -45
  462. package/dist/extensions/solid/kernel_solid.d.ts +0 -45
  463. package/dist/extensions/solid/kernel_solid.d.ts.map +0 -1
  464. package/dist/host_commands-BlR3oZ3x.js +0 -2406
  465. package/dist/host_commands-C2cpU3yC.cjs +0 -1
  466. package/dist/internal-BoxWl4dG.js +0 -2565
  467. package/dist/internal-CGBEwfeB.cjs +0 -2
  468. package/dist/interop-CT-REx0W.cjs +0 -1
  469. package/dist/interop-CcY6ASQc.js +0 -18
  470. package/dist/interop-DqxleBo7.js +0 -18
  471. package/dist/interop-T1HZ-Dpa.cjs +0 -1
  472. package/dist/kernel-Cz2Kxlwl.js +0 -240
  473. package/dist/kernel-DgyrLFjW.js +0 -227
  474. package/dist/kernel-sw7ScKWu.cjs +0 -1
  475. package/dist/kernel-yWV3XnAb.cjs +0 -1
  476. package/dist/reactive/array.d.cts +0 -24
  477. package/dist/reactive/array.d.ts +0 -24
  478. package/dist/reactive/array.d.ts.map +0 -1
  479. package/dist/reactive/index.cjs +0 -1
  480. package/dist/reactive/index.d.cts +0 -11
  481. package/dist/reactive/index.d.ts +0 -11
  482. package/dist/reactive/index.d.ts.map +0 -1
  483. package/dist/reactive/index.development.cjs +0 -1
  484. package/dist/reactive/index.development.js +0 -18
  485. package/dist/reactive/index.js +0 -18
  486. package/dist/reactive/interop.d.cts +0 -19
  487. package/dist/reactive/interop.d.ts +0 -19
  488. package/dist/reactive/interop.d.ts.map +0 -1
  489. package/dist/reactive/kernel.d.cts +0 -73
  490. package/dist/reactive/kernel.d.ts +0 -73
  491. package/dist/reactive/kernel.d.ts.map +0 -1
  492. package/dist/reactive/map.d.cts +0 -16
  493. package/dist/reactive/map.d.ts +0 -16
  494. package/dist/reactive/map.d.ts.map +0 -1
  495. package/dist/reactive/shallow.d.cts +0 -11
  496. package/dist/reactive/shallow.d.ts +0 -11
  497. package/dist/reactive/shallow.d.ts.map +0 -1
  498. package/dist/reactive/struct.d.cts +0 -10
  499. package/dist/reactive/struct.d.ts +0 -10
  500. package/dist/reactive/struct.d.ts.map +0 -1
  501. package/dist/shallow-Bs39EPh1.js +0 -150
  502. package/dist/shallow-C5YmTbtb.cjs +0 -1
  503. package/dist/shallow-CiFC70Dz.js +0 -159
  504. package/dist/shallow-DQ8aG51_.cjs +0 -1
  505. package/dist/shared-B4v0L1cw.js +0 -109
  506. package/dist/shared-DU78k0pU.cjs +0 -1
  507. package/dist/topological_sort-BDvEyb9W.cjs +0 -1
  508. package/dist/topological_sort-DK6EjpWa.js +0 -135
  509. package/dist/typed_arrays-D3NmwtCG.cjs +0 -1
  510. package/dist/typed_arrays-DIVwVy5_.js +0 -258
@@ -1,67 +1,30 @@
1
1
  import { EntityID } from './entity.js';
2
- import { CursorBinder } from './ref.js';
2
+ import { AccessorColumns, CursorBinder, SparseCursorCheck } from './ref.js';
3
3
  import { FrameTraceSink } from './frame_trace.js';
4
4
  import { ComponentDef, ComponentHandle, ComponentSchema, FieldValues } from './component.js';
5
- import { SparseComponentDef, SparseComponentID } from './sparse_store.js';
6
- import { RelationDef, RelationOptions } from './relation.js';
7
- import { EmptyEventSchema, EventDef, EventReader, EventShape } from './event.js';
5
+ import { SparseComponentStore, SparseComponentDef, SparseComponentID } from './sparse_store.js';
6
+ import { RelationDef, RelationHooks, RelationServiceHost } from './relation.js';
7
+ import { EventHooks } from './event.js';
8
+ import { ResourceRegistry } from './resource_registry.js';
8
9
  import { BitSet, TypedArrayTag } from '../../type_primitives/index.js';
9
10
  import { Archetype, ArchetypeID } from './archetype.js';
10
- import { Query, QueryHost } from './query.js';
11
+ import { Query } from './query.js';
12
+ import { QueryHost } from './query_cache.js';
13
+ import { QueryTerms } from './query_terms.js';
14
+ import { DrainResult, ObservationFlags, StructuralObserverEvents, Template } from './store_types.js';
11
15
  import { ObserverHost } from './observer.js';
16
+ import { ChangeFeed } from './change_feed.js';
17
+ import { EntityAllocator } from './entity_allocator.js';
18
+ import { SnapshotHooks, SnapshotHost } from './snapshot.js';
12
19
  import { InPlaceBufferAllocator, ColumnStoreRegionHandle, StoreRegionSpec, ColumnStore } from '../store/index.js';
13
20
  import { ECSMemoryCapContext } from './ecs_memory.js';
14
- export interface ComponentMeta {
15
- /** Optional debug name from `registerComponent(schema, { name })`
16
- * diagnostic messages only, never behaviour. */
17
- name?: string;
18
- fieldNames: string[];
19
- fieldIndex: Record<string, number>;
20
- fieldTypes: TypedArrayTag[];
21
- /** Has an onAdd observer — collect effective adds for this component. */
22
- obsAdd: boolean;
23
- /** Has an onRemove observer — collect effective removes for this component. */
24
- obsRem: boolean;
25
- /** Has an onDisable observer — collect effective disables for this
26
- * component at the toggle drain. */
27
- obsDisable: boolean;
28
- /** Has an onEnable observer — collect effective enables for this
29
- * component at the toggle drain. */
30
- obsEnable: boolean;
31
- /** Has a per-entity onSet observer — record dirty rows on the write path
32
- * (the opt-in dirty list). */
33
- trackDirty: boolean;
34
- }
35
- /**
36
- * Effective `(component, entity)` structural events for one fixed-point round,
37
- * collected during `_flushAdds` / `_flushRemoves` and handed to the observer
38
- * dispatch hook. Flat parallel arrays, count-bounded (`*_len`), reused across
39
- * rounds — never reallocated in the flush. This is a scheduling artifact: it is
40
- * NOT part of `stateHash` or snapshot. See `observer.ts`.
41
- */
42
- export interface StructuralObserverEvents {
43
- addComp: number[];
44
- addEid: number[];
45
- addLen: number;
46
- remComp: number[];
47
- remEid: number[];
48
- remLen: number;
49
- /** Effective disable events — collected during the toggle drain
50
- * (`_flushToggles`), one per `(component, entity)` of each net-disabled
51
- * entity's mask. Empty on a structural (add/remove/destroy) round. */
52
- disComp: number[];
53
- disEid: number[];
54
- disLen: number;
55
- /** Effective enable events, symmetric with the disable arrays. */
56
- enaComp: number[];
57
- enaEid: number[];
58
- enaLen: number;
59
- }
60
- /** Runtime shape of one template / `addComponents` entry: a def plus optional
61
- * field values (omitted fields zero-fill). The public authoring surface is
62
- * callable-bundle varargs, schema-checked per item by `StrictBundles`
63
- * (component.ts); the `ECS` facade normalizes those bundles into this erased
64
- * array before calling the store, so the store stays schema-agnostic. */
21
+ export type { ComponentMeta, DrainResult, ObservationFlags, StructuralObserverEvents, Template } from './store_types.js';
22
+ /** Runtime shape of one template entry or `addComponents` entry: a def plus
23
+ * optional field values (an omitted field zero-fills). The public authoring
24
+ * surface is callable-bundle varargs, schema-checked for each item by
25
+ * `StrictBundles` in `component.ts`. The `ECS` facade normalizes those bundles
26
+ * into this erased array before it calls the store, so the store stays
27
+ * schema-agnostic. */
65
28
  type TemplateEntryData = {
66
29
  readonly def: ComponentDef;
67
30
  readonly values?: Readonly<Record<string, number>>;
@@ -75,155 +38,210 @@ type TemplateFieldNames<Defs extends readonly ComponentDef[]> = Defs[number] ext
75
38
  export type TemplateOverrides<Defs extends readonly ComponentDef[]> = {
76
39
  readonly [K in TemplateFieldNames<Defs>]?: number;
77
40
  };
78
- declare const __templateDefs: unique symbol;
79
- /** A resolved template — an archetype template produced by
80
- * `ECS.template(...)`. **Opaque** apart from `defs`: callers hold it and pass
81
- * it to `ECS.spawn` / `ECS.spawnMany` (and may reference it in a system's
82
- * `spawns` / `despawns` access declaration — the scheduler expands it to
83
- * `defs`); the remaining fields are engine-internal and may change. `spawn`
84
- * lands an entity directly in `archetype_id` with zero archetype transitions,
85
- * writing `flatValues` (defaults in `_flatColumns` order) in one append
86
- * pass. */
87
- export interface Template<Defs extends readonly ComponentDef[] = readonly ComponentDef[]> {
88
- readonly archetypeId: ArchetypeID;
89
- readonly flatValues: number[];
90
- readonly overrideIndex: Map<string, number>;
91
- /** The component set this template spawns into, in entry order. */
92
- readonly defs: readonly ComponentDef[];
93
- readonly [__templateDefs]?: Defs;
94
- }
95
41
  export interface StoreOptions {
96
42
  initialCapacity?: number;
97
43
  /** Pluggable SAB buffer source. When provided, `createColumnStore`,
98
44
  * `extendColumnStore`, and `growColumnStore` route through it. Default is
99
45
  * `growableSabAllocator`. Typed `InPlaceBufferAllocator`: a live
100
46
  * Store's flush loops hoist entity-index views across grows, so only
101
- * in-place allocators may back one the constructor also
47
+ * in-place allocators may back one, the constructor also
102
48
  * runtime-asserts the marker for untyped JS callers. Consumers normally
103
- * don't touch this directly; `ECSOptions.memory` resolves to it. */
49
+ * don't touch this directly. `ECSOptions.memory` resolves to it. */
104
50
  bufferAllocator?: InPlaceBufferAllocator;
105
51
  /** Sizing intent the world was constructed with, used to phrase
106
52
  * allocator-cap and entity-index-overflow errors in the caller's own
107
- * terms ("3.2× the declared budget") instead of raw bytes. Wired by
108
- * `ECS` from `resolveECSMemory`; absent for bare test Stores. */
53
+ * terms (a multiple of the declared budget) instead of raw bytes. Wired by
54
+ * `ECS` from `resolveECSMemory`. Absent for bare test Stores. */
109
55
  capContext?: ECSMemoryCapContext;
110
56
  /** Fired after every SAB resize (extend or grow). The new SAB has
111
57
  * already been built and archetypes have already refreshed their
112
- * views by the time this fires. Used by ECS to call
113
- * `sim.setLayout(0)` so WASM-side cached pointers re-walk. */
114
- onBufferResized?: () => void;
58
+ * views by the time this fires. `ECS` uses it to call `setLayout` on every
59
+ * subscribed layout listener, so a WASM-side cached pointer re-walks. */
60
+ onBufferReplaced?: () => void;
115
61
  /** Max live entities the SAB entity-index region holds.
116
- * Default `ENTITY_INDEX_DEFAULT_CAPACITY` (`1 << 20` the full EntityID
62
+ * Default `ENTITY_INDEX_DEFAULT_CAPACITY` (`1 << 20`, the full EntityID
117
63
  * index space). Exceeding this at runtime throws `EID_MAX_INDEX_OVERFLOW`.
118
- * Tests with small entity counts may set lower to bench the SAB region size
119
- * or to make index exhaustion reachable; a 1000-entity workload fits
120
- * comfortably in the default. */
64
+ * Tests with small entity counts may set lower to shrink the SAB region or
65
+ * to make index exhaustion reachable. */
121
66
  entityIndexCapacity?: number;
67
+ /** Byte offset inside the backing where the store header goes. Default 0.
68
+ * Forwarded verbatim to `createColumnStore`, and every offset in the bytes
69
+ * is relative to it. `ECSOptions.memory.storeBase` resolves to it. */
70
+ storeBase?: number;
122
71
  /** Consumer-declared SAB regions, forwarded verbatim to
123
72
  * `createColumnStore`. Each `StoreRegionSpec` carries an opaque `region_id`,
124
- * a precomputed byte size, and an `init` closure; the engine lays them out
125
- * generically and exposes them via `regionHandle(id)` / `regionOffset(id)`.
126
- * A game (e.g. `@internal/sim`'s region specs) supplies these the engine
127
- * ships no game regions of its own. Omitted ⇒ none. */
73
+ * a precomputed byte size, and an `init` closure. The engine lays them out
74
+ * generically and exposes them through `regionHandle(id)` and
75
+ * `regionOffset(id)`. A consumer supplies the specs. The engine ships no
76
+ * region of its own. Omitted ⇒ none. */
128
77
  regions?: readonly StoreRegionSpec[];
129
78
  /** Byte size of the opt-in sim-bindings region, forwarded verbatim to
130
79
  * `createColumnStore`. A consumer that attaches a WASM backend passes its own
131
- * size (`@internal/sim`'s `SIM_BINDINGS_BYTES`, computed from the binding
132
- * manifest); the host then writes the `(component_id, field_id)` IDs into the
133
- * region. Omitted / 0 ⇒ no region (a pure-TS game pays nothing for the WASM
134
- * seam). De-welded from the engine ABI so a manifest edit doesn't
135
- * drift an engine golden. */
80
+ * size, computed from its own binding manifest. The host then writes the
81
+ * `(component_id, field_id)` ids into the region. Omitted or 0 ⇒ no region,
82
+ * so a pure-TS world pays nothing for the WASM seam. The size stays out of
83
+ * the engine ABI, so a manifest edit does not drift an engine golden. */
136
84
  bindingsRegionBytes?: number;
137
85
  /** Opt into the **determinism surface**. Default `false`.
138
- * Gates the three methods that fold/serialize state in canonical (sorted)
86
+ * Gates the three methods that fold and serialize state in canonical (sorted)
139
87
  * order: `stateHash`, `snapshotSparse`, `restoreSparse`. When `false`
140
- * those throw `DETERMINISM_DISABLED` the canonical-ordering tax (sparse
88
+ * those throw `DETERMINISM_DISABLED`, the canonical-ordering tax (sparse
141
89
  * `canonicalIndices` sort + relation target-set sort) is never paid, and a
142
90
  * consumer can't accidentally read a non-canonical digest. When `true`,
143
- * today's behavior is reproduced bit-for-bit. This is the ONLY effect of the
91
+ * today's behavior is reproduced bit-for-bit. This is the only effect of the
144
92
  * flag: it does not touch the per-tick path, the in-place-allocator invariant
145
93
  * (a memory-safety requirement that holds regardless), or the
146
94
  * always-on `enabled_count` partition maintenance. The flag's value is a
147
- * capability gate, not a hot-path switch `stateHash`/snapshot are never
148
- * called per tick. */
95
+ * plugin gate, not a hot-path switch. `stateHash` and the snapshot are
96
+ * never called per tick. */
149
97
  deterministic?: boolean;
150
98
  }
151
- export declare class Store implements ObserverHost, QueryHost {
152
- private readonly entityAllocator;
153
- private readonly componentMetas;
154
- private componentCount;
155
- private readonly sparseStores;
156
- /** Debug names parallel to `sparseStores` — diagnostics only. */
157
- private readonly sparseNames;
158
- private readonly relationService;
159
- private readonly events;
160
- private readonly archGraph;
161
- private readonly registeredQueries;
162
- private emptyArchetypeId;
163
- private entityArchetype;
164
- private entityRow;
99
+ export declare class Store implements ChangeFeed, ObserverHost, QueryHost {
100
+ private readonly _entityAllocator;
101
+ private readonly _componentMetas;
102
+ private readonly _sparseStores;
103
+ /** Debug names parallel to `_sparseStores`, diagnostics only. */
104
+ private readonly _sparseNames;
105
+ /** The reused result of `drainSparseSet`, parallel to `_sparseStores`,
106
+ * allocated for a sparse component with an entity-level onSet. */
107
+ private readonly _sparseDrains;
108
+ private _relations;
109
+ private _events;
110
+ private readonly _archGraph;
111
+ private _emptyArchetypeId;
112
+ private _entityArchetypes;
113
+ private _entityRows;
114
+ private readonly _queries;
165
115
  private readonly _deferred;
166
- private readonly _snapshots;
167
- _tick: number;
116
+ private _snapshots;
117
+ get relations(): RelationHooks;
118
+ /** The relation service, for a caller that is not `ecs.relations`.
119
+ *
120
+ * The getter above names `ecs.relations` in its fault. That is the wrong
121
+ * remedy when a system context or a query term reached the service. Every
122
+ * seam a user can reach passes its own name here. Error path only. The
123
+ * successful read is the same field read the getter makes. */
124
+ requireRelations(api: string): RelationHooks;
125
+ /** Build the host the relation service needs. Closures, not field
126
+ * references: `generations`, `entityArchetypes` and `entityRows` are
127
+ * reallocated when capacity grows, so each accessor re-reads the live
128
+ * field. */
129
+ relationHost(): RelationServiceHost;
130
+ /** Install the relations plugin. Called once, by the plugin. */
131
+ installRelations(service: RelationHooks): void;
132
+ get events(): EventHooks;
133
+ /** The event registry, for a caller that is not `ecs.events`. Names the
134
+ * seam the user reached, the way `requireRelations` does. */
135
+ requireEvents(api: string): EventHooks;
136
+ /** Whether the events plugin is installed. Read once per frame by the
137
+ * tick-tail clear, which must be a no-op on a world without channels. */
138
+ get hasEvents(): boolean;
139
+ /** Install the events plugin. Called once, by the plugin. */
140
+ installEvents(registry: EventHooks): void;
141
+ /** Build the host a snapshot plugin needs. Only the store can reach
142
+ * these fields, so it builds the record and the plugin owns the service.
143
+ * The accessors re-read live fields per call: the column store and the
144
+ * entity-index views are replaced on a restore. All cold path. */
145
+ snapshotHost(): SnapshotHost;
146
+ /** The allocator, which is its own snapshot seam. Handed to the plugin
147
+ * so the store need not construct the service itself. */
148
+ get entityAllocator(): EntityAllocator;
149
+ /** Install the snapshot plugin. Called once, by the plugin. */
150
+ installSnapshots(service: SnapshotHooks): void;
151
+ get snapshots(): SnapshotHooks;
152
+ get resources(): ResourceRegistry;
153
+ tick: number;
154
+ /** The change tick. A monotonic counter that the schedule advances before
155
+ * each system run, before each phase flush, before the onSet dispatch and
156
+ * at the end of each update. Every write stamps it on the archetype column
157
+ * it touches, and a consumer compares against the value of its own last
158
+ * run. The frame tick above cannot order a writer and a reader inside one
159
+ * frame, so a write by an earlier system was reported on two frames. This
160
+ * counter orders them. It starts above the initial column stamp, so the
161
+ * first run of a system sees every row that exists. A scheduling artifact:
162
+ * not in `stateHash` or the snapshot, and monotonic across a restore. */
163
+ changeTick: number;
164
+ /** Advance the change tick and return the new value, the stamp for the
165
+ * run that follows. Hot path: one increment per system run. */
166
+ advanceChangeTick(): number;
168
167
  /** Per-world frame-trace sink, installed via `ECS.setTrace`.
169
168
  * `null` unless a consumer attaches a recorder. Every call site is
170
- * `if (DEV) store._trace?.…`, so production builds dead-code-eliminate
171
- * the seam and pay only this one nullable field. The sink observes; it never
172
- * folds into `stateHash` (a scheduling artifact, like `_changedTick` / the
173
- * observer state below). */
174
- _trace: FrameTraceSink | null;
169
+ * `if (DEV) store.trace?.…`, so production builds dead-code-eliminate
170
+ * the seam and pay only this one nullable field. The sink observes. It never
171
+ * folds into `stateHash`. It is a scheduling artifact, like `_changedTick`
172
+ * and like the observer state below. */
173
+ trace: FrameTraceSink | null;
175
174
  private _structuralObserverCount;
176
- /** Count of components with any onDisable/onEnable observer. While 0
175
+ /** Count of components with any onDisable and onEnable observer. While 0
177
176
  * (with `_structuralObserverCount` also 0), `flushStructural` takes the
178
177
  * byte-for-byte fast path and the toggle drain skips event collection. */
179
178
  private _toggleObserverCount;
179
+ /** One flag record for each consumer of the change feed, keyed by the
180
+ * plugin name, each array indexed by component id and sparse. The
181
+ * store merges by OR across the records, so the flags a component carries
182
+ * are the union of what every consumer asked for. Cold: written when a
183
+ * consumer gains or loses an observer, read on the same call. */
184
+ private readonly _observationFlags;
185
+ /** The sparse half of the record above, one boolean for each sparse
186
+ * component a consumer wants the entity grain of. */
187
+ private readonly _sparseObservationFlags;
180
188
  /** Reused effective-event scratch for the current flush round. */
181
189
  private readonly _obsEvents;
182
- /** Installed via `setStructuralObserverHook` dispatches a round's collected
183
- * events to the observer registry (ordering + callbacks), which may enqueue
184
- * further structural ops. */
185
- private _structuralObserverHook;
186
- /** Install the structural-observer dispatch hook (called once by `ECS`
187
- * during construction) the named seam replacing direct writes to the
188
- * previously-public field. */
189
- setStructuralObserverHook(fn: (ev: StructuralObserverEvents) => void): void;
190
+ /** Installed via `addStructuralHook`, each one dispatches a round's
191
+ * collected events to one consumer of the change feed, which may enqueue
192
+ * further structural ops. Empty on a world that installed no consumer, and
193
+ * the flush reads it once per round. */
194
+ private readonly _structuralHooks;
195
+ /** Add a consumer of the structural event batches. Each round of the
196
+ * observed flush hands its effective events to every hook, in install
197
+ * order. The events scratch is store-owned and reused, so a hook reads it
198
+ * during the call and copies whatever it keeps. Cold path. */
199
+ addStructuralHook(fn: (ev: StructuralObserverEvents) => void): void;
190
200
  private _collectDestroyEid;
191
201
  private readonly _collectDestroyRemoveBit;
192
202
  private _collectToggleEid;
193
203
  private readonly _collectDisableBit;
194
204
  private readonly _collectEnableBit;
195
205
  /** Net-transition snapshot for the toggle drain: entity → its disabled
196
- * state at the START of the drain. Reused, cleared each drain. Lets
206
+ * state at the start of the drain. Reused, cleared each drain. Lets
197
207
  * `_flushToggles` emit one event per *net* transition (disable→enable→disable
198
- * within a tick = a single onDisable) instead of one per buffered op required
208
+ * within a tick = a single onDisable) instead of one per buffered op, required
199
209
  * because the radix canonical-order pass would otherwise reorder duplicate eids
200
- * and mis-sequence a consumer's delete/republish. */
210
+ * and mis-sequence a consumer's delete and republish. */
201
211
  private readonly _toggleInitial;
202
- _anyDirtyTracked: boolean;
212
+ /** True once any component has row ticks. Gates the record at every write
213
+ * site, so a world without one pays a load and a branch there. */
214
+ anyDirtyTracked: boolean;
215
+ /** The components with row ticks, for the archetypes born later. */
203
216
  private readonly _dirtyTrackedCids;
204
217
  private readonly _dirtyLists;
205
- private readonly _dirtyMarks;
218
+ private readonly _drainResults;
206
219
  /** Set by any path that changes a SAB-backed archetype's live row count
207
- * (`flushStructural`/`flushDestroyed` when they did work; immediate
220
+ * (`flushStructural` and `flushDestroys` when they did work, immediate
208
221
  * `destroyEntity`, `addComponent(s)`, `removeComponent(s)` on the
209
- * Store). Cleared by `publishRowCountsToDescriptor`. Lets read-only
222
+ * Store). Cleared by `publishRowCounts`. Lets read-only
210
223
  * phases' `ctx.flush` skip the descriptor walk entirely. */
211
224
  private _rowCountsDirty;
212
225
  /** Monotonic counter bumped by every membership-changing path (immediate
213
226
  * `addComponent(s)`, `removeComponent(s)`, `destroyEntity`,
214
227
  * `batchAddComponent`, `batchRemoveComponent`, `flushStructural`,
215
- * `flushDestroyed`, and new-archetype installs in `ArchetypeGraph.install`).
216
- * Read by `Query._nonEmpty()` via `QueryResolver._getQueryDirtyEpoch`
217
- * a query whose stored `_lastSeenEpoch` matches the current epoch
228
+ * `flushDestroys`, and new-archetype installs in `ArchetypeGraph.install`).
229
+ * Read by `Query.nonEmptyArchs()` via `QueryResolver.getQueryDirtyEpoch`.
230
+ * A query whose stored `_lastSeenEpoch` matches the current epoch
218
231
  * reuses its cached non-empty list. Replaces the previous walk
219
- * over `registeredQueries` that wrote one dirty bit per query per
220
- * mutation; 5000 startup adds × Q queries used to be 5000×Q writes,
221
- * now it's 5000 integer increments. Public so ECS can forward through
222
- * its `QueryResolver` impl; not part of the user-facing API. */
223
- _queryDirtyEpoch: number;
224
- private readonly initialCapacity;
225
- private readonly _scratchTargetMask;
232
+ * over `_registeredQueries` that wrote one dirty bit per query per
233
+ * mutation. A startup that adds many rows used to write one bit for each
234
+ * query at each mutation. Now it writes one integer increment. Public, so
235
+ * ECS can forward through its `QueryResolver` impl. It is not part of the
236
+ * user-facing API. */
237
+ queryDirtyEpoch: number;
226
238
  private _columnStore;
239
+ /** Row capacity every new archetype's columns start with. Taken from
240
+ * `StoreOptions.initialCapacity` at construction, read again by the
241
+ * archetype graph's `initialCapacity` host closure whenever it plants a
242
+ * new archetype. It sat under the row-grain banner, which owns the change
243
+ * detection state, and it sizes the backing instead. */
244
+ private readonly _initialCapacity;
227
245
  /** Installed on every SAB-backed Archetype so the Archetype can
228
246
  * request a SAB grow when an insertion would exceed its column
229
247
  * capacity. Doubles the offending archetype's row capacity (or jumps
@@ -233,43 +251,46 @@ export declare class Store implements ObserverHost, QueryHost {
233
251
  * SAB-backed archetype. */
234
252
  private readonly _growHandler;
235
253
  /** Build the intent-aware fatal for an allocator cap hit. The
236
- * allocator can only name raw bytes; the Store knows what the caller
254
+ * allocator can only name raw bytes. The Store knows what the caller
237
255
  * declared (`capContext`) and how many entities are live, so the error
238
- * says "3.2× the declared budget runaway creation upstream?" instead
256
+ * names the live count as a multiple of the declared budget instead
239
257
  * of leaving the caller to reverse-engineer byte counts. Fatality is
240
258
  * unchanged (no grow-beyond-cap fallback). */
241
259
  private _capExceededError;
242
260
  private readonly _bufferAllocator;
243
261
  private readonly _capContext;
244
262
  private readonly _onBufferResized;
245
- /** Construct with an `initialCapacity` number (legacy form) or an
263
+ /** Construct with an `_initialCapacity` number (legacy form) or an
246
264
  * options object (adds `bufferAllocator` and
247
- * `onBufferResized` callback). Both signatures coexist so test fixtures
265
+ * `onBufferReplaced` callback). Both signatures coexist so test fixtures
248
266
  * that pass `new Store(4)` keep working. */
249
267
  constructor(arg?: number | StoreOptions);
250
268
  /** Capacity of the entity-index SAB region (max slots ≈ max live
251
- * entities). Fixed at construction; a future
269
+ * entities). Fixed at construction. A future
252
270
  * follow-up will grow it via `growColumnStore` when `entityHighWater`
253
271
  * hits the cap. */
254
272
  private readonly _entityIndexCapacity;
273
+ /** Byte offset of the store header inside the backing. Fixed at
274
+ * construction, and every offset in the bytes is relative to it. */
275
+ private readonly _storeBase;
255
276
  /** Consumer-declared SAB regions, captured so the realloc path
256
277
  * re-lays them out. `undefined` when no consumer regions were declared.
257
278
  * The region contents survive a grow via the self-describing region table
258
- * (`extend.ts` snapshot/restore), so this is only the layout recipe. */
279
+ * (`extend.ts` snapshot and restore), so this is only the layout recipe. */
259
280
  private readonly _regions;
260
281
  /** Byte size of the opt-in sim-bindings region. 0 ⇒ no region (the
261
- * pure-TS default). Captured so the initial `createColumnStore` reserves it;
262
- * across a realloc the size is re-derived from the old header by
263
- * `optionsFromOld`, so it is not threaded through the grow/extend path. */
282
+ * pure-TS default). Captured so the initial `createColumnStore` reserves it.
283
+ * Across a realloc the size is re-derived from the old header by
284
+ * `optionsFromOld`, so it is not threaded through the grow and extend path. */
264
285
  private readonly _bindingsRegionBytes;
265
- /** Determinism opt-in. When `false` (the default), the
266
- * canonical-ordering determinism surface (`stateHash` / `snapshotSparse` /
267
- * `restoreSparse`) throws `DETERMINISM_DISABLED` rather than running its
268
- * sort. Memory-safety invariants (the in-place allocator) and the
269
- * `enabled_count` partition are unaffected — they hold regardless. */
286
+ /** Determinism opt-in. When `false` (the default), the canonical-ordering
287
+ * determinism surface throws `DETERMINISM_DISABLED` rather than run its
288
+ * sort. That surface is `stateHash`, `snapshotSparse` and `restoreSparse`.
289
+ * The memory-safety invariant of the in-place allocator and the
290
+ * `enabled_count` partition hold regardless. */
270
291
  private readonly _deterministic;
271
- /** Whether the determinism surface is enabled. `false` ⇒ `stateHash`
272
- * / `snapshotSparse` / `restoreSparse` throw `DETERMINISM_DISABLED`. */
292
+ /** Whether the determinism surface is enabled. `false` ⇒ `stateHash`,
293
+ * `snapshotSparse` and `restoreSparse` throw `DETERMINISM_DISABLED`. */
273
294
  get deterministic(): boolean;
274
295
  /** Guard the canonical-ordering determinism surface. Throws
275
296
  * `DETERMINISM_DISABLED` when determinism wasn't opted into, naming the
@@ -277,72 +298,62 @@ export declare class Store implements ObserverHost, QueryHost {
277
298
  * (not `DEV`-gated): the surface is cold (never per-tick) so one boolean
278
299
  * check is free, and a silent non-canonical digest is the failure mode we're
279
300
  * preventing. */
280
- private _requireDeterministic;
281
- /** Reject `f32`/`f64` fields on a `deterministic: true` world at registration.
282
- * IEEE-754 rounds differently across V8 / Bun / Zig at the 1-ULP
283
- * level, so a float column in a fixed-update path is a silent per-tick
284
- * `stateHash` divergence between client and server — the one thing the
285
- * determinism opt-in exists to prevent. Non-deterministic worlds
286
- * skip this entirely (floats stay allowed), so it costs the default path
287
- * nothing. `kind` names the storage class in the error ("component" /
288
- * "sparse component"); the array shorthand's `f64` default lands here too, so
289
- * a deterministic world must pass an explicit integer type. */
290
- private _rejectNonDeterministicFields;
301
+ private _assertDeterministic;
291
302
  /** Rebuild the Int32Array views over the SAB entity-index region
292
- * after a host-side SAB realloc (extend / grow). Called from
293
- * `_handleBufferResized` BEFORE the user-supplied `onBufferResized`
294
- * callback fires so any downstream reader sees coherent views. */
303
+ * after a host-side SAB realloc (extend and grow). Called from
304
+ * `_handleBufferResized`, before the user-supplied `onBufferReplaced`
305
+ * callback fires, so any downstream reader sees coherent views. */
295
306
  private _refreshEntityIndexViews;
296
- /** Centralised "SAB was just reallocated" handler. Refreshes the
297
- * Int32Array views FIRST (so user callbacks observe valid views),
307
+ /** Centralised "SAB was only reallocated" handler. Refreshes the
308
+ * Int32Array views first (so user callbacks observe valid views),
298
309
  * then mirrors `entityHighWater` into the region's length header,
299
310
  * then fires the user-supplied callback. */
300
311
  private _handleBufferResized;
301
- /** SAB backing every archetype's column views. Read-only handle; the
302
- * live mutation happens through `archGetOrCreateFromMask`.
303
- * Exposed for tests, snapshot/restore, and the upcoming
304
- * `columnStoreStateHash` wire-up. Production reads of column data should
312
+ /** SAB backing every archetype's column views. Read-only handle. The
313
+ * live mutation happens through `_archGetOrCreateFromMask`.
314
+ * Exposed for tests, snapshot and restore, and `columnStoreStateHash`.
315
+ * Production reads of column data should
305
316
  * still go through `Archetype.getColumnRead` (which sources from this
306
317
  * SAB under the hood). */
307
318
  get columnStore(): ColumnStore;
308
319
  /** Resolve a consumer-declared SAB region's byte offset by `region_id`, or
309
320
  * 0 when the region is absent (no region was declared with that id). The
310
- * generic, de-gamed replacement for the removed game-named accessors
311
- * (`terrain_view` / `spatial_grid_view` / … ); a consumer pairs this with
312
- * its own region module (e.g. `@internal/sim`'s region helpers) to
313
- * materialise a typed view. TS twin of Zig `abi.find_region`. */
321
+ * generic replacement for the removed game-named accessors, such as
322
+ * `terrain_view` and `spatial_grid_view`. A consumer pairs this with its own
323
+ * region module to materialise a typed view. It is the TS twin of the
324
+ * `abi.find_region` a WASM backend calls. */
314
325
  regionOffset(regionId: number): number;
315
326
  /** A handle to a consumer-declared SAB region resolved by `region_id`, or
316
- * `null` when absent. Carries the live `buffer`/`view` plus the region's byte
327
+ * `null` when absent. Carries the live `buffer` and `view` plus the region's byte
317
328
  * `offset` and `bytes`, so a consumer's region module can build a TypedArray
318
329
  * view over exactly the region's span without re-reading the directory.
319
- * Re-fetch after a SAB grow (the offset/view may have moved). */
330
+ * Re-fetch after a SAB grow (the offset and view may have moved). */
320
331
  regionHandle(regionId: number): ColumnStoreRegionHandle | null;
321
332
  /**
322
333
  * Stamp every SAB-backed archetype's live `length` into its descriptor's
323
- * `row_count` field. `extendColumnStore` /
334
+ * `row_count` field. `extendColumnStore` and
324
335
  * `growColumnStore` are the only other writers of `row_count`, and they
325
- * record the count at the moment of the resize `Archetype.addEntity`
336
+ * record the count at the moment of the resize, `Archetype.addEntity`
326
337
  * does not update it, so any insertion after the most recent resize
327
- * leaves the descriptor stale. Zig systems that drive their per-row loop
328
- * off `arch_hdr.row_count` (every `tick_*` export)
329
- * read those stale bytes and silently skip the just-spawned rows.
338
+ * leaves the descriptor stale. A compute backend that drives its per-row
339
+ * loop off the descriptor's `row_count` reads those stale bytes and
340
+ * silently skips the newly spawned rows.
330
341
  *
331
342
  * Lockstep walk: SAB descriptors are written by `extendColumnStore` in
332
343
  * the order non-SAB archetypes are promoted, which is the same id-order
333
- * those archetypes occupy in `this.archGraph.archetypes`. Iterating that array
344
+ * those archetypes occupy in `this._archGraph.archetypes`. Iterating that array
334
345
  * once, skipping non-SAB entries, and advancing an `archAddr` cursor
335
346
  * by the descriptor's `column_count` lets us write `row_count` without
336
347
  * the throwaway `Map<archId, length>` the previous version allocated
337
348
  * on every call. Cheap: descriptor-region seeks only, no column
338
349
  * I/O.
339
350
  *
340
- * Gated by `_rowCountsDirty` mutation paths
341
- * (`flushStructural`, `flushDestroyed`, immediate `destroyEntity`,
342
- * `addComponent(s)`, `removeComponent(s)`) set the flag; this method
351
+ * Gated by `_rowCountsDirty`, mutation paths
352
+ * (`flushStructural`, `flushDestroys`, immediate `destroyEntity`,
353
+ * `addComponent(s)`, `removeComponent(s)`) set the flag. This method
343
354
  * clears it. Read-only phases that flush only to drain empty buffers
344
355
  * pay nothing. */
345
- publishRowCountsToDescriptor(): void;
356
+ publishRowCounts(): void;
346
357
  /** FNV-1a-style 32-bit digest over (archetype_id, live_row_count, live
347
358
  * column bytes) for each archetype in id order, followed by the sparse
348
359
  * stores (out-of-identity components) in registration order.
@@ -350,90 +361,86 @@ export declare class Store implements ObserverHost, QueryHost {
350
361
  * determinism. It replaces the earlier per-networked-component fold.
351
362
  *
352
363
  * **Sparse coverage.** Sparse data lives outside the archetype
353
- * graph, so it is folded separately after the archetype loop per store:
364
+ * graph, so it is folded separately after the archetype loop, per store:
354
365
  * the sparse-component id, the member count, then each member's source
355
- * entity index + f64 field words, walked in CANONICAL ascending-index order
366
+ * entity index + f64 field words, walked in canonical ascending-index order
356
367
  * (`SparseComponentStore.canonicalIndices`). Canonical order is what makes
357
368
  * the digest insertion-order-independent: two worlds with identical sparse
358
- * contents built by different add/remove sequences agree. Keyed by entity
369
+ * contents built by different add and remove sequences agree. Keyed by entity
359
370
  * index, and destruction purges the slot, so a recycled index never carries
360
371
  * a stale occupant's data into the hash.
361
372
  *
362
373
  * It is strictly broader than the prior per-networked-component fold
363
- * (covers every column, not just a hand-picked subset of networked
374
+ * (covers every column, not only a hand-picked subset of networked
364
375
  * components), and strictly tighter than `columnStoreStateHash(...)`
365
376
  * which scans the full SAB including trailing unused capacity.
366
377
  *
367
378
  * **Per-word fold.** The inner column loop folds one 32-bit
368
- * word at a time using FNV-1a's `xor + imul(PRIME)` step. This is NOT
369
- * byte-for-byte FNV-1a-32 of the column bytes — it's a deterministic
379
+ * word at a time using FNV-1a's `xor + imul(PRIME)` step. This is not
380
+ * byte-for-byte FNV-1a-32 of the column bytes. It's a deterministic
370
381
  * digest with the same equality semantics, and much quicker than the
371
- * per-byte loop it replaces. Trailing 03 tail bytes (only possible for
372
- * u8/u16 columns at odd row counts) are folded together as a single
382
+ * per-byte loop it replaces. Trailing 0 to 3 tail bytes (only possible for
383
+ * u8 or u16 columns at odd row counts) are folded together as a single
373
384
  * little-endian word so the algorithm stays branch-free in the inner
374
385
  * loop. The 4-byte `id` and `len` headers are folded as words for the
375
386
  * same reason. Byte order is little-endian to match the platform's
376
- * native TypedArray layout; the digest is opaque (no consumer compares
387
+ * native TypedArray layout. The digest is opaque (no consumer compares
377
388
  * against a literal value), so endianness is an implementation detail
378
389
  * rather than wire contract.
379
390
  *
380
391
  * Determinism: same store ⇒ same digest within a process, and across
381
- * processes on the same architecture (which is all `replay_match`
382
- * needs both replays run the same algorithm on the same words).
392
+ * processes on the same architecture, which is all a lockstep replay
393
+ * needs, because both replays run the same algorithm on the same words.
383
394
  *
384
395
  * **Opt-in.** Throws `DETERMINISM_DISABLED` unless the
385
396
  * Store was constructed with `{ deterministic: true }`. The canonical
386
397
  * ordering this fold relies on (sparse `canonicalIndices`, sorted relation
387
398
  * target sets) is the determinism tax the flag gates. */
388
399
  stateHash(): number;
389
- private archGet;
390
- /** Look up the `EntityID` at `row` in archetype `archetype_id`. Used
391
- * by a WASM system to resolve an
392
- * `EntityID` from an event-ring payload Zig writes
393
- * `(archId, row, …)` to the event ring,
394
- * and TS bridges it back through `ctx.emit(...)` via this method.
400
+ private _archGet;
401
+ /** Look up the `EntityID` at `row` in archetype `archetype_id`. A compute
402
+ * backend resolves an `EntityID` from an event-ring payload through it. The
403
+ * backend writes `(archId, row, …)` to the event ring, and TS bridges it
404
+ * back through `ctx.emit(...)` via this method.
395
405
  *
396
406
  * Throws `ECSError` if `archetype_id` is out of range or `row` is
397
- * past the archetype's live row count these would indicate a
398
- * ring-payload corruption or a stale row index (extend / grow
399
- * happened mid-tick), both of which are bugs the parity test would
400
- * surface. */
407
+ * past the archetype's live row count. Either one means a corrupt
408
+ * ring payload or a stale row index (an extend or a grow happened
409
+ * mid-tick), and both are defects. */
401
410
  entityIdAtRow(archetypeId: number, row: number): EntityID;
402
- /** Find or create an archetype for the given component mask see
411
+ /** Find or create an archetype for the given component mask, see
403
412
  * `ArchetypeGraph.getOrCreateFromMask`. */
404
- private archGetOrCreateFromMask;
405
- /** Bulk variant of `archGetOrCreateFromMask` one `extendColumnStore`
413
+ private _archGetOrCreateFromMask;
414
+ /** Bulk variant of `_archGetOrCreateFromMask`, one `extendColumnStore`
406
415
  * call for the whole batch (the prewarm pass). See
407
- * `ArchetypeGraph.createManyFromMasks`. */
416
+ * `ArchetypeGraph.getOrCreateFromMasks`. */
408
417
  archCreateManyFromMasks(masks: readonly BitSet[]): ArchetypeID[];
409
- /** Snapshot every existing archetype's SAB rows, call `extendColumnStore`
410
- * once with `newSpecs`, then refresh every pre-existing SAB-backed
411
- * Archetype's TypedArray views. The single `existing` snapshot is the
412
- * key win in the bulk variant single-mask creation rebuilds it per
413
- * call (i.e. N times for N new archetypes). */
414
- private _archExtendStoreWithNewSpecs;
415
- /** Materialise the `Archetype` object for a freshly-minted graph node
416
- * binds the graph's topology to THIS store's column backing and grow
418
+ /** Call `extendColumnStore` once with `newSpecs`, then refresh every
419
+ * pre-existing SAB-backed Archetype's TypedArray views when the extend had
420
+ * to realloc. The `existing` row counts are built only when that realloc
421
+ * path runs: the in-place path moves no rows and reads no list, and to walk
422
+ * every archetype on every extend made the cost of the N-th archetype grow
423
+ * with N. The bulk variant makes one extend for many archetypes, so the
424
+ * list is built at most one time for them all. */
425
+ private _extendStore;
426
+ /** Materialise the `Archetype` object for a freshly-minted graph node,
427
+ * binds the graph's topology to this store's column backing and grow
417
428
  * handler (`ArchetypeGraphHost.materialize`). Store-owned so the graph
418
429
  * never touches `_columnStore`. */
419
430
  private _materializeArchetype;
420
- /** Push a newly-installed archetype into every registered query whose masks
421
- * it satisfies (`ArchetypeGraphHost.fanIntoQueries`; the query registry
422
- * stays on Store). No epoch bump — see the note in `ArchetypeGraph.install`. */
423
- private _fanIntoQueries;
424
431
  /** Resolve "add component_id to archetype_id" → target ArchetypeID (edge-cached). */
425
- private archResolveAdd;
432
+ private _archResolveAdd;
426
433
  /** Resolve "remove component_id from archetype_id" → target ArchetypeID (edge-cached). */
427
- private archResolveRemove;
434
+ private _archResolveRemove;
428
435
  createEntity(): EntityID;
429
- /** Allocate an entity slot WITHOUT placing it in the empty archetype, for
430
- * the template spawn paths. Returns the packed `EntityID`; the slot index
431
- * is left in `entityAllocator.lastIndex`. Skips the empty-archetype
436
+ /** Allocate an entity slot without placing it in the empty archetype, for
437
+ * the template spawn paths. Returns the packed `EntityID`. The slot index
438
+ * is left in `_entityAllocator.lastIndex`. Skips the empty-archetype
432
439
  * membership write `createEntity` performs (the caller installs the real
433
440
  * archetype + row). This *commits* the slot (bumps counts, stamps the
434
- * generation so `isAlive` is already true), so the caller MUST have
441
+ * generation so `isAlive` is already true), so the caller must have
435
442
  * reserved the column capacity for the row first
436
- * (`Archetype.ensureRowCapacity`) otherwise a cap throw from the
443
+ * (`Archetype.reserveRows`), otherwise a cap throw from the
437
444
  * subsequent append leaves the slot phantom-alive. */
438
445
  private _allocEntity;
439
446
  /** Pre-check that `count` fresh entity slots can be allocated without
@@ -441,17 +448,17 @@ export declare class Store implements ObserverHost, QueryHost {
441
448
  * `_allocEntity`'s own per-call high-water guard would otherwise
442
449
  * throw `EID_MAX_INDEX_OVERFLOW` partway through the alloc loop, leaving the
443
450
  * slots it already committed phantom-alive. Free-list reuse covers the first
444
- * `entityFreeIndices.length` slots; only the remainder draws down the
451
+ * `entityFreeIndices.length` slots. Only the remainder draws down the
445
452
  * high-water headroom. */
446
- private _ensureEntityIndexCapacity;
447
- /** Resolve a template: compute the target archetype (creating it if absent
453
+ private _assertEntityIndexCapacity;
454
+ /** Build a template: compute the target archetype (creating it if absent,
448
455
  * fits the prewarm model), pre-flatten default field values into
449
456
  * `_flatColumns` order, and build the override index (field name → flat
450
- * column index; `TEMPLATE_OVERRIDE_AMBIGUOUS` for a name shared by more than
457
+ * column index. `TEMPLATE_OVERRIDE_AMBIGUOUS` for a name shared by more than
451
458
  * one component, which a flat override cannot target). */
452
- resolveTemplate(entries: readonly TemplateEntryData[]): Template;
459
+ createTemplate(entries: readonly TemplateEntryData[]): Template;
453
460
  /** Resolve an override key to its flat column index, with the DEV guards
454
- * for unknown and ambiguous field names; `-1` means skip (the production
461
+ * for unknown and ambiguous field names. `-1` means skip (the production
455
462
  * fallback where DEV would have thrown). */
456
463
  private _resolveOverrideColumn;
457
464
  /** Apply per-instance overrides to the freshly-spawned row. Each key is a
@@ -465,20 +472,20 @@ export declare class Store implements ObserverHost, QueryHost {
465
472
  * applies any per-instance overrides. */
466
473
  spawn(p: Template, overrides?: Record<string, number | undefined>): EntityID;
467
474
  /** Bulk-spawn `count` identical entities into the template's archetype. The
468
- * field writes are O(columns) one `TypedArray.fill` per column via
469
- * `addEntitiesWithValues` not O(count×columns). Returns the new ids in
475
+ * field writes are O(columns), one `TypedArray.fill` per column via
476
+ * `addEntitiesWithValues`, not O(count×columns). Returns the new ids in
470
477
  * spawn order. */
471
478
  spawnMany(p: Template, count: number, overrides?: Record<string, number | undefined>): EntityID[];
472
479
  /** Immediately destroy an entity, removing it from its archetype.
473
480
  *
474
- * With no `delete`/`clear` target-cleanup policy registered (the common
475
- * case) this tears the one entity down and returns no allocation. When a
481
+ * With no `delete` or `clear` target-cleanup policy registered (the common
482
+ * case) this tears the one entity down and returns, no allocation. When a
476
483
  * policy is in play, a `delete`-target's sources are appended to a local
477
484
  * work-list this method then drains in the same iterative pass:
478
485
  * the `work.length` re-read drives chains and trees out without recursion, so
479
486
  * depth is bounded by entity count, not tree depth. This mirrors the deferred
480
- * `flushDestroyed` buffer mechanism both paths are iterative and reach the
481
- * identical end state; the only difference is the shared `pendingDestroy`
487
+ * `flushDestroys` buffer mechanism, both paths are iterative and reach the
488
+ * identical end state. The only difference is the shared `pendingDestroy`
482
489
  * buffer there vs. a local work-list here. `isAlive` dedups a source reached
483
490
  * twice (diamonds) and terminates cycles, exactly as the generation guard does
484
491
  * in the deferred loop. */
@@ -487,20 +494,20 @@ export declare class Store implements ObserverHost, QueryHost {
487
494
  * then recycle (or retire) its slot. Shared by both immediate-destroy entry
488
495
  * points (the fast no-cascade path and the work-list driver in
489
496
  * `destroyEntity`). When `cascade` is non-null, a `delete`-policy target's
490
- * surviving sources are appended to it for the driver to drain;
497
+ * surviving sources are appended to it for the driver to drain.
491
498
  * `null` skips that collection for callers that cannot cascade. The caller
492
499
  * must have already confirmed `id` is alive. */
493
500
  private _destroyOne;
494
501
  /**
495
- * Liveness check, **fail-closed** against forged / retired / out-of-bounds
502
+ * Liveness check, **fail-closed** against a forged, retired or out-of-bounds
496
503
  * handles. For a general-purpose engine that may receive a handle from
497
- * serialization, IPC, or any untrusted caller, three malformed inputs must read
504
+ * serialization, ipc, or any untrusted caller, three malformed inputs must read
498
505
  * dead rather than alias a slot:
499
- * - **Out of range** an `id` outside the 31-bit packed space (`< 0` or
506
+ * - **Out of range**, an `id` outside the 31-bit packed space (`< 0` or
500
507
  * `> MAX_ENTITY_ID`). Without this, the 20-bit index mask below silently
501
- * folds garbage high bits onto a valid slot. (Same bound the snapshot /
502
- * postMessage decode applies.)
503
- * - **Tombstone generation** a handle carrying `RETIRED_GENERATION`, which
508
+ * folds garbage high bits onto a valid slot. (The snapshot decode and the
509
+ * `postMessage` decode apply the same bound.)
510
+ * - **Tombstone generation**, a handle carrying `RETIRED_GENERATION`, which
504
511
  * the allocator stamps into a retired slot and never issues to a live
505
512
  * entity, would otherwise match a retired slot's parked generation and read
506
513
  * alive (the ABA tombstone, previously documented as a known gap).
@@ -511,18 +518,18 @@ export declare class Store implements ObserverHost, QueryHost {
511
518
  /**
512
519
  * Liveness and the packed index in one result: the entity index if `id` is
513
520
  * live, else `-1`. Same three fail-closed guards as `isAlive` (documented
514
- * above), and the sole implementation of them `isAlive` is a comparison on
521
+ * above), and the sole implementation of them, `isAlive` is a comparison on
515
522
  * top of this.
516
523
  *
517
524
  * Why it returns the index instead of a boolean: every by-id caller needs
518
- * BOTH answers, and the pair used to cost two derivations of the same index.
519
- * `hasComponent` called `isAlive(id)` which computed `getEntityIndex(id)`
520
- * internally and then computed `getEntityIndex(id)` again to reach
521
- * `entityArchetype`. The generational check has already touched the index; a
525
+ * both answers, and the pair used to cost two derivations of the same index.
526
+ * `hasComponent` called `isAlive(id)`, which computed `getEntityIndex(id)`
527
+ * internally, and then computed `getEntityIndex(id)` again to reach
528
+ * `_entityArchetypes`. The generational check has already touched the index. A
522
529
  * caller that is about to index a parallel array with it should be handed the
523
530
  * one that was computed, not re-derive it. The generations read is inlined
524
531
  * here for the same reason, rather than delegated to
525
- * `entityAllocator.isAliveIndex` which stays as the index-domain entry point
532
+ * `_entityAllocator.isAliveIndex`, which stays as the index-domain entry point
526
533
  * for callers that already hold an index.
527
534
  *
528
535
  * **This gives much less than it appears to give.** The estimate for this
@@ -531,7 +538,7 @@ export declare class Store implements ObserverHost, QueryHost {
531
538
  * the estimate. The work that we removed was truly not necessary. But V8
532
539
  * already made both one-line functions inline, and it already removed most of
533
540
  * the duplicated calculation. Therefore "the work is plainly not necessary" is
534
- * not an argument about performance for a JIT compiler. This note stops the
541
+ * not an argument about performance for a jit compiler. This note stops the
535
542
  * next reader from making the same estimate again.
536
543
  */
537
544
  private _liveIndex;
@@ -539,32 +546,33 @@ export declare class Store implements ObserverHost, QueryHost {
539
546
  /** An archetype's row count moved from `preLen` to its current
540
547
  * `arch.length` on a **shrink** (rows removed: the source of a transition, a
541
548
  * destroy, a batch-source drain). Always marks SAB row counts dirty
542
- * (the descriptor walk just needs "something moved"); bumps the query-dirty
543
- * epoch only on a `length` 0/non-zero crossing, the only case where
549
+ * (the descriptor walk only needs "something moved"). It bumps the
550
+ * query-dirty epoch only when `length` crosses zero, the only case where
544
551
  * `Query._nonEmptyArchetypes` can change on a shrink. Mutations that
545
552
  * move row counts within the same side (6→5) leave the non-empty set unchanged
546
553
  * and skip the bump.
547
554
  *
548
- * A shrink does **not** need the `enabledCount` crossing test: the
549
- * only enabled-count move it can make is 1→0 (the last enabled row leaves an
550
- * archetype that keeps disabled rows), which leaves the archetype in a default
551
- * query's non-empty list as a harmless stale *inclusion* `count`/`forEach`
552
- * bound on `enabledCount` (now 0) iterate it zero times. Only a **grow** into
555
+ * A shrink does **not** need the `enabledCount` crossing test. The only
556
+ * enabled-count move it can make is 1→0, when the last enabled row leaves an
557
+ * archetype that keeps disabled rows. That leaves the archetype in a default
558
+ * query's non-empty list as a harmless stale *inclusion*. `count` and
559
+ * `forEach` bound on `enabledCount` (now 0), so they iterate it zero times.
560
+ * Only a **grow** into
553
561
  * an all-disabled archetype can stale-*exclude* a live row, so the enabled
554
562
  * crossing lives in `_onArchGrow`, off this path.
555
563
  *
556
- * **Inlining-sensitive keep the body tiny.** This function is called
557
- * once or twice per immediate-mode `addComponent` / `removeComponent` and the
558
- * mutation hot path depends on it being inlined at every call site.
559
- * An earlier change added an `if (registeredQueries.length === 0) return;`
560
- * gate to skip the bump for no-query workloads. The bench showed a large
561
- * regression of the mutation churn loop, because the extra statement pushed
562
- * the function past V8's per-call inlining budget. The gate is no longer in
563
- * the code. Do a bench run before you merge a change here. Code review
564
- * alone is not sufficient. */
565
- private _onArchLenChange;
566
- /** An archetype **grew** rows were appended (the target of a transition, a
567
- * spawn, a batch-target fill). Like `_onArchLenChange` it marks row counts
564
+ * Inlining-sensitive, keep the body tiny. Each immediate-mode `addComponent`
565
+ * or `removeComponent` calls it once or twice. The mutation hot path depends
566
+ * on the inline at every call site.
567
+ * An earlier change added an `if (_registeredQueries.length === 0) return;`
568
+ * gate to skip the bump for no-query workloads. The mutation churn loop got
569
+ * far slower, because the extra statement pushed the function past V8's
570
+ * per-call inlining budget. The gate is no longer in the code. Measure the
571
+ * mutation hot path before you merge a change here. Code review alone is
572
+ * not sufficient. */
573
+ private _onArchShrink;
574
+ /** An archetype **grew**, rows were appended (the target of a transition, a
575
+ * spawn, a batch-target fill). Like `_onArchShrink` it marks row counts
568
576
  * dirty and bumps the query-dirty epoch on a `length` 0/non-zero crossing
569
577
  * (`includeDisabled` membership), but it *also* bumps on an `enabledCount`
570
578
  * 0→1 crossing. The non-empty filter is field-split: a default
@@ -573,12 +581,12 @@ export declare class Store implements ObserverHost, QueryHost {
573
581
  * enabledCount == 0`) crosses `enabledCount` 0→1 without touching `length`,
574
582
  * so the `preLen` test alone (the earlier proxy, valid only while
575
583
  * `enabledCount === length`) misses it and a cached default query keeps a stale
576
- * `_nonEmpty` list. Only grows can do this, so only grow sites carry the test.
584
+ * `nonEmptyArchs` list. Only grows can do this, so only grow sites carry the test.
577
585
  *
578
586
  * **Precondition: ≥1 row was appended** (every caller adds at least one row),
579
- * so `arch.length > 0` afterward which is why the crossings simplify and the
580
- * body stays inlinable (the inlining caveat on `_onArchLenChange` applies
581
- * here too; this is bench-verified). The general
587
+ * so `arch.length > 0` afterward, which is why the crossings simplify and the
588
+ * body stays inlinable (the inlining caveat on `_onArchShrink` applies
589
+ * here too). The general
582
590
  * `(pre === 0) !== (post === 0)` boundary test collapses given the post side:
583
591
  * - `length`: post > 0 always ⇒ a crossing iff `preLen === 0`.
584
592
  * - `enabledCount`: non-decreasing on a grow ⇒ a 0-crossing iff it was 0
@@ -587,17 +595,17 @@ export declare class Store implements ObserverHost, QueryHost {
587
595
  * read is short-circuited away on the hot path (`preLen` or `preEnabled`
588
596
  * non-zero), so a no-disabled workload pays only two scalar compares. */
589
597
  private _onArchGrow;
590
- /** Dirty bookkeeping for an enable/disable toggle. `length` is
591
- * unchanged (no row added/removed) but `enabled_count` moved, so: republish
592
- * row counts (the descriptor's `enabled_count` changed, so the WASM sim and
593
- * snapshot see the new partition), and bump the query epoch only when the
594
- * *enabled* count crossed 0 the boundary at which an archetype enters/leaves
595
- * a query's non-empty set (`Query._nonEmpty` filters on `entityCount`, which
596
- * is now `enabled_count`). */
598
+ /** Dirty bookkeeping for an enable and disable toggle. `length` is
599
+ * unchanged (no row added or removed) but `enabled_count` moved, so: republish
600
+ * row counts, because the descriptor's `enabled_count` changed and a compute
601
+ * backend and the snapshot must see the new partition. Bump the query epoch only when
602
+ * the *enabled* count crossed 0. That is the boundary at which an archetype
603
+ * enters and leaves a query's non-empty set, because `Query.nonEmptyArchs`
604
+ * filters on `entityCount`, which is now `enabled_count`. */
597
605
  private _onArchEnabledChange;
598
606
  /** Immediately disable an entity (idempotent). The entity must hold at least
599
- * one component a component-less entity occupies no archetype row, so it
600
- * cannot be partitioned (a `DEV` error; prod no-op). */
607
+ * one component, a component-less entity occupies no archetype row, so it
608
+ * cannot be partitioned (a `DEV` error, prod no-op). */
601
609
  disableEntity(id: EntityID): void;
602
610
  /** Immediately enable an entity (idempotent). */
603
611
  enableEntity(id: EntityID): void;
@@ -605,53 +613,53 @@ export declare class Store implements ObserverHost, QueryHost {
605
613
  * disabled (it has no row to partition). */
606
614
  isDisabled(id: EntityID): boolean;
607
615
  /** 0-crossing detection for the per-entity flush paths (`_flushAdds`,
608
- * `_flushRemoves`) without per-entity Map traffic the same cost the
616
+ * `_flushRemoves`) without per-entity Map traffic, the same cost the
609
617
  * destroy drain also avoids. Each touched archetype is stamped with the
610
618
  * current flush epoch (`Archetype._flushSeenEpoch`), its pre-length and
611
- * pre-enabled-count recorded on first sight (`_flushPreLen` /
612
- * `_flushPreEnabled`), and pushed onto this scratch list;
619
+ * pre-enabled-count recorded on first sight (`_flushPreLen` and
620
+ * `_flushPreEnabled`), and pushed onto this scratch list
613
621
  * `_settleFlushDirty` walks the list once after the loop. The field
614
622
  * accesses per entity replace a `Map.has` + `Map.set` hash probe pair. The
615
623
  * epoch is bumped at settle so the next flush re-records. */
616
624
  private _flushEpoch;
617
625
  private readonly _flushTouched;
618
626
  /** Resolve dirty flags for a per-entity batch flush from the captured
619
- * pre-counts. Marks row counts dirty if any archetype was touched; bumps
627
+ * pre-counts. Marks row counts dirty if any archetype was touched. Bumps
620
628
  * the query epoch once if any touched archetype crossed the 0 boundary on
621
629
  * *either* `length` (includeDisabled membership) or `enabledCount`
622
- * (default-query membership) the deferred analog of the immediate
623
- * `_onArchLenChange` two-field check. A single bump is sufficient
630
+ * (default-query membership), the deferred analog of the immediate
631
+ * `_onArchShrink` two-field check. A single bump is sufficient
624
632
  * (queries only need to know "something changed"). Clears the touched list
625
633
  * and advances the flush epoch on exit. */
626
634
  private _settleFlushDirty;
627
635
  destroyEntityDeferred(id: EntityID): void;
628
- /** Buffer an enable/disable toggle for the phase flush. The row swap a
636
+ /** Buffer an enable and disable toggle for the phase flush. The row swap a
629
637
  * toggle performs would corrupt a `forEach` over that archetype if applied
630
- * mid-system, so it is deferred like add/remove. */
638
+ * mid-system, so it is deferred like an add and a remove. */
631
639
  disableEntityDeferred(id: EntityID): void;
632
640
  enableEntityDeferred(id: EntityID): void;
633
- /** Drain buffered enable/disable toggles, applying each in operation order via
641
+ /** Drain buffered enable and disable toggles, applying each in operation order via
634
642
  * the immediate path (which is idempotent and updates dirty flags). Called at
635
- * the flush boundary after structural adds/removes settle, so a toggle sees the
643
+ * the flush boundary after structural adds and removes settle, so a toggle sees the
636
644
  * entity's final archetype placement for the tick.
637
645
  *
638
- * When an onDisable/onEnable observer is registered (`_toggleObserverCount >
646
+ * When an onDisable and onEnable observer is registered (`_toggleObserverCount >
639
647
  * 0`) this also collects effective toggle events into `_obsEvents` for
640
648
  * the dispatch hook, collapsed to one event per *net* transition across the
641
649
  * drain (see `_toggleInitial`). The no-observer path is byte-for-byte the
642
650
  * earlier drain. */
643
651
  private _flushToggles;
644
- /** Fan one entity's net toggle transition out to an onDisable / onEnable event
652
+ /** Fan one entity's net toggle transition out to an onDisable and onEnable event
645
653
  * per carried component. Walks the entity's archetype mask through the
646
- * matching pre-bound bit visitor; a component-less entity (no row) carries
654
+ * matching pre-bound bit visitor. A component-less entity (no row) carries
647
655
  * nothing and is skipped. */
648
656
  private _collectToggle;
649
657
  get pendingToggleCount(): number;
650
658
  /** Flush all buffered entity destructions in batch.
651
659
  *
652
660
  * When onRemove observers are registered (`_structuralObserverCount > 0`),
653
- * a destroy fires onRemove for every component the entity carried a destroy
654
- * *is* a remove of the whole mask collected here and dispatched by the
661
+ * a destroy fires onRemove for every component the entity carried, a destroy
662
+ * *is* a remove of the whole mask, collected here and dispatched by the
655
663
  * `flushStructural` fixed-point loop, the only caller in that mode (it drains
656
664
  * `pendingDestroy` each round so the trailing `ctx.flush()` call is a no-op).
657
665
  * Same commit-then-observe discipline as `_flushRemoves`: the entity is fully
@@ -660,151 +668,227 @@ export declare class Store implements ObserverHost, QueryHost {
660
668
  * no-observer path is byte-for-byte unchanged (`collecting` gate).
661
669
  *
662
670
  * Re-entrancy: while the observed fixed point owns the flush, the loop
663
- * drains destroys itself via `_drainDestroyed`, so a re-entrant
671
+ * drains destroys itself via `_flushDestroys`, so a re-entrant
664
672
  * `ctx.flush()` from a callback no-ops (the guard lives in
665
- * `DeferredCommandBuffer.flushDestroyed`) otherwise it would collect
673
+ * `DeferredCommandBuffer.flushDestroys`), otherwise it would collect
666
674
  * into the shared `_obsEvents` scratch mid-dispatch and corrupt it. */
667
- flushDestroyed(): void;
668
- private _drainDestroyed;
675
+ flushDestroys(): void;
676
+ private _flushDestroys;
669
677
  get pendingDestroyCount(): number;
670
678
  addComponentDeferred(entityId: EntityID, def: ComponentDef<Record<string, never>>): void;
671
679
  addComponentDeferred<S extends ComponentSchema>(entityId: EntityID, def: ComponentDef<S>, values: FieldValues<S>): void;
672
680
  removeComponentDeferred(entityId: EntityID, def: ComponentDef): void;
673
- /** Phase-boundary structural flush. The drain policy no-observer fast
674
- * path, observed fixed point (adds/removes → destroys → toggles),
675
- * convergence guard, re-entrancy lives in `DeferredCommandBuffer`
676
- *; the batch appliers it drives are the `_flush*` /
677
- * `_drainDestroyed` methods below. */
681
+ /** Phase-boundary structural flush. The drain policy, no-observer fast
682
+ * path, observed fixed point (adds and removes → destroys → toggles),
683
+ * convergence guard, re-entrancy, lives in `DeferredCommandBuffer`.
684
+ * The batch appliers it drives are the `_flush*` and
685
+ * `_flushDestroys` methods below. */
678
686
  flushStructural(): void;
679
687
  /** Batch-apply all deferred component additions. */
680
688
  private _flushAdds;
681
689
  /** Batch-apply all deferred component removals. */
682
690
  private _flushRemoves;
683
691
  get pendingStructuralCount(): number;
684
- /** Set the per-component observation flags from the registry's aggregate of
685
- * live observers for `cid`. Maintains `_structuralObserverCount` and
692
+ /** Record what one consumer of the change feed asks the store to collect
693
+ * for `cid`, then apply the OR of every consumer's ask.
694
+ *
695
+ * `consumer` is the plugin name. The observer registry passes
696
+ * `"observers"`. One consumer dropping a flag never takes that flag away
697
+ * from another, which is why the merge sits in front of the transition
698
+ * logic. All-false is the same as absent. Cold path: a consumer calls this
699
+ * when it gains or loses an observer. */
700
+ configureObservation(consumer: string, cid: number, flags: ObservationFlags): void;
701
+ /** Set the per-component observation flags from the merged ask of every
702
+ * consumer for `cid`. Maintains `_structuralObserverCount` and
686
703
  * `_toggleObserverCount` (the fast-path gates) and lazily allocates the
687
704
  * dirty list when per-entity onSet tracking turns on. */
688
- _configureComponentObservation(cid: number, hasAdd: boolean, hasRem: boolean, hasDisable: boolean, hasEnable: boolean, trackDirty: boolean): void;
689
- /** Record a per-entity onSet "changed" event for the entity. Called from the
690
- * field-write path (`SystemContext.setField` / `markChanged`) and gated by
691
- * the caller on `_anyDirtyTracked`. Appends to the dirty list only if the
692
- * dedup bit was clear (the dirty list + dedup-bit mechanism). */
693
- _noteSet(def: ComponentHandle, eid: EntityID): void;
694
- private _growDirtyMarks;
695
- /** Detach and return the dirty-row list for `cid`, clearing its dedup bits and
696
- * leaving the store with a fresh empty list (so re-dirties during the drain
697
- * accumulate for the NEXT tick, not this one). Returns a shared empty array
698
- * when nothing is dirty. Caller owns the returned array. */
699
- _takeDirty(cid: number): EntityID[];
700
- /** Clear any dirty dedup bits for a freed entity index across every tracked
701
- * component, so a recycled slot at the same index can be marked afresh. Gated
702
- * by `_anyDirtyTracked` at the destroy call sites. */
703
- private _clearDirtyForIndex;
705
+ private _applyObservation;
706
+ /** Give `cid` a row tick plane: one change tick for each row of every
707
+ * archetype that holds it, stamped by every write path. The row grain of
708
+ * change detection. An entity-level onSet observer calls this. Idempotent,
709
+ * and never undone. Cold. */
710
+ trackRows(def: ComponentHandle | SparseComponentDef): void;
711
+ /** Record what one consumer of the change feed asks the store to collect
712
+ * for sparse component `sid`, then apply the OR of every consumer's ask.
713
+ * Same merge rule as the dense form. Cold path. */
714
+ configureSparseObservation(consumer: string, sid: number, hasSet: boolean): void;
715
+ /** Row ticks on for sparse component `sid`, or the pending records dropped
716
+ * when the last consumer of its change feed leaves. */
717
+ private _applySparseObservation;
718
+ /** Collect the members of sparse component `sid` recorded since the last
719
+ * drain, for the entity-level onSet dispatch. `run` is the change tick of
720
+ * the dispatch. A member is alive by construction, because a destroy purges
721
+ * it, and the walk skips a disabled one, which default queries hide. The
722
+ * component-level change tick gates the walk, so an idle component costs
723
+ * one compare. Member order. The result array is reused. */
724
+ drainSparseSet(sid: number, run: number): EntityID[];
725
+ /** The row tick of `entityId`'s sparse component, 0 for a non-member, for
726
+ * `ctx.sparseChanged`. Throws when the component keeps no row ticks. */
727
+ sparseTickOf(def: SparseComponentDef, entityId: EntityID): number;
728
+ /** The store a mutable sparse cursor stamps through on each `at()`
729
+ * (ref.ts `createSparseCursor`). */
730
+ sparseTickPlane(def: SparseComponentDef): SparseComponentStore;
731
+ /** `trackRows` by component id, for the observer registry, which holds
732
+ * the id alone. */
733
+ private _trackRowsById;
734
+ /** The list length past which a frame switches to the scan. A list entry
735
+ * costs a push, three checks and a sort slot. A scan costs one compare per
736
+ * row of each stamped archetype, and those rows are at most the live
737
+ * entities, so a cap of a fraction of the live count keeps the list cost
738
+ * paid below the scan cost that replaces it, whatever the world's size. */
739
+ private _listCap;
740
+ /** Record a row for the entity-level onSet of `cid`, from a path that has
741
+ * resolved the archetype and the row (`setField`, `ref`, a cursor). Gated
742
+ * by the caller on `anyDirtyTracked`. Stamps the row tick, and pushes the
743
+ * entity onto the dirty list when the row's previous stamp lay at or below
744
+ * the last drain: the row is then new to the list. Hot path on the by-id
745
+ * writes of a tracked component: two loads, one compare, one store. */
746
+ noteSet(cid: number, arch: Archetype, row: number, eid: EntityID): void;
747
+ /** `noteSet` for a caller that holds the entity alone (`ctx.markChanged`).
748
+ * A dead entity, and one that does not hold the component, record nothing. */
749
+ noteSetEntity(def: ComponentHandle, eid: EntityID): void;
750
+ /** A chunk loop took the row tick column of `cid` (`cols.ticks`). Its
751
+ * stores make no list entry, so the next drain scans the plane. */
752
+ noteScan(cid: number): void;
753
+ /** Collect the rows recorded for `cid` since the last drain, for the
754
+ * entity-level onSet dispatch. `run` is the change tick of the dispatch.
755
+ *
756
+ * Two sources. `scanned` comes from a walk of the tick plane of every
757
+ * archetype stamped since the last drain, taken only in a frame where a
758
+ * chunk loop took `cols.ticks(def)`. A row inside the enabled partition is
759
+ * alive, a member and enabled by construction, so the dispatch fires these
760
+ * with no check, and the walk costs one compare per row of each stamped
761
+ * archetype. `listed` comes from the dirty list the by-id paths keep, minus
762
+ * the entries a scan covers. A listed entity may have died, lost the
763
+ * component or been disabled since its record, so the dispatch checks
764
+ * each. Both come back in row order, and the result object is reused. */
765
+ drainSet(cid: number, run: number): DrainResult;
704
766
  /** Visit every non-empty archetype containing `cid` whose component-column
705
- * changed at or after `baseline`, in canonical (ascending archetype-id) order
706
- * the archetype-granular onSet detection point. Reuses the existing
707
- * per-archetype change tick (free; no write-path cost). */
708
- _forEachChangedArchetype(cid: number, baseline: number, cb: (arch: Archetype) => void): void;
767
+ * changed after `baseline`, in canonical (ascending archetype-id) order,
768
+ * the archetype-granular onSet detection point. Reuses the existing
769
+ * per-archetype change tick (free, no write-path cost). `baseline` is the
770
+ * change tick of the consumer's previous visit, so a stamp equal to it was
771
+ * made by that visit and is not reported again. */
772
+ forEachChangedArchetype(cid: number, baseline: number, cb: (arch: Archetype) => void): void;
709
773
  /** Enabled live entities currently carrying `cid`, used by `yieldExisting` to
710
774
  * replay onAdd on registration. Bounded by `enabled_count`: a disabled
711
775
  * entity is excluded from default queries, so seeding it via onAdd would
712
- * publish a row that an immediate onDisable should have removed it is simply
713
- * absent at seed (the "delete on disable" semantics). Unordered here the
776
+ * publish a row that an immediate onDisable should have removed. It is
777
+ * absent at seed (the "delete on disable" semantics). Unordered here, the
714
778
  * registry radix-sorts. */
715
- _collectEntitiesWithComponent(cid: number): EntityID[];
779
+ collectEnabledWith(cid: number): EntityID[];
716
780
  registerComponent<S extends Record<string, TypedArrayTag>>(schema: S, name?: string): ComponentDef<S>;
717
781
  /** `'Pos' (component 5)` when the component was registered with a debug
718
- * name, else `component 5` the label diagnostics interpolate. */
782
+ * name, else `component 5`, the label diagnostics interpolate. */
719
783
  componentLabel(cid: number): string;
720
- /** Return the field index assigned to `(def, fieldName)` at component
721
- * registration. Indexes are insertion-order, zero-based, and stable for
722
- * the lifetime of the ECS. Used by systems that pass `(component_id,
723
- * field_id)` pairs across the WASM FFI. */
784
+ /** The field index assigned to `(def, fieldName)` at registration. A system
785
+ * that passes `(component_id, field_id)` pairs across the WASM FFI resolves
786
+ * them once, at setup. */
724
787
  fieldIdOf(def: ComponentHandle, fieldName: string): number;
725
788
  /** Register a sparse component or tag. Unlike `registerComponent`, this
726
789
  * allocates from a separate id space and never touches the archetype mask,
727
790
  * so it does **not** count against `STORE_DESCRIPTOR_COMPONENT_LIMIT`. See
728
791
  * `sparse_store.ts`. */
729
792
  registerSparseComponent<S extends Record<string, TypedArrayTag>>(schema: S, name?: string): SparseComponentDef<S>;
730
- /** Sparse sibling of `componentLabel` sparse ids are a separate id space. */
793
+ /** Sparse sibling of `componentLabel`, sparse ids are a separate id space. */
731
794
  sparseLabel(sid: number): string;
732
- /** Allocate the backing sparse store WITHOUT the float guard, for
733
- * engine-internal backings whose `f64` holds an EXACT integer rather than a
795
+ /** Allocate the backing sparse store without the float guard, for
796
+ * engine-internal backings whose `f64` holds an exact integer rather than a
734
797
  * user quantity: the exclusive-relation `{ target }` slot stores an `EntityID`
735
- * (≤ 2^53, so f64 is bit-exact and cross-host identical the ban targets float
798
+ * (≤ 2^53, so f64 is bit-exact and cross-host identical, the ban targets float
736
799
  * *arithmetic* rounding, which a target slot never undergoes). User schemas go
737
800
  * through `registerSparseComponent`, which guards first. */
738
801
  private _pushSparseStore;
739
- private sparseStoreOf;
802
+ private _sparseStoreOf;
740
803
  /** Add (or overwrite) a sparse component on an entity. No archetype
741
- * transition, no row copy the entity's `archetype_id` is unchanged. */
804
+ * transition, no row copy, the entity's `archetype_id` is unchanged. */
742
805
  addSparse(entityId: EntityID, def: SparseComponentDef, values?: Record<string, number>): void;
743
806
  /** Remove a sparse component from an entity. No-op if absent. */
744
807
  removeSparse(entityId: EntityID, def: SparseComponentDef): void;
745
- /** Total, like `hasComponent` `false` for a dead entity, never a throw. */
808
+ /** Total, like `hasComponent`, `false` for a dead entity, never a throw. */
746
809
  hasSparse(entityId: EntityID, def: SparseComponentDef): boolean;
747
810
  getSparseField(entityId: EntityID, def: SparseComponentDef, field: string): number;
748
811
  setSparseField(entityId: EntityID, def: SparseComponentDef, field: string, value: number): void;
749
812
  /** Drop all sparse data for a destroyed entity index so a recycled slot
750
- * can't inherit it. Gated by the caller on `sparseStores.length > 0` to
813
+ * can't inherit it. Gated by the caller on `_sparseStores.length > 0` to
751
814
  * keep the destroy hot path free when sparse storage is unused. */
752
815
  private _purgeSparse;
816
+ /** A sparse component's field names in schema order, the cursor's reserved
817
+ * name check (ref.ts). */
818
+ sparseFieldNames(def: SparseComponentDef): readonly string[];
819
+ /** A sparse component's accessor column array (ref.ts). Its identity is
820
+ * stable for the life of the store, so a cursor holds it from creation. */
821
+ sparseAccessorColumns(def: SparseComponentDef): AccessorColumns;
822
+ /**
823
+ * The DEV-only check a sparse cursor runs on every `at()` (ref.ts
824
+ * `createSparseCursor`): the access declaration (the same reason
825
+ * `cursorBinder` checks on `at()`), the liveness of the entity, and its
826
+ * membership. Production code never calls it, so a read of a non-member
827
+ * there gives whatever the column holds at that index, test with
828
+ * `hasSparse` first when the component can be absent, as `getSparseField`
829
+ * asks too.
830
+ */
831
+ sparseCursorCheck(def: SparseComponentDef, write: boolean): SparseCursorCheck;
753
832
  /** Serialize the sparse stores **and** relation side data to a self-contained
754
- * byte buffer the sparse half of a world snapshot (the dense half is the
755
- * SAB snapshot). Two framed sections: the sparse stores (`snapshot_sparse_-
756
- * stores` exclusive relation targets + multi membership ride here) followed
757
- * by the relation side data (`snapshotRelations` — multi forward target
833
+ * byte buffer, the sparse half of a world snapshot (the dense half is the
834
+ * SAB snapshot). Two framed sections: the sparse stores
835
+ * (`snapshotSparseStores`, exclusive relation targets and multi membership
836
+ * ride here) followed
837
+ * by the relation side data (`snapshotRelations`, multi forward target
758
838
  * sets, which live outside the sparse store). Both are written in canonical
759
839
  * entity-index order, so two worlds with identical contents inserted in
760
840
  * different orders snapshot byte-for-byte the same. The reverse index
761
- * is derived and never serialized `restoreSparse` rebuilds it. Pairs with
841
+ * is derived and never serialized, `restoreSparse` rebuilds it. Pairs with
762
842
  * `restoreSparse`.
763
843
  *
764
844
  * **Opt-in.** Throws `DETERMINISM_DISABLED` unless the
765
- * Store was constructed with `{ deterministic: true }` the canonical
845
+ * Store was constructed with `{ deterministic: true }`, the canonical
766
846
  * entity-index ordering is the determinism tax the flag gates. */
767
847
  snapshotSparse(): Uint8Array;
768
848
  /** Repopulate the sparse stores from `snapshotSparse` bytes, replacing all
769
849
  * current sparse data (full-equality round-trip of membership + data), then
770
850
  * rebuild every relation's derived side indices: multi forward sets from the
771
851
  * relation section, and the reverse index for both cardinalities (exclusive
772
- * from the just-restored sparse target field, multi from the rebuilt forward
852
+ * from the newly restored sparse target field, multi from the rebuilt forward
773
853
  * sets). The sparse components and relations must already be registered in
774
- * the same order restore carries data, not the registration (which is
854
+ * the same order, restore carries data, not the registration (which is
775
855
  * code). Throws `SparseRestoreError` if the snapshot's shape, field identity,
776
856
  * entity-index bounds, or frame length don't validate.
777
857
  *
778
858
  * **Opt-in.** Throws `DETERMINISM_DISABLED` unless the
779
- * Store was constructed with `{ deterministic: true }`; paired with
859
+ * Store was constructed with `{ deterministic: true }`. Paired with
780
860
  * `snapshotSparse`, which produces the canonical bytes restore consumes. */
781
861
  restoreSparse(bytes: Uint8Array): void;
862
+ /** The members and their values are the snapshot's now, so no record made
863
+ * before the restore names a write of theirs. */
864
+ private _resetSparseTicks;
782
865
  /**
783
866
  * Capture the full live world to one self-contained byte buffer that
784
- * `restoreInto` can mount back onto a live, ticking world ("rewind a running
785
- * world and keep ticking"). Three sections (see `resume.ts`): the dense SAB
786
- * column bytes (`snapshotColumnStore`), the sparse + relation bytes
787
- * (`snapshotSparse`), and the host-side bookkeeping the SAB omits — the world
788
- * tick, the entity recycle free-list (in live order; no byte source, and its
867
+ * `restore` can mount back onto a live, ticking world ("rewind a running
868
+ * world and keep ticking"). Three sections, which the snapshots plugin
869
+ * frames: the dense SAB
870
+ * column bytes (`columnStoreBytesView`), the sparse + relation bytes
871
+ * (`snapshotSparse`), and the host-side bookkeeping the SAB omits, the world
872
+ * tick, the entity recycle free-list (in live order, no byte source, and its
789
873
  * order is load-bearing for byte-identical resume), the alive count, and each
790
- * archetype's `length` / `enabledCount`.
874
+ * archetype's `length` and `enabledCount`.
791
875
  *
792
876
  * **Opt-in.** Throws `DETERMINISM_DISABLED` unless constructed with
793
- * `{ deterministic: true }` the sparse section rides the canonical-ordering
877
+ * `{ deterministic: true }`, the sparse section rides the canonical-ordering
794
878
  * surface and byte-identical resume is a determinism property. Pairs with
795
- * `restoreInto`.
879
+ * `restore`.
796
880
  *
797
- * **v1 scope.** Resources + events are NOT captured (resume requires
798
- * resource-free per-tick state; events are tick-cleared). Change-detection /
799
- * scheduler baselines (`changed()` queries) are likewise not captured — they
800
- * are scheduling artifacts, never folded into `stateHash`. Take the snapshot
801
- * at a tick boundary (between `update()`s). See the ADR. */
881
+ * Scope. It captures no resource and no event, because resume requires
882
+ * resource-free per-tick state and the tick clears events. It captures no
883
+ * change-detection baseline either, so a `changed()` query starts fresh.
884
+ * Those are scheduling artifacts, and never fold into `stateHash`. Take the
885
+ * snapshot at a tick boundary, between two `update()` calls. */
802
886
  snapshot(): Uint8Array;
803
887
  /**
804
888
  * Mount a `snapshot()` buffer onto this live world and leave it ready to keep
805
889
  * ticking. Fails closed on a malformed frame or a registration mismatch
806
- * BEFORE any live state is touched (the archetype/component graph is rebuilt
807
- * from code, not the snapshot same contract as `restoreSparse`). On
890
+ * before any live state is touched (the archetype and component graph is rebuilt
891
+ * from code, not the snapshot, same contract as `restoreSparse`). On
808
892
  * success the world's dense + sparse state, entity allocator, and tick are
809
893
  * exactly the captured world's.
810
894
  *
@@ -812,43 +896,23 @@ export declare class Store implements ObserverHost, QueryHost {
812
896
  * snapshot's exactly (prewarm so the archetype set is stable) and the same
813
897
  * entity-index capacity. **Opt-in:** throws `DETERMINISM_DISABLED`
814
898
  * unless `{ deterministic: true }`. See `snapshot()` for the v1 scope. */
815
- restoreInto(bytes: Uint8Array): void;
816
- /** Adopt a restored dense store (`SnapshotService.restoreInto`'s mount
899
+ restore(bytes: Uint8Array): void;
900
+ /** Adopt a restored dense store (the snapshot service's restore mount
817
901
  * step): swap the live backing, refresh every buffer-backed archetype's
818
902
  * views, recover the allocator high-water from the restored region, and
819
903
  * republish (the grow tail). Store-owned because it assigns
820
- * `_columnStore` the service never writes Store fields. */
904
+ * `_columnStore`, the service never writes Store fields. */
821
905
  private _mountRestoredDense;
822
- /** Rebuild each SAB-backed archetype's host-side `length` / `enabledCount` /
823
- * `_entityIds` after the dense backing was swapped in `restoreInto`. `length`
824
- * + the per-row entity-id back-reference come from a scan of the restored
825
- * entity-index region (which entity occupies which row); `enabledCount` comes
826
- * from the captured host-state (the partition boundary is positional only
827
- * it has no per-entity byte source). */
906
+ /** Rebuild each SAB-backed archetype's host-side `length`, `enabledCount`
907
+ * and `_entityIds` after `restore` swapped the dense backing. `length` and
908
+ * the per-row entity-id back-reference come from a scan of the restored
909
+ * entity-index region, which says what entity occupies what row.
910
+ * `enabledCount` comes from the captured host state, because the partition
911
+ * boundary is positional and has no per-entity byte source. */
828
912
  private _reconstructHostRows;
829
- registerRelation(opts?: RelationOptions): RelationDef;
830
- /** Number of registered relations. Visible to tests asserting the
831
- * no-transition invariant alongside `archetype_count`. */
832
- get relationCount(): number;
833
- addRelation(src: EntityID, def: RelationDef, tgt: EntityID): void;
834
- removeRelation(src: EntityID, def: RelationDef, tgt?: EntityID): void;
835
- targetOf(src: EntityID, def: RelationDef): EntityID | undefined;
836
- targetsOf(src: EntityID, def: RelationDef): EntityID[];
837
- sourcesOf(tgt: EntityID, def: RelationDef): EntityID[];
838
- hasRelation(src: EntityID, def: RelationDef): boolean;
839
- pairsOf(def: RelationDef): readonly (readonly [EntityID, EntityID])[];
840
- sourcesOfAny(tgt: EntityID): readonly (readonly [RelationDef, EntityID])[];
841
- relationBackingSparseId(def: RelationDef): SparseComponentID;
842
- /** Drive a `(*, T)` wildcard query (`Query.forEachRelatedTo`) — see
843
- * `RelationService.forEachRelationTargetMatch`. */
844
- _forEachRelationTargetMatch(target: EntityID, include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, sparseInclude: readonly SparseComponentID[], sparseExclude: readonly SparseComponentID[], includeDisabled: boolean, cb: (entityId: EntityID) => void): void;
845
- compactRelations(): number;
846
- ancestorsOf(src: EntityID, def: RelationDef): EntityID[];
847
- rootOf(src: EntityID, def: RelationDef): EntityID;
848
- cascadeOf(root: EntityID, def: RelationDef): EntityID[];
849
913
  /** Second query-match path: iterate entities matching a
850
914
  * dense mask **and** sparse-membership terms, invoking `cb` per entity.
851
- * Yields `EntityID`s, not archetype spans sparse members are scattered
915
+ * Yields `EntityID`s, not archetype spans, sparse members are scattered
852
916
  * across archetypes, so there is no SoA column to hand back. Driven by the
853
917
  * cheapest candidate set:
854
918
  *
@@ -861,36 +925,44 @@ export declare class Store implements ObserverHost, QueryHost {
861
925
  * store.
862
926
  * - **neither** → walk `denseArchetypes`' entity ids (dense-only fallback).
863
927
  *
864
- * Only reached via `Query.forEachEntity`; dense `forEach` never consults
928
+ * Only reached via `Query.forEachEntity`. Dense `forEach` never consults
865
929
  * the sparse stores, so dense-only queries are unaffected. */
866
- _forEachSparseMatch(include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, sparseInclude: readonly SparseComponentID[], sparseExclude: readonly SparseComponentID[], denseArchetypes: readonly Archetype[], cb: (entityId: EntityID) => void, includeDisabled: boolean): void;
930
+ forEachSparseMatch(include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, terms: QueryTerms, denseArchetypes: readonly Archetype[], cb: (entityId: EntityID) => void): void;
867
931
  /** Fourth query-match path: the matched set in hierarchy depth order
868
- * (parents before children) see `RelationService.forEachHierarchyMatch`. */
869
- _forEachHierarchyMatch(include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, sparseInclude: readonly SparseComponentID[], sparseExclude: readonly SparseComponentID[], denseArchetypes: readonly Archetype[], relation: RelationDef, maxDepth: number, includeDisabled: boolean, cb: (entityId: EntityID) => void): void;
932
+ * (parents before children), see `forEachHierarchyMatch` on the relation service. */
933
+ forEachHierarchyMatch(include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, terms: QueryTerms, denseArchetypes: readonly Archetype[], relation: RelationDef, maxDepth: number, cb: (entityId: EntityID) => void): void;
934
+ /** Third query-match path: the `(*, T)` wildcard, every source pointing at
935
+ * one target. `Query.forEachRelatedTo` is the seam the user reaches. */
936
+ forEachTargetMatch(target: EntityID, include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, terms: QueryTerms, cb: (entityId: EntityID) => void): void;
937
+ /** The sparse membership store a relation rides, which is how a query turns
938
+ * a `(R, *)` term into a sparse term. `api` names the query verb the user
939
+ * called, because `andRelation` and `notRelation` both land here. */
940
+ relationBackingSparseId(def: RelationDef, api: string): SparseComponentID;
941
+ private readonly _scratchTargetMask;
870
942
  addComponent(entityId: EntityID, def: ComponentDef<Record<string, never>>): void;
871
943
  addComponent<S extends ComponentSchema>(entityId: EntityID, def: ComponentDef<S>, values: FieldValues<S>): void;
872
- /** @internal cold tail of `addComponent`: the entity already holds `def`
944
+ /** @internal, cold tail of `addComponent`: the entity already holds `def`
873
945
  * (overwrite in place, no transition), or the (source, component) add edge
874
- * has not been cached yet (first time this pair is seen resolve, which
946
+ * has not been cached yet (first time this pair is seen, resolve, which
875
947
  * plants the edge, then re-enter the hot body). Split out so the edge-hit
876
- * path above stays a straight line with one holey-array probe; this runs at
948
+ * path above stays a straight line with one holey-array probe. This runs at
877
949
  * most once per (archetype, component) pair plus on in-place overwrites. */
878
950
  private _addComponentCold;
879
951
  /** Add multiple components in one transition (resolves final archetype, then moves once).
880
952
  *
881
953
  * Final-mask resolve, not graph walk. The previous implementation called
882
- * `archResolveAdd` once per entry, which threaded through every
883
- * intermediate archetype on the path and each unseen intermediate
954
+ * `_archResolveAdd` once per entry, which threaded through every
955
+ * intermediate archetype on the path, and each unseen intermediate
884
956
  * triggered a fresh `extendColumnStore` even though no entity ever lived
885
957
  * there. Computing the union mask up front and resolving once via
886
- * `archGetOrCreateFromMask` collapses N-1 intermediate-archetype
958
+ * `_archGetOrCreateFromMask` collapses N-1 intermediate-archetype
887
959
  * creations into zero for the batched case. The lazy
888
- * single-mask path remains the same; this just avoids feeding it
960
+ * single-mask path remains the same. This only avoids feeding it
889
961
  * archetypes the entity never visits.
890
962
  *
891
963
  * Composite-add edge cache. The final-mask resolve, unlike the
892
964
  * single-add `edges[]` walk, re-pays a per-call `mask.hash()`, `ArchetypeGraph.lookup`
893
- * (the Map-of-buckets + `equals` scan), and `getBatchTransitionMap` on
965
+ * (the Map-of-buckets + `equals` scan), and `transitionMapTo` on
894
966
  * every call. That is much slower than a cached edge walk, and a probe put the
895
967
  * cost on the two `Map.get` calls, and not on the hash. So a repeated (source,
896
968
  * added-set) add now resolves through `currentArch`'s composite-add cache: one
@@ -899,37 +971,37 @@ export declare class Store implements ObserverHost, QueryHost {
899
971
  * the final-mask path below (no intermediate planting) and plants the edge. */
900
972
  addComponents(entityId: EntityID, entries: readonly TemplateEntryData[]): void;
901
973
  /** Shared move+write tail of `addComponents`: place the entity into
902
- * the already-resolved `targetArch` a `moveEntityFrom` along the cached
974
+ * the already-resolved `targetArch`, a `moveEntityFrom` along the cached
903
975
  * `map` when it has a row, else a fresh append (the rowless empty-archetype
904
- * source ignores `map`) then write every entry's fields. Both the
976
+ * source ignores `map`), then write every entry's fields. Both the
905
977
  * composite-edge-cache hit and the final-mask cold path funnel through here so
906
978
  * the placement logic lives once. */
907
979
  private _addComponentsInto;
908
980
  removeComponent(entityId: EntityID, def: ComponentDef): void;
909
- /** @internal cold tail of `removeComponent`: the entity doesn't hold `def`
981
+ /** @internal, cold tail of `removeComponent`: the entity doesn't hold `def`
910
982
  * (no-op), or the (source, component) remove edge has not been cached yet.
911
- * Mirror of `_addComponentCold`; same depth-1 re-entry argument. */
983
+ * Mirror of `_addComponentCold`. Same depth-1 re-entry argument. */
912
984
  private _removeComponentCold;
913
985
  /** Remove multiple components in one transition (resolves final archetype, then moves once).
914
986
  *
915
987
  * Final-mask resolve, not graph walk. Same rationale as `addComponents`
916
- * above the previous per-step path threaded `archResolveRemove`
988
+ * above, the previous per-step path threaded `_archResolveRemove`
917
989
  * once per def, which materialised every intermediate archetype on the
918
990
  * removal path. Computing the difference mask up front and resolving
919
991
  * once avoids planting N-1 intermediates the entity never lives in. */
920
992
  removeComponents(entityId: EntityID, defs: ComponentDef[]): void;
921
- /** Total: a dead/stale `entityId` returns `false` rather
922
- * than throwing a "has" probe is exactly what callers reach for to avoid
993
+ /** Total: a dead or stale `entityId` returns `false` rather
994
+ * than throwing, a "has" probe is exactly what callers reach for to avoid
923
995
  * touching dead entities, so it must be safe to ask. */
924
996
  hasComponent(entityId: EntityID, def: ComponentHandle): boolean;
925
997
  /**
926
- * Bulk add a component to ALL entities in the given archetype.
998
+ * Bulk add a component to all entities in the given archetype.
927
999
  * Uses TypedArray.set() for O(columns) instead of O(N×columns).
928
1000
  * The archetype must not already contain this component.
929
1001
  */
930
1002
  batchAddComponent(src: ArchetypeID, def: ComponentDef, values?: Record<string, number>): void;
931
1003
  /**
932
- * Bulk remove a component from ALL entities in the given archetype.
1004
+ * Bulk remove a component from all entities in the given archetype.
933
1005
  * Uses TypedArray.set() for O(columns) instead of O(N×columns).
934
1006
  * The archetype must contain this component.
935
1007
  */
@@ -937,23 +1009,23 @@ export declare class Store implements ObserverHost, QueryHost {
937
1009
  getEntityArchetype(entityId: EntityID): Archetype;
938
1010
  getEntityRow(entityId: EntityID): number;
939
1011
  /**
940
- * The row `resolveEntity` placed the entity at the alloc-free second
1012
+ * The row `resolveEntity` placed the entity at, the alloc-free second
941
1013
  * return value of a resolve, read immediately after the call. Same out-param
942
- * pattern as `EntityAllocator.lastIndex` and `_moveResult`; returning a
1014
+ * pattern as `EntityAllocator.lastIndex` and `_moveResult`. Returning a
943
1015
  * `{ arch, row }` pair instead would allocate on every by-id read.
944
1016
  */
945
1017
  resolvedRow: number;
946
1018
  /**
947
- * (archetype, row) for a by-id access, derived from ONE index computation.
1019
+ * (archetype, row) for a by-id access, derived from one index computation.
948
1020
  *
949
1021
  * `getEntityArchetype` and `getEntityRow` are each one line, and every by-id
950
- * caller needs both so the pair cost two derivations of the same packed
1022
+ * caller needs both, so the pair cost two derivations of the same packed
951
1023
  * index and two call frames to read two elements of two parallel arrays
952
1024
  * addressed identically. This is that pair, fused: index once, publish the
953
1025
  * row on `resolvedRow`, return the archetype.
954
1026
  *
955
- * The two single-purpose accessors stay above tests reach for one half at a
956
- * time but no runtime path uses them in a pair any more.
1027
+ * The two single-purpose accessors stay above, tests reach for one half at a
1028
+ * time, but no runtime path uses them in a pair any more.
957
1029
  */
958
1030
  resolveEntity(entityId: EntityID): Archetype;
959
1031
  /**
@@ -965,52 +1037,34 @@ export declare class Store implements ObserverHost, QueryHost {
965
1037
  * `at()`: a mutable cursor bumps the component's change tick on every
966
1038
  * repoint (matching `ctx.ref`), a read-only one never does.
967
1039
  *
968
- * The access check lives HERE, in the binder, and not only at the call that
1040
+ * The access check lives here, in the binder, and not only at the call that
969
1041
  * creates the cursor. A cursor is made one time and then kept, so it outlives
970
- * the span that made it: a cursor made at host level (where no system is
971
- * active, and the check at creation therefore passes) writes an undeclared
972
- * component when a system body uses it, and a `ctx.cursor` that a system
973
- * stores in an outer variable does the same in the NEXT system. Both slip
1042
+ * the span that made it. A cursor made at host level writes an undeclared
1043
+ * component when a system body uses it, because no system is active at
1044
+ * creation and the check there passes. A `ctx.cursor` that a system stores in
1045
+ * an outer variable does the same in the next system. Both slip
974
1046
  * past a check that only runs at creation. `at()` is the point of use, so the
975
1047
  * check belongs on it. The creation-site check stays as well: it fails early,
976
1048
  * and its stack names the line that made the cursor.
977
1049
  */
978
1050
  cursorBinder(def: ComponentHandle, stampTick: boolean): CursorBinder;
979
- /** A component's field names in schema order the cursor prototype key and
1051
+ /** A component's field names in schema order, the cursor prototype key and
980
1052
  * ordinal source (ref.ts). One array per component, owned by its meta. */
981
1053
  componentFieldNames(def: ComponentHandle): readonly string[];
982
1054
  /**
983
- * Find all archetypes matching the given masks.
984
- * Uses the inverted componentIndex to start from the component with the
985
- * fewest archetypes, minimizing the number of superset checks.
1055
+ * Find all archetypes matching the given masks. Delegates to
1056
+ * `QueryRegistry`, which starts the superset scan from the smallest
1057
+ * component bucket. Query mint path only.
986
1058
  */
987
1059
  getMatchingArchetypes(required: BitSet, excluded?: BitSet, anyOf?: BitSet): readonly Archetype[];
988
1060
  /**
989
- * Register a live query. Returns a mutable Archetype[] that this Store will
990
- * push newly-created matching archetypes into, keeping the query always up-to-date.
1061
+ * Register a live query. Returns a mutable Archetype[] that the registry
1062
+ * pushes newly-created matching archetypes into, keeping the query always
1063
+ * up-to-date.
991
1064
  */
992
1065
  registerQuery(include: BitSet, exclude?: BitSet, anyOf?: BitSet): Archetype[];
993
1066
  updateQueryRef(result: Archetype[], query: Query<any>): void;
994
1067
  get archetypeCount(): number;
995
- registerEvent<S extends EventShape<S>>(fields: readonly (keyof S & string)[]): EventDef<S>;
996
- emitEvent(def: EventDef<any>, values: Record<string, number>): void;
997
- emitSignal(def: EventDef<EmptyEventSchema>): void;
998
- getEventReader<S extends EventShape<S>>(def: EventDef<S>): EventReader<S>;
999
- clearEvents(): void;
1000
- /** `DEV`-only mid-update emit detection — see
1001
- * `EventRegistry.devBufferedEventCount`. */
1002
- _devBufferedEventCount(): number;
1003
- registerEventByKey<S extends EventShape<S>>(key: symbol, fields: readonly (keyof S & string)[]): EventDef<S>;
1004
- getEventDefByKey(key: symbol): EventDef<any>;
1005
- hasEventKey(key: symbol): boolean;
1006
- private readonly resources;
1007
- registerResource(key: symbol, value: unknown): void;
1008
- getResource(key: symbol): unknown;
1009
- setResource(key: symbol, value: unknown): void;
1010
- /** Fails closed on a missing key; the present → absent → present
1011
- * lifecycle — see `ResourceRegistry.remove`. */
1012
- removeResource(key: symbol): void;
1013
- hasResource(key: symbol): boolean;
1068
+ private readonly _resources;
1014
1069
  }
1015
- export {};
1016
1070
  //# sourceMappingURL=store.d.ts.map