@oasys/oecs 0.3.1 → 0.5.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 (324) hide show
  1. package/CHANGELOG.md +553 -0
  2. package/README.md +207 -267
  3. package/dist/core/ecs/access_check.d.cts +77 -0
  4. package/dist/core/ecs/access_check.d.ts +77 -0
  5. package/dist/core/ecs/access_check.d.ts.map +1 -0
  6. package/dist/core/ecs/archetype.d.cts +486 -0
  7. package/dist/core/ecs/archetype.d.ts +486 -0
  8. package/dist/core/ecs/archetype.d.ts.map +1 -0
  9. package/dist/core/ecs/archetype_graph.d.cts +96 -0
  10. package/dist/core/ecs/archetype_graph.d.ts +96 -0
  11. package/dist/core/ecs/archetype_graph.d.ts.map +1 -0
  12. package/dist/core/ecs/builtin_relations.d.cts +43 -0
  13. package/dist/core/ecs/builtin_relations.d.ts +43 -0
  14. package/dist/core/ecs/builtin_relations.d.ts.map +1 -0
  15. package/dist/core/ecs/command_log.d.cts +116 -0
  16. package/dist/core/ecs/command_log.d.ts +116 -0
  17. package/dist/core/ecs/command_log.d.ts.map +1 -0
  18. package/dist/core/ecs/component.d.cts +176 -0
  19. package/dist/core/ecs/component.d.ts +176 -0
  20. package/dist/core/ecs/component.d.ts.map +1 -0
  21. package/dist/core/ecs/compute_backend.d.cts +37 -0
  22. package/dist/core/ecs/compute_backend.d.ts +37 -0
  23. package/dist/core/ecs/compute_backend.d.ts.map +1 -0
  24. package/dist/core/ecs/debug_names.d.cts +7 -0
  25. package/dist/core/ecs/debug_names.d.ts +7 -0
  26. package/dist/core/ecs/debug_names.d.ts.map +1 -0
  27. package/dist/core/ecs/deferred_commands.d.cts +55 -0
  28. package/dist/core/ecs/deferred_commands.d.ts +55 -0
  29. package/dist/core/ecs/deferred_commands.d.ts.map +1 -0
  30. package/dist/core/ecs/dispatch_trace.d.cts +93 -0
  31. package/dist/core/ecs/dispatch_trace.d.ts +93 -0
  32. package/dist/core/ecs/dispatch_trace.d.ts.map +1 -0
  33. package/dist/core/ecs/ecs.d.cts +562 -0
  34. package/dist/core/ecs/ecs.d.ts +562 -0
  35. package/dist/core/ecs/ecs.d.ts.map +1 -0
  36. package/dist/core/ecs/ecs_memory.d.cts +179 -0
  37. package/dist/core/ecs/ecs_memory.d.ts +179 -0
  38. package/dist/core/ecs/ecs_memory.d.ts.map +1 -0
  39. package/dist/core/ecs/entity.d.cts +34 -0
  40. package/dist/core/ecs/entity.d.ts +34 -0
  41. package/dist/core/ecs/entity.d.ts.map +1 -0
  42. package/dist/core/ecs/entity_allocator.d.cts +59 -0
  43. package/dist/core/ecs/entity_allocator.d.ts +59 -0
  44. package/dist/core/ecs/entity_allocator.d.ts.map +1 -0
  45. package/dist/core/ecs/event.d.cts +79 -0
  46. package/dist/core/ecs/event.d.ts +79 -0
  47. package/dist/core/ecs/event.d.ts.map +1 -0
  48. package/dist/core/ecs/event_registry.d.cts +22 -0
  49. package/dist/core/ecs/event_registry.d.ts +22 -0
  50. package/dist/core/ecs/event_registry.d.ts.map +1 -0
  51. package/dist/core/ecs/facades.d.cts +158 -0
  52. package/dist/core/ecs/facades.d.ts +158 -0
  53. package/dist/core/ecs/facades.d.ts.map +1 -0
  54. package/dist/core/ecs/frame_stepper.d.cts +58 -0
  55. package/dist/core/ecs/frame_stepper.d.ts +58 -0
  56. package/dist/core/ecs/frame_stepper.d.ts.map +1 -0
  57. package/dist/core/ecs/frame_trace.d.cts +133 -0
  58. package/dist/core/ecs/frame_trace.d.ts +133 -0
  59. package/dist/core/ecs/frame_trace.d.ts.map +1 -0
  60. package/dist/core/ecs/host_commands.d.cts +274 -0
  61. package/dist/core/ecs/host_commands.d.ts +274 -0
  62. package/dist/core/ecs/host_commands.d.ts.map +1 -0
  63. package/dist/core/ecs/index.d.cts +46 -0
  64. package/dist/core/ecs/index.d.ts +46 -0
  65. package/dist/core/ecs/index.d.ts.map +1 -0
  66. package/dist/core/ecs/observer.d.cts +173 -0
  67. package/dist/core/ecs/observer.d.ts +173 -0
  68. package/dist/core/ecs/observer.d.ts.map +1 -0
  69. package/dist/core/ecs/query.d.cts +718 -0
  70. package/dist/core/ecs/query.d.ts +718 -0
  71. package/dist/core/ecs/query.d.ts.map +1 -0
  72. package/dist/core/ecs/ref.d.cts +31 -0
  73. package/dist/core/ecs/ref.d.ts +31 -0
  74. package/dist/core/ecs/ref.d.ts.map +1 -0
  75. package/dist/core/ecs/relation.d.cts +243 -0
  76. package/dist/core/ecs/relation.d.ts +243 -0
  77. package/dist/core/ecs/relation.d.ts.map +1 -0
  78. package/dist/core/ecs/relation_service.d.cts +235 -0
  79. package/dist/core/ecs/relation_service.d.ts +235 -0
  80. package/dist/core/ecs/relation_service.d.ts.map +1 -0
  81. package/dist/core/ecs/resource.d.cts +53 -0
  82. package/dist/core/ecs/resource.d.ts +53 -0
  83. package/dist/core/ecs/resource.d.ts.map +1 -0
  84. package/dist/core/ecs/resource_registry.d.cts +20 -0
  85. package/dist/core/ecs/resource_registry.d.ts +20 -0
  86. package/dist/core/ecs/resource_registry.d.ts.map +1 -0
  87. package/dist/core/ecs/resume.d.cts +85 -0
  88. package/dist/core/ecs/resume.d.ts +85 -0
  89. package/dist/core/ecs/resume.d.ts.map +1 -0
  90. package/dist/core/ecs/run_condition.d.cts +81 -0
  91. package/dist/core/ecs/run_condition.d.ts +81 -0
  92. package/dist/core/ecs/run_condition.d.ts.map +1 -0
  93. package/dist/core/ecs/schedule.d.cts +142 -0
  94. package/dist/core/ecs/schedule.d.ts +142 -0
  95. package/dist/core/ecs/schedule.d.ts.map +1 -0
  96. package/dist/core/ecs/snapshot_service.d.cts +94 -0
  97. package/dist/core/ecs/snapshot_service.d.ts +94 -0
  98. package/dist/core/ecs/snapshot_service.d.ts.map +1 -0
  99. package/dist/core/ecs/sparse_store.d.cts +110 -0
  100. package/dist/core/ecs/sparse_store.d.ts +110 -0
  101. package/dist/core/ecs/sparse_store.d.ts.map +1 -0
  102. package/dist/core/ecs/store.d.cts +950 -0
  103. package/dist/core/ecs/store.d.ts +950 -0
  104. package/dist/core/ecs/store.d.ts.map +1 -0
  105. package/dist/core/ecs/store_layout_listener.d.cts +23 -0
  106. package/dist/core/ecs/store_layout_listener.d.ts +23 -0
  107. package/dist/core/ecs/store_layout_listener.d.ts.map +1 -0
  108. package/dist/core/ecs/system.d.cts +250 -0
  109. package/dist/core/ecs/system.d.ts +250 -0
  110. package/dist/core/ecs/system.d.ts.map +1 -0
  111. package/dist/core/ecs/utils/arrays.d.cts +7 -0
  112. package/dist/{utils → core/ecs/utils}/arrays.d.ts +2 -1
  113. package/dist/core/ecs/utils/arrays.d.ts.map +1 -0
  114. package/dist/core/ecs/utils/constants.d.ts +12 -0
  115. package/dist/core/ecs/utils/constants.d.ts.map +1 -0
  116. package/dist/core/ecs/utils/error.d.cts +61 -0
  117. package/dist/core/ecs/utils/error.d.ts +61 -0
  118. package/dist/core/ecs/utils/error.d.ts.map +1 -0
  119. package/dist/core/store/action_ring.d.cts +136 -0
  120. package/dist/core/store/action_ring.d.ts +136 -0
  121. package/dist/core/store/action_ring.d.ts.map +1 -0
  122. package/dist/core/store/allocator.d.cts +238 -0
  123. package/dist/core/store/allocator.d.ts +238 -0
  124. package/dist/core/store/allocator.d.ts.map +1 -0
  125. package/dist/core/store/buffer_backed_column.d.cts +69 -0
  126. package/dist/core/store/buffer_backed_column.d.ts +69 -0
  127. package/dist/core/store/buffer_backed_column.d.ts.map +1 -0
  128. package/dist/core/store/column_store.d.cts +273 -0
  129. package/dist/core/store/column_store.d.ts +273 -0
  130. package/dist/core/store/column_store.d.ts.map +1 -0
  131. package/dist/core/store/command_dispatch.d.cts +52 -0
  132. package/dist/core/store/command_dispatch.d.ts +52 -0
  133. package/dist/core/store/command_dispatch.d.ts.map +1 -0
  134. package/dist/core/store/command_ring.d.cts +107 -0
  135. package/dist/core/store/command_ring.d.ts +107 -0
  136. package/dist/core/store/command_ring.d.ts.map +1 -0
  137. package/dist/core/store/descriptor.d.cts +80 -0
  138. package/dist/core/store/descriptor.d.ts +80 -0
  139. package/dist/core/store/descriptor.d.ts.map +1 -0
  140. package/dist/core/store/entity_index.d.cts +108 -0
  141. package/dist/core/store/entity_index.d.ts +108 -0
  142. package/dist/core/store/entity_index.d.ts.map +1 -0
  143. package/dist/core/store/event_ring.d.cts +95 -0
  144. package/dist/core/store/event_ring.d.ts +95 -0
  145. package/dist/core/store/event_ring.d.ts.map +1 -0
  146. package/dist/core/store/extend.d.cts +39 -0
  147. package/dist/core/store/extend.d.ts +39 -0
  148. package/dist/core/store/extend.d.ts.map +1 -0
  149. package/dist/core/store/grow.d.cts +34 -0
  150. package/dist/core/store/grow.d.ts +34 -0
  151. package/dist/core/store/grow.d.ts.map +1 -0
  152. package/dist/core/store/header.d.cts +64 -0
  153. package/dist/core/store/header.d.ts +64 -0
  154. package/dist/core/store/header.d.ts.map +1 -0
  155. package/dist/core/store/index.d.cts +17 -0
  156. package/dist/core/store/index.d.ts +17 -0
  157. package/dist/core/store/index.d.ts.map +1 -0
  158. package/dist/core/store/layout_ops.d.cts +152 -0
  159. package/dist/core/store/layout_ops.d.ts +152 -0
  160. package/dist/core/store/layout_ops.d.ts.map +1 -0
  161. package/dist/core/store/region_table.d.cts +74 -0
  162. package/dist/core/store/region_table.d.ts +74 -0
  163. package/dist/core/store/region_table.d.ts.map +1 -0
  164. package/dist/core/store/snapshot.d.cts +43 -0
  165. package/dist/core/store/snapshot.d.ts +43 -0
  166. package/dist/core/store/snapshot.d.ts.map +1 -0
  167. package/dist/core/store/state_hash.d.cts +38 -0
  168. package/dist/core/store/state_hash.d.ts +38 -0
  169. package/dist/core/store/state_hash.d.ts.map +1 -0
  170. package/dist/core/store/store_regions.d.cts +38 -0
  171. package/dist/core/store/store_regions.d.ts +38 -0
  172. package/dist/core/store/store_regions.d.ts.map +1 -0
  173. package/dist/core/store/vendored_abi/abi.d.cts +43 -0
  174. package/dist/core/store/vendored_abi/abi.d.ts +43 -0
  175. package/dist/core/store/vendored_abi/abi.d.ts.map +1 -0
  176. package/dist/dev_flag.d.cts +17 -0
  177. package/dist/dev_flag.d.ts +17 -0
  178. package/dist/dev_flag.d.ts.map +1 -0
  179. package/dist/extensions/editor/editor.d.cts +170 -0
  180. package/dist/extensions/editor/editor.d.ts +170 -0
  181. package/dist/extensions/editor/editor.d.ts.map +1 -0
  182. package/dist/extensions/editor/field_handle.d.cts +40 -0
  183. package/dist/extensions/editor/field_handle.d.ts +40 -0
  184. package/dist/extensions/editor/field_handle.d.ts.map +1 -0
  185. package/dist/extensions/editor/index.cjs +1 -0
  186. package/dist/extensions/editor/index.d.cts +21 -0
  187. package/dist/extensions/editor/index.d.ts +21 -0
  188. package/dist/extensions/editor/index.d.ts.map +1 -0
  189. package/dist/extensions/editor/index.js +237 -0
  190. package/dist/extensions/reactive/ecs_sync.d.cts +214 -0
  191. package/dist/extensions/reactive/ecs_sync.d.ts +214 -0
  192. package/dist/extensions/reactive/ecs_sync.d.ts.map +1 -0
  193. package/dist/extensions/reactive/index.cjs +1 -0
  194. package/dist/extensions/reactive/index.d.cts +23 -0
  195. package/dist/extensions/reactive/index.d.ts +23 -0
  196. package/dist/extensions/reactive/index.d.ts.map +1 -0
  197. package/dist/extensions/reactive/index.js +215 -0
  198. package/dist/extensions/solid/index.cjs +1 -0
  199. package/dist/extensions/solid/index.d.cts +6 -0
  200. package/dist/extensions/solid/index.d.ts +6 -0
  201. package/dist/extensions/solid/index.d.ts.map +1 -0
  202. package/dist/extensions/solid/index.js +36 -0
  203. package/dist/extensions/solid/kernel_solid.d.cts +45 -0
  204. package/dist/extensions/solid/kernel_solid.d.ts +45 -0
  205. package/dist/extensions/solid/kernel_solid.d.ts.map +1 -0
  206. package/dist/host_commands-BF8QMi3c.cjs +1 -0
  207. package/dist/host_commands-i4cAeyL5.js +2318 -0
  208. package/dist/index.cjs +1 -1
  209. package/dist/index.d.cts +66 -0
  210. package/dist/index.d.ts +65 -12
  211. package/dist/index.d.ts.map +1 -1
  212. package/dist/index.js +7080 -1289
  213. package/dist/internal.cjs +2 -0
  214. package/dist/internal.d.cts +20 -0
  215. package/dist/internal.d.ts +20 -0
  216. package/dist/internal.d.ts.map +1 -0
  217. package/dist/internal.js +141 -0
  218. package/dist/interop-CT-REx0W.cjs +1 -0
  219. package/dist/interop-CcY6ASQc.js +18 -0
  220. package/dist/kernel-DgyrLFjW.js +227 -0
  221. package/dist/kernel-yWV3XnAb.cjs +1 -0
  222. package/dist/primitives.cjs +1 -0
  223. package/dist/primitives.d.cts +18 -0
  224. package/dist/primitives.d.ts +18 -0
  225. package/dist/primitives.d.ts.map +1 -0
  226. package/dist/primitives.js +45 -0
  227. package/dist/reactive/array.d.cts +24 -0
  228. package/dist/reactive/array.d.ts +24 -0
  229. package/dist/reactive/array.d.ts.map +1 -0
  230. package/dist/reactive/index.cjs +1 -0
  231. package/dist/reactive/index.d.cts +11 -0
  232. package/dist/reactive/index.d.ts +11 -0
  233. package/dist/reactive/index.d.ts.map +1 -0
  234. package/dist/reactive/index.js +18 -0
  235. package/dist/reactive/interop.d.cts +19 -0
  236. package/dist/reactive/interop.d.ts +19 -0
  237. package/dist/reactive/interop.d.ts.map +1 -0
  238. package/dist/reactive/kernel.d.cts +73 -0
  239. package/dist/reactive/kernel.d.ts +73 -0
  240. package/dist/reactive/kernel.d.ts.map +1 -0
  241. package/dist/reactive/map.d.cts +16 -0
  242. package/dist/reactive/map.d.ts +16 -0
  243. package/dist/reactive/map.d.ts.map +1 -0
  244. package/dist/reactive/shallow.d.cts +11 -0
  245. package/dist/reactive/shallow.d.ts +11 -0
  246. package/dist/reactive/shallow.d.ts.map +1 -0
  247. package/dist/reactive/struct.d.cts +10 -0
  248. package/dist/reactive/struct.d.ts +10 -0
  249. package/dist/reactive/struct.d.ts.map +1 -0
  250. package/dist/shallow-DQ8aG51_.cjs +1 -0
  251. package/dist/shallow-DfnAm9vG.js +150 -0
  252. package/dist/shared-BU1Cd40h.js +103 -0
  253. package/dist/shared-BymrGTyR.cjs +1 -0
  254. package/dist/shared.cjs +1 -0
  255. package/dist/shared.d.cts +22 -0
  256. package/dist/shared.d.ts +22 -0
  257. package/dist/shared.d.ts.map +1 -0
  258. package/dist/shared.js +7 -0
  259. package/dist/topological_sort-BDvEyb9W.cjs +1 -0
  260. package/dist/topological_sort-DK6EjpWa.js +135 -0
  261. package/dist/type_primitives/assertions.d.cts +32 -0
  262. package/dist/type_primitives/assertions.d.ts +25 -9
  263. package/dist/type_primitives/assertions.d.ts.map +1 -1
  264. package/dist/type_primitives/binary_heap/binary_heap.d.cts +37 -0
  265. package/dist/type_primitives/binary_heap/binary_heap.d.ts +6 -2
  266. package/dist/type_primitives/binary_heap/binary_heap.d.ts.map +1 -1
  267. package/dist/type_primitives/bitset/bitset.d.cts +54 -0
  268. package/dist/type_primitives/bitset/bitset.d.ts +16 -4
  269. package/dist/type_primitives/bitset/bitset.d.ts.map +1 -1
  270. package/dist/type_primitives/brand.d.cts +23 -0
  271. package/dist/type_primitives/brand.d.ts +6 -1
  272. package/dist/type_primitives/brand.d.ts.map +1 -1
  273. package/dist/type_primitives/error.d.cts +16 -0
  274. package/dist/type_primitives/error.d.ts +8 -3
  275. package/dist/type_primitives/error.d.ts.map +1 -1
  276. package/dist/type_primitives/index.d.cts +13 -0
  277. package/dist/type_primitives/index.d.ts +12 -9
  278. package/dist/type_primitives/index.d.ts.map +1 -1
  279. package/dist/type_primitives/sparse_map/sparse_map.d.cts +30 -0
  280. package/dist/type_primitives/sparse_map/sparse_map.d.ts +7 -3
  281. package/dist/type_primitives/sparse_map/sparse_map.d.ts.map +1 -1
  282. package/dist/type_primitives/sparse_set/sparse_set.d.cts +28 -0
  283. package/dist/type_primitives/sparse_set/sparse_set.d.ts +4 -0
  284. package/dist/type_primitives/sparse_set/sparse_set.d.ts.map +1 -1
  285. package/dist/type_primitives/topological_sort/topological_sort.d.cts +29 -0
  286. package/dist/type_primitives/topological_sort/topological_sort.d.ts +7 -3
  287. package/dist/type_primitives/topological_sort/topological_sort.d.ts.map +1 -1
  288. package/dist/type_primitives/typed_arrays/typed_arrays.d.cts +127 -0
  289. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +53 -16
  290. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -1
  291. package/dist/typed_arrays-BcXcnzB4.js +258 -0
  292. package/dist/typed_arrays-D3NmwtCG.cjs +1 -0
  293. package/dist/utils/error.d.cts +6 -0
  294. package/dist/utils/error.d.ts +2 -20
  295. package/dist/utils/error.d.ts.map +1 -1
  296. package/dist/version.d.cts +10 -0
  297. package/dist/version.d.ts +10 -0
  298. package/dist/version.d.ts.map +1 -0
  299. package/package.json +115 -5
  300. package/dist/archetype.d.ts +0 -108
  301. package/dist/archetype.d.ts.map +0 -1
  302. package/dist/component.d.ts +0 -45
  303. package/dist/component.d.ts.map +0 -1
  304. package/dist/ecs.d.ts +0 -104
  305. package/dist/ecs.d.ts.map +0 -1
  306. package/dist/entity.d.ts +0 -11
  307. package/dist/entity.d.ts.map +0 -1
  308. package/dist/event.d.ts +0 -30
  309. package/dist/event.d.ts.map +0 -1
  310. package/dist/query.d.ts +0 -94
  311. package/dist/query.d.ts.map +0 -1
  312. package/dist/ref.d.ts +0 -23
  313. package/dist/ref.d.ts.map +0 -1
  314. package/dist/resource.d.ts +0 -23
  315. package/dist/resource.d.ts.map +0 -1
  316. package/dist/schedule.d.ts +0 -45
  317. package/dist/schedule.d.ts.map +0 -1
  318. package/dist/store.d.ts +0 -118
  319. package/dist/store.d.ts.map +0 -1
  320. package/dist/system.d.ts +0 -16
  321. package/dist/system.d.ts.map +0 -1
  322. package/dist/utils/arrays.d.ts.map +0 -1
  323. package/dist/utils/constants.d.ts.map +0 -1
  324. /package/dist/{utils/constants.d.ts → core/ecs/utils/constants.d.cts} +0 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Command dispatch — the generic drain surface a consumer registers against
3
+ * (#624, game-agnostic ECS / epic #616).
4
+ *
5
+ * The command ring (`command_ring.ts`) carries opaque `(opCode, payload)`
6
+ * slots; the engine never interprets a code. This module is the thin,
7
+ * game-free glue that lets a consumer bind a payload codec + typed handler to
8
+ * each opcode and drain the ring in one call. The opcode enum and the codecs
9
+ * themselves stay consumer-owned — for our game they live in
10
+ * `@internal/sim`'s `command_payloads.ts` (`COMMAND_OP`, `SpawnUnitFields`,
11
+ * `encode/decode_spawn_unit_payload`); the engine knows none of those names.
12
+ *
13
+ * Usage:
14
+ *
15
+ * const dispatcher = new CommandDispatcher()
16
+ * .on(COMMAND_OP.spawn_unit, spawn_unit_codec, (fields) => spawn(fields));
17
+ * dispatcher.drain(view, ringOff); // runs after wasm.tick() returns
18
+ *
19
+ * Unregistered opcodes are skipped (the same forward-compatible stance as the
20
+ * raw `drainCommandRing` handler that ignores codes it doesn't know).
21
+ */
22
+ /** Decode (and, symmetrically, encode) the 15-byte payload region of a
23
+ * command slot into a typed value. A consumer supplies one per opcode it
24
+ * cares about; the engine only ever calls `decode` during a drain, but the
25
+ * `encode` half keeps the codec a single round-trippable unit (and is what
26
+ * test/host producers use to push). */
27
+ export interface PayloadCodec<T> {
28
+ /** Encode `value` into a fresh `COMMAND_RING_SLOT_BYTES - 1` (15) byte
29
+ * payload, ready for `pushCommand(view, off, op, payload)`. */
30
+ encode(value: T): Uint8Array;
31
+ /** Decode a 15-byte payload back into the typed value. */
32
+ decode(payload: Uint8Array): T;
33
+ }
34
+ /**
35
+ * Registry mapping command opcodes to a payload codec + handler. Generic over
36
+ * the consumer's opcodes — the engine ships the mechanism; the game supplies
37
+ * the codes and codecs.
38
+ */
39
+ export declare class CommandDispatcher {
40
+ private readonly bindings;
41
+ /** Register `handler` for `opCode`, decoding each slot with `codec`.
42
+ * Re-registering an opcode replaces its binding. `opCode` must be a u8 in
43
+ * `[1, 255]` — `0` is the reserved empty-slot marker and can never carry a
44
+ * command. Returns `this` for chaining. */
45
+ on<T>(opCode: number, codec: PayloadCodec<T>, handler: (value: T) => void): this;
46
+ /** Drain every pending command, decoding + dispatching each to its
47
+ * registered handler. Commands with no registered opcode are skipped.
48
+ * Returns the number of slots drained (including skipped ones — the ring is
49
+ * advanced regardless, matching `drainCommandRing`). */
50
+ drain(view: DataView, ringOff: number): number;
51
+ }
52
+ //# sourceMappingURL=command_dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command_dispatch.d.ts","sourceRoot":"","sources":["../../../src/core/store/command_dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH;;;;uCAIuC;AACvC,MAAM,WAAW,YAAY,CAAC,CAAC;IAC9B;mEAC+D;IAC/D,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC;IAC7B,0DAA0D;IAC1D,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,CAAC;CAC/B;AAWD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAE7D;;;+CAG2C;IAC3C,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,IAAI;IAgBhF;;;4DAGwD;IACxD,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAO9C"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Command ring — WASM-side producer / TS-side consumer SPSC ring buffer
3
+ * for structural-change intents emitted during `sim.tick()`. Plan §7.5
4
+ * (`docs/ideas/buffer-wasm-sim-plan-2026-05-14T1600.md`).
5
+ *
6
+ * Layout:
7
+ *
8
+ * [ write_head: u32 ] slot 0..N-1, monotonic (NOT slot-modulo)
9
+ * [ read_head: u32 ] slot 0..N-1, monotonic
10
+ * [ capacity: u32 ] slot count, power-of-two
11
+ * [ overflow: u32 ] 0 = OK; 1 = WASM exhausted the ring this tick
12
+ * [ slot 0: 16 B ] opCode: u8, payload: [15]u8
13
+ * [ slot 1: 16 B ] ...
14
+ * ...
15
+ *
16
+ * SPSC contract (Phase 4 scope):
17
+ * - Producer: WASM `sim.tick()`. Pushes 0..N commands during one tick;
18
+ * bumps `write_head` after each.
19
+ * - Consumer: TS host, immediately after `wasm.tick()` returns. Drains
20
+ * 0..N pending commands; bumps `read_head` after each.
21
+ * - The two never run concurrently in Phase 4 (single host thread
22
+ * orchestrates both). Phase 5's worker offload promotes the head
23
+ * bumps to `Atomics.store`; that's an additive change without
24
+ * altering the layout.
25
+ *
26
+ * Overflow:
27
+ * - If WASM would write a slot when (`write_head - read_head == capacity`),
28
+ * it sets `overflow = 1` and drops the command. TS treats overflow as
29
+ * a hard error in dev builds; production logs and continues (a command
30
+ * might be lost rather than crash the host).
31
+ *
32
+ * Slot format (plan §7.5):
33
+ * byte 0: opCode (u8). 0 is reserved as the empty-slot marker
34
+ * (`COMMAND_OP_EMPTY`); all other codes are consumer-defined.
35
+ * The engine never interprets a code — it drains
36
+ * `(opCode, payload)` and hands them to the attached
37
+ * consumer, which owns the opcode enum + payload codecs (the
38
+ * game's live in `@internal/sim`'s `command_payloads.ts`).
39
+ * bytes 1..15: payload, op-specific. Multi-byte fields may be
40
+ * unaligned within the payload; readers must use byte-
41
+ * oriented helpers (DataView in TS, `mem.readInt` in Zig).
42
+ *
43
+ * The ring lives BEFORE the layout-descriptor region in the SAB (right
44
+ * after the 32-byte header) so its offset is stable across descriptor /
45
+ * column-region growth. The host writes `header.command_ring_off` to
46
+ * point at it during `createColumnStore`; absent ring is signalled by
47
+ * `command_ring_off === 0`.
48
+ */
49
+ /** Total bytes for the ring header. */
50
+ export declare const COMMAND_RING_HEADER_BYTES = 16;
51
+ /** Fixed slot size — 1-byte opCode + 15-byte payload. */
52
+ export declare const COMMAND_RING_SLOT_BYTES = 16;
53
+ /** Default ring capacity in slots. 256 × 16 B = 4 KiB of ring data plus
54
+ * 16 B header. Sized for the worst-case burst (peak spawn intents per
55
+ * tick) × small safety margin. Tune up if a Phase 4 burst pushes past it
56
+ * in the bench harness. */
57
+ export declare const COMMAND_RING_DEFAULT_CAPACITY_SLOTS = 256;
58
+ /** Byte offsets within the ring header. Mirrored on the Zig side in
59
+ * `packages/sim/src/command_ring.zig` — keep in sync. */
60
+ export declare const COMMAND_RING_HEADER_OFFSETS: {
61
+ readonly write_head: 0;
62
+ readonly read_head: 4;
63
+ readonly capacity_slots: 8;
64
+ readonly overflow_flag: 12;
65
+ };
66
+ /** Op-code `0` is reserved across the SAB layer as the empty-slot marker
67
+ * so a zero-initialised SAB doesn't appear to hold a valid command (mirror
68
+ * of `EVENT_OP_EMPTY`). All non-zero codes are opaque to the engine —
69
+ * the attached consumer owns the opcode enum + payload codecs (the game's
70
+ * `COMMAND_OP` + `SpawnUnitFields` live in `@internal/sim`). */
71
+ export declare const COMMAND_OP_EMPTY = 0;
72
+ /** Total bytes the ring occupies for `capacity_slots` slots. */
73
+ export declare function commandRingBytes(capacitySlots: number): number;
74
+ export declare class CommandRingError extends Error {
75
+ constructor(message: string);
76
+ }
77
+ /** Initialise the ring header at `ringOff` in the SAB. Zeroes
78
+ * `write_head`, `read_head`, and `overflow_flag`; sets `capacity_slots`.
79
+ * Slot bytes are left as-is (callers normally allocate the ring on a
80
+ * fresh, zero-initialised SAB). */
81
+ export declare function initCommandRing(view: DataView, ringOff: number, capacitySlots: number): void;
82
+ /** Read live ring-header field. */
83
+ export declare function ringWriteHead(view: DataView, ringOff: number): number;
84
+ export declare function ringReadHead(view: DataView, ringOff: number): number;
85
+ export declare function ringCapacitySlots(view: DataView, ringOff: number): number;
86
+ export declare function ringOverflow(view: DataView, ringOff: number): boolean;
87
+ /** Pending command count = `(write_head - read_head) mod 2^32`. The
88
+ * `>>> 0` keeps the result a u32 in the wrap-around case (rings live for
89
+ * the host's lifetime; 2^32 commands at 50 Hz ≈ 2 years, but the
90
+ * arithmetic should be correct regardless). */
91
+ export declare function pendingCommandCount(view: DataView, ringOff: number): number;
92
+ /** Push a command into the ring from the TS side. Production producer is
93
+ * WASM (via `command_ring.zig`); this is for host-side tests and for
94
+ * symmetric tests across the two sides. Returns `false` on overflow and
95
+ * sets the overflow flag. Payload must be exactly 15 bytes. */
96
+ export declare function pushCommand(view: DataView, ringOff: number, opCode: number, payload: Uint8Array): boolean;
97
+ /** Read one command from the ring. Returns opCode (0 = empty/no
98
+ * command) and fills `outPayload` (15 bytes) with the slot's payload.
99
+ * When 0 is returned, `outPayload` is untouched. */
100
+ export declare function popCommand(view: DataView, ringOff: number, outPayload: Uint8Array): number;
101
+ /** Visit every pending command and bump `read_head` past them. Yields
102
+ * `{ opCode, payload }` per slot where `payload` is a freshly-copied
103
+ * 15-byte Uint8Array (so the handler can hold it past the next pop
104
+ * without aliasing the ring). Stops when the ring is empty. Used by the
105
+ * TS host drain that runs right after `wasm.tick()` returns. */
106
+ export declare function drainCommandRing(view: DataView, ringOff: number, handler: (opCode: number, payload: Uint8Array) => void): number;
107
+ //# sourceMappingURL=command_ring.d.ts.map
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Command ring — WASM-side producer / TS-side consumer SPSC ring buffer
3
+ * for structural-change intents emitted during `sim.tick()`. Plan §7.5
4
+ * (`docs/ideas/buffer-wasm-sim-plan-2026-05-14T1600.md`).
5
+ *
6
+ * Layout:
7
+ *
8
+ * [ write_head: u32 ] slot 0..N-1, monotonic (NOT slot-modulo)
9
+ * [ read_head: u32 ] slot 0..N-1, monotonic
10
+ * [ capacity: u32 ] slot count, power-of-two
11
+ * [ overflow: u32 ] 0 = OK; 1 = WASM exhausted the ring this tick
12
+ * [ slot 0: 16 B ] opCode: u8, payload: [15]u8
13
+ * [ slot 1: 16 B ] ...
14
+ * ...
15
+ *
16
+ * SPSC contract (Phase 4 scope):
17
+ * - Producer: WASM `sim.tick()`. Pushes 0..N commands during one tick;
18
+ * bumps `write_head` after each.
19
+ * - Consumer: TS host, immediately after `wasm.tick()` returns. Drains
20
+ * 0..N pending commands; bumps `read_head` after each.
21
+ * - The two never run concurrently in Phase 4 (single host thread
22
+ * orchestrates both). Phase 5's worker offload promotes the head
23
+ * bumps to `Atomics.store`; that's an additive change without
24
+ * altering the layout.
25
+ *
26
+ * Overflow:
27
+ * - If WASM would write a slot when (`write_head - read_head == capacity`),
28
+ * it sets `overflow = 1` and drops the command. TS treats overflow as
29
+ * a hard error in dev builds; production logs and continues (a command
30
+ * might be lost rather than crash the host).
31
+ *
32
+ * Slot format (plan §7.5):
33
+ * byte 0: opCode (u8). 0 is reserved as the empty-slot marker
34
+ * (`COMMAND_OP_EMPTY`); all other codes are consumer-defined.
35
+ * The engine never interprets a code — it drains
36
+ * `(opCode, payload)` and hands them to the attached
37
+ * consumer, which owns the opcode enum + payload codecs (the
38
+ * game's live in `@internal/sim`'s `command_payloads.ts`).
39
+ * bytes 1..15: payload, op-specific. Multi-byte fields may be
40
+ * unaligned within the payload; readers must use byte-
41
+ * oriented helpers (DataView in TS, `mem.readInt` in Zig).
42
+ *
43
+ * The ring lives BEFORE the layout-descriptor region in the SAB (right
44
+ * after the 32-byte header) so its offset is stable across descriptor /
45
+ * column-region growth. The host writes `header.command_ring_off` to
46
+ * point at it during `createColumnStore`; absent ring is signalled by
47
+ * `command_ring_off === 0`.
48
+ */
49
+ /** Total bytes for the ring header. */
50
+ export declare const COMMAND_RING_HEADER_BYTES = 16;
51
+ /** Fixed slot size — 1-byte opCode + 15-byte payload. */
52
+ export declare const COMMAND_RING_SLOT_BYTES = 16;
53
+ /** Default ring capacity in slots. 256 × 16 B = 4 KiB of ring data plus
54
+ * 16 B header. Sized for the worst-case burst (peak spawn intents per
55
+ * tick) × small safety margin. Tune up if a Phase 4 burst pushes past it
56
+ * in the bench harness. */
57
+ export declare const COMMAND_RING_DEFAULT_CAPACITY_SLOTS = 256;
58
+ /** Byte offsets within the ring header. Mirrored on the Zig side in
59
+ * `packages/sim/src/command_ring.zig` — keep in sync. */
60
+ export declare const COMMAND_RING_HEADER_OFFSETS: {
61
+ readonly write_head: 0;
62
+ readonly read_head: 4;
63
+ readonly capacity_slots: 8;
64
+ readonly overflow_flag: 12;
65
+ };
66
+ /** Op-code `0` is reserved across the SAB layer as the empty-slot marker
67
+ * so a zero-initialised SAB doesn't appear to hold a valid command (mirror
68
+ * of `EVENT_OP_EMPTY`). All non-zero codes are opaque to the engine —
69
+ * the attached consumer owns the opcode enum + payload codecs (the game's
70
+ * `COMMAND_OP` + `SpawnUnitFields` live in `@internal/sim`). */
71
+ export declare const COMMAND_OP_EMPTY = 0;
72
+ /** Total bytes the ring occupies for `capacity_slots` slots. */
73
+ export declare function commandRingBytes(capacitySlots: number): number;
74
+ export declare class CommandRingError extends Error {
75
+ constructor(message: string);
76
+ }
77
+ /** Initialise the ring header at `ringOff` in the SAB. Zeroes
78
+ * `write_head`, `read_head`, and `overflow_flag`; sets `capacity_slots`.
79
+ * Slot bytes are left as-is (callers normally allocate the ring on a
80
+ * fresh, zero-initialised SAB). */
81
+ export declare function initCommandRing(view: DataView, ringOff: number, capacitySlots: number): void;
82
+ /** Read live ring-header field. */
83
+ export declare function ringWriteHead(view: DataView, ringOff: number): number;
84
+ export declare function ringReadHead(view: DataView, ringOff: number): number;
85
+ export declare function ringCapacitySlots(view: DataView, ringOff: number): number;
86
+ export declare function ringOverflow(view: DataView, ringOff: number): boolean;
87
+ /** Pending command count = `(write_head - read_head) mod 2^32`. The
88
+ * `>>> 0` keeps the result a u32 in the wrap-around case (rings live for
89
+ * the host's lifetime; 2^32 commands at 50 Hz ≈ 2 years, but the
90
+ * arithmetic should be correct regardless). */
91
+ export declare function pendingCommandCount(view: DataView, ringOff: number): number;
92
+ /** Push a command into the ring from the TS side. Production producer is
93
+ * WASM (via `command_ring.zig`); this is for host-side tests and for
94
+ * symmetric tests across the two sides. Returns `false` on overflow and
95
+ * sets the overflow flag. Payload must be exactly 15 bytes. */
96
+ export declare function pushCommand(view: DataView, ringOff: number, opCode: number, payload: Uint8Array): boolean;
97
+ /** Read one command from the ring. Returns opCode (0 = empty/no
98
+ * command) and fills `outPayload` (15 bytes) with the slot's payload.
99
+ * When 0 is returned, `outPayload` is untouched. */
100
+ export declare function popCommand(view: DataView, ringOff: number, outPayload: Uint8Array): number;
101
+ /** Visit every pending command and bump `read_head` past them. Yields
102
+ * `{ opCode, payload }` per slot where `payload` is a freshly-copied
103
+ * 15-byte Uint8Array (so the handler can hold it past the next pop
104
+ * without aliasing the ring). Stops when the ring is empty. Used by the
105
+ * TS host drain that runs right after `wasm.tick()` returns. */
106
+ export declare function drainCommandRing(view: DataView, ringOff: number, handler: (opCode: number, payload: Uint8Array) => void): number;
107
+ //# sourceMappingURL=command_ring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command_ring.d.ts","sourceRoot":"","sources":["../../../src/core/store/command_ring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,uCAAuC;AACvC,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,yDAAyD;AACzD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;2BAG2B;AAC3B,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD;yDACyD;AACzD,eAAO,MAAM,2BAA2B;;;;;CAK9B,CAAC;AAEX;;;;gEAIgE;AAChE,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE9D;AASD,qBAAa,gBAAiB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI3B;AAED;;;mCAGmC;AACnC,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAU5F;AAED,mCAAmC;AACnC,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AACD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpE;AACD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzE;AACD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAErE;AAED;;;+CAG+C;AAC/C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;;+DAG+D;AAC/D,wBAAgB,WAAW,CAC1B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,UAAU,GACjB,OAAO,CAmCT;AAED;;oDAEoD;AACpD,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAiB1F;AAED;;;;gEAIgE;AAChE,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,IAAI,GACpD,MAAM,CAWR"}
@@ -0,0 +1,80 @@
1
+ import { COLUMN_DESCRIPTOR_BYTES, COLUMN_DESCRIPTOR_OFFSETS, ARCHETYPE_DESCRIPTOR_HEADER_BYTES, ARCHETYPE_DESCRIPTOR_OFFSETS, COMPONENT_MASK_WORDS } from './vendored_abi/abi.cjs';
2
+ export { COLUMN_DESCRIPTOR_BYTES, COLUMN_DESCRIPTOR_OFFSETS, ARCHETYPE_DESCRIPTOR_HEADER_BYTES, ARCHETYPE_DESCRIPTOR_OFFSETS, COMPONENT_MASK_WORDS };
3
+ /** type_tag values for `ColumnDescriptor.type_tag`. The order matches the
4
+ * TypedArrayTag union in `packages/std/type_primitives` so a tag's element
5
+ * width is `TYPE_TAG_STRIDE[tag]`. */
6
+ export declare const TYPE_TAG: {
7
+ readonly u8: 0;
8
+ readonly i8: 1;
9
+ readonly u16: 2;
10
+ readonly i16: 3;
11
+ readonly u32: 4;
12
+ readonly i32: 5;
13
+ readonly f32: 6;
14
+ readonly f64: 7;
15
+ };
16
+ export type TypeTagValue = (typeof TYPE_TAG)[keyof typeof TYPE_TAG];
17
+ /** Element width in bytes for each `TYPE_TAG`. Indexed by tag value so a
18
+ * `stride = TYPE_TAG_STRIDE[tag]` lookup is O(1) array access. */
19
+ export declare const TYPE_TAG_STRIDE: Readonly<Record<TypeTagValue, number>>;
20
+ /** String-tag → numeric-tag bridge. `TypedArrayTag` ("u8", "f32", …) is the
21
+ * vocabulary the TS-side component registry speaks; `TypeTagValue` is the
22
+ * numeric enum the SAB descriptors carry on the wire. Bridging happens at
23
+ * the ECS↔SAB seam — Archetype layouts come in as strings, ColumnSpecs go
24
+ * out as integers. */
25
+ export declare const TYPED_ARRAY_TAG_TO_TYPE_TAG: {
26
+ readonly u8: 0;
27
+ readonly i8: 1;
28
+ readonly u16: 2;
29
+ readonly i16: 3;
30
+ readonly u32: 4;
31
+ readonly i32: 5;
32
+ readonly f32: 6;
33
+ readonly f64: 7;
34
+ };
35
+ export interface ColumnDescriptor {
36
+ readonly componentId: number;
37
+ readonly fieldId: number;
38
+ readonly typeTag: TypeTagValue;
39
+ /** Byte offset of the column's first row, measured from SAB byte 0. */
40
+ readonly byteOff: number;
41
+ /** Element width in bytes; should always equal `TYPE_TAG_STRIDE[type_tag]`. */
42
+ readonly stride: number;
43
+ }
44
+ export declare function writeColumnDescriptor(view: DataView, off: number, c: ColumnDescriptor): void;
45
+ export declare function readColumnDescriptor(view: DataView, off: number): ColumnDescriptor;
46
+ /** Number of distinct components the cross-language ECS supports:
47
+ * `COMPONENT_MASK_WORDS × 32` bits in the SAB archetype descriptor mask. The
48
+ * Zig side matches archetypes purely on this mask, so a component whose ID is
49
+ * ≥ this limit would be invisible there while the heap-side `BitSet` stayed
50
+ * correct — silently conflating archetypes that differ only in such a
51
+ * component. `Store.registerComponent` enforces this as a hard registration
52
+ * ceiling so the overflow fails loudly instead (#381). The mask is sized to
53
+ * the `BitSet`'s `INITIAL_WORD_COUNT`, so a registry within the limit never
54
+ * grows a component mask past its initial words. */
55
+ export declare const STORE_DESCRIPTOR_COMPONENT_LIMIT: number;
56
+ export interface ArchetypeDescriptor {
57
+ readonly archetypeId: number;
58
+ /** Component bitmask, `COMPONENT_MASK_WORDS` little-endian u32 words. Word
59
+ * `w` holds component IDs in `[w*32, w*32+32)`. */
60
+ readonly componentMask: readonly number[];
61
+ readonly rowCount: number;
62
+ readonly rowCapacity: number;
63
+ /** Enabled-row count `≤ row_count` (#577 / #599). Per-row entity-scan loops in
64
+ * the WASM sim bound on this so disabled entities (swapped to the tail
65
+ * `[enabled_count, row_count)`) are not simulated; row-indexed cross-entity
66
+ * reads still use `row_count`. */
67
+ readonly enabledCount: number;
68
+ readonly columns: readonly ColumnDescriptor[];
69
+ }
70
+ /** Total bytes a descriptor will occupy, given its column count. Useful for
71
+ * planning the layout descriptor region size up front. */
72
+ export declare function archetypeDescriptorBytes(columnCount: number): number;
73
+ export declare function writeArchetypeDescriptor(view: DataView, off: number, d: ArchetypeDescriptor): number;
74
+ export declare function readArchetypeDescriptor(view: DataView, off: number): ArchetypeDescriptor;
75
+ export declare function writeLayoutDescriptorRegion(view: DataView, regionOff: number, descriptors: readonly ArchetypeDescriptor[]): number;
76
+ export declare function readLayoutDescriptorRegion(view: DataView, regionOff: number, archetypeCount: number): readonly ArchetypeDescriptor[];
77
+ /** Total bytes needed for a layout descriptor region holding these archetypes.
78
+ * Use to size the SAB region between header end and the first column. */
79
+ export declare function layoutDescriptorRegionBytes(descriptors: readonly ArchetypeDescriptor[]): number;
80
+ //# sourceMappingURL=descriptor.d.ts.map
@@ -0,0 +1,80 @@
1
+ import { COLUMN_DESCRIPTOR_BYTES, COLUMN_DESCRIPTOR_OFFSETS, ARCHETYPE_DESCRIPTOR_HEADER_BYTES, ARCHETYPE_DESCRIPTOR_OFFSETS, COMPONENT_MASK_WORDS } from './vendored_abi/abi.js';
2
+ export { COLUMN_DESCRIPTOR_BYTES, COLUMN_DESCRIPTOR_OFFSETS, ARCHETYPE_DESCRIPTOR_HEADER_BYTES, ARCHETYPE_DESCRIPTOR_OFFSETS, COMPONENT_MASK_WORDS };
3
+ /** type_tag values for `ColumnDescriptor.type_tag`. The order matches the
4
+ * TypedArrayTag union in `packages/std/type_primitives` so a tag's element
5
+ * width is `TYPE_TAG_STRIDE[tag]`. */
6
+ export declare const TYPE_TAG: {
7
+ readonly u8: 0;
8
+ readonly i8: 1;
9
+ readonly u16: 2;
10
+ readonly i16: 3;
11
+ readonly u32: 4;
12
+ readonly i32: 5;
13
+ readonly f32: 6;
14
+ readonly f64: 7;
15
+ };
16
+ export type TypeTagValue = (typeof TYPE_TAG)[keyof typeof TYPE_TAG];
17
+ /** Element width in bytes for each `TYPE_TAG`. Indexed by tag value so a
18
+ * `stride = TYPE_TAG_STRIDE[tag]` lookup is O(1) array access. */
19
+ export declare const TYPE_TAG_STRIDE: Readonly<Record<TypeTagValue, number>>;
20
+ /** String-tag → numeric-tag bridge. `TypedArrayTag` ("u8", "f32", …) is the
21
+ * vocabulary the TS-side component registry speaks; `TypeTagValue` is the
22
+ * numeric enum the SAB descriptors carry on the wire. Bridging happens at
23
+ * the ECS↔SAB seam — Archetype layouts come in as strings, ColumnSpecs go
24
+ * out as integers. */
25
+ export declare const TYPED_ARRAY_TAG_TO_TYPE_TAG: {
26
+ readonly u8: 0;
27
+ readonly i8: 1;
28
+ readonly u16: 2;
29
+ readonly i16: 3;
30
+ readonly u32: 4;
31
+ readonly i32: 5;
32
+ readonly f32: 6;
33
+ readonly f64: 7;
34
+ };
35
+ export interface ColumnDescriptor {
36
+ readonly componentId: number;
37
+ readonly fieldId: number;
38
+ readonly typeTag: TypeTagValue;
39
+ /** Byte offset of the column's first row, measured from SAB byte 0. */
40
+ readonly byteOff: number;
41
+ /** Element width in bytes; should always equal `TYPE_TAG_STRIDE[type_tag]`. */
42
+ readonly stride: number;
43
+ }
44
+ export declare function writeColumnDescriptor(view: DataView, off: number, c: ColumnDescriptor): void;
45
+ export declare function readColumnDescriptor(view: DataView, off: number): ColumnDescriptor;
46
+ /** Number of distinct components the cross-language ECS supports:
47
+ * `COMPONENT_MASK_WORDS × 32` bits in the SAB archetype descriptor mask. The
48
+ * Zig side matches archetypes purely on this mask, so a component whose ID is
49
+ * ≥ this limit would be invisible there while the heap-side `BitSet` stayed
50
+ * correct — silently conflating archetypes that differ only in such a
51
+ * component. `Store.registerComponent` enforces this as a hard registration
52
+ * ceiling so the overflow fails loudly instead (#381). The mask is sized to
53
+ * the `BitSet`'s `INITIAL_WORD_COUNT`, so a registry within the limit never
54
+ * grows a component mask past its initial words. */
55
+ export declare const STORE_DESCRIPTOR_COMPONENT_LIMIT: number;
56
+ export interface ArchetypeDescriptor {
57
+ readonly archetypeId: number;
58
+ /** Component bitmask, `COMPONENT_MASK_WORDS` little-endian u32 words. Word
59
+ * `w` holds component IDs in `[w*32, w*32+32)`. */
60
+ readonly componentMask: readonly number[];
61
+ readonly rowCount: number;
62
+ readonly rowCapacity: number;
63
+ /** Enabled-row count `≤ row_count` (#577 / #599). Per-row entity-scan loops in
64
+ * the WASM sim bound on this so disabled entities (swapped to the tail
65
+ * `[enabled_count, row_count)`) are not simulated; row-indexed cross-entity
66
+ * reads still use `row_count`. */
67
+ readonly enabledCount: number;
68
+ readonly columns: readonly ColumnDescriptor[];
69
+ }
70
+ /** Total bytes a descriptor will occupy, given its column count. Useful for
71
+ * planning the layout descriptor region size up front. */
72
+ export declare function archetypeDescriptorBytes(columnCount: number): number;
73
+ export declare function writeArchetypeDescriptor(view: DataView, off: number, d: ArchetypeDescriptor): number;
74
+ export declare function readArchetypeDescriptor(view: DataView, off: number): ArchetypeDescriptor;
75
+ export declare function writeLayoutDescriptorRegion(view: DataView, regionOff: number, descriptors: readonly ArchetypeDescriptor[]): number;
76
+ export declare function readLayoutDescriptorRegion(view: DataView, regionOff: number, archetypeCount: number): readonly ArchetypeDescriptor[];
77
+ /** Total bytes needed for a layout descriptor region holding these archetypes.
78
+ * Use to size the SAB region between header end and the first column. */
79
+ export declare function layoutDescriptorRegionBytes(descriptors: readonly ArchetypeDescriptor[]): number;
80
+ //# sourceMappingURL=descriptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"descriptor.d.ts","sourceRoot":"","sources":["../../../src/core/store/descriptor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAeH,OAAO,EACN,uBAAuB,EACvB,yBAAyB,EACzB,iCAAiC,EACjC,4BAA4B,EAC5B,oBAAoB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,uBAAuB,EACvB,yBAAyB,EACzB,iCAAiC,EACjC,4BAA4B,EAC5B,oBAAoB,EACpB,CAAC;AAEF;;sCAEsC;AACtC,eAAO,MAAM,QAAQ;;;;;;;;;CASX,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEpE;kEACkE;AAClE,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CASjE,CAAC;AAEH;;;;sBAIsB;AACtB,eAAO,MAAM,2BAA2B;;;;;;;;;CAS9B,CAAC;AAUX,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAS5F;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAQlF;AAeD;;;;;;;;oDAQoD;AACpD,eAAO,MAAM,gCAAgC,QAA4B,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;uDACmD;IACnD,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;sCAGkC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC9C;AAED;0DAC0D;AAC1D,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,MAAM,EACX,CAAC,EAAE,mBAAmB,GACpB,MAAM,CAoBR;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAuBxF;AAcD,wBAAgB,2BAA2B,CAC1C,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,SAAS,mBAAmB,EAAE,GACzC,MAAM,CAMR;AAED,wBAAgB,0BAA0B,CACzC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACpB,SAAS,mBAAmB,EAAE,CAShC;AAED;yEACyE;AACzE,wBAAgB,2BAA2B,CAC1C,WAAW,EAAE,SAAS,mBAAmB,EAAE,GACzC,MAAM,CAMR"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Entity-index SAB region — `EntityID` → `(archetype_id, row, generation)`
3
+ * lookup table shared with the Zig sim. (#245 / Phase 4 PR 4B)
4
+ *
5
+ * Every hot fixed-update system that needs to resolve an `EntityID`
6
+ * held in a column (e.g. a component field that points at another
7
+ * entity → that entity's columns) has to do
8
+ * `entityId → archetype + row` translation. The plan forbids
9
+ * WASM-into-TS callbacks during `tick()` (§4 / §5.2), so the lookup
10
+ * tables must live in shared memory.
11
+ *
12
+ * Layout:
13
+ *
14
+ * [ length: u32 ] high-water index (count of slots ever issued)
15
+ * [ capacity: u32 ] backing-array length (slots × 1; not bytes)
16
+ * [ _pad0: u32 ] alignment pad to 16 bytes
17
+ * [ _pad1: u32 ] ───
18
+ * [ generations[capacity]: i32 ]
19
+ * [ archetypes[capacity]: i32 ]
20
+ * [ rows[capacity]: i32 ]
21
+ *
22
+ * Sentinels:
23
+ * - `generations[i] = 0` (INITIAL_GENERATION) for never-used slots.
24
+ * Generation grows by 1 on every destroy; an `EntityID`'s generation
25
+ * field matches `generations[index]` iff it's still alive.
26
+ * - `archetypes[i] = -1` (UNASSIGNED) when the slot hasn't been placed
27
+ * into an archetype, OR when the entity is destroyed.
28
+ * - `rows[i] = -1` (UNASSIGNED) on destroy / not-placed.
29
+ *
30
+ * Field width: i32 (signed) on the TS side so `-1` round-trips through
31
+ * `Int32Array` without unsigned coercion. Zig reads as i32 too — bit
32
+ * pattern is identical to u32 `0xFFFFFFFF` for the UNASSIGNED case, and
33
+ * for valid archetype_ids (bounded by `MAX_INDEX = 2^20`) the sign bit
34
+ * is never set, so signed/unsigned interpretation agrees.
35
+ *
36
+ * Region placement: between command ring and descriptor region so the
37
+ * offset is stable across descriptor / column-region growth (same
38
+ * property the command ring gets). Grow path (when entityHighWater
39
+ * exceeds `capacity`) uses `growColumnStore` — slow path, same as
40
+ * descriptor-region overflow.
41
+ *
42
+ * Reading from Zig: see `packages/sim/src/entity_index.zig` for the
43
+ * symmetric reader.
44
+ */
45
+ /** Fixed bytes of the region header (length, capacity, two pad u32s for
46
+ * 16-byte alignment of the i32 arrays that follow). */
47
+ export declare const ENTITY_INDEX_HEADER_BYTES = 16;
48
+ /** Bytes per slot: three i32 columns. */
49
+ export declare const ENTITY_INDEX_BYTES_PER_SLOT = 12;
50
+ /** Byte offsets within the region header. Locked by the
51
+ * `entity_index.test.ts` golden bytes; any change here is a
52
+ * `SIM_ABI_VERSION` bump. */
53
+ export declare const ENTITY_INDEX_HEADER_OFFSETS: {
54
+ readonly length: 0;
55
+ readonly capacity: 4;
56
+ };
57
+ /** Default initial slot count when the engine creates a Store. Matches
58
+ * `MAX_INDEX = (1 << 20) - 1 + 1 = 1_048_576` (the EntityID 20-bit index
59
+ * range, see `entity.ts`), so the region pre-sizes to the entire
60
+ * addressable entity space and `createEntity` can never run out under
61
+ * the default. 1M × 12 B ≈ 12 MiB SAB region — virtual memory only;
62
+ * physical pages allocate lazily via OS page-fault on first touch, so
63
+ * the typical 1000-entity workload pays for ~12 KiB physical even though the
64
+ * virtual reservation is 12 MiB.
65
+ *
66
+ * Tests / benches can pass a smaller `StoreOptions.entityIndexCapacity`
67
+ * to bench tighter reservations. A future PR will replace this with
68
+ * on-demand growth via `growColumnStore` so the default can drop. */
69
+ export declare const ENTITY_INDEX_DEFAULT_CAPACITY: number;
70
+ /** Total region bytes for `capacity` slots: header + 3 i32 columns. */
71
+ export declare function entityIndexRegionBytes(capacity: number): number;
72
+ /** Byte offset of the generations column within the region. */
73
+ export declare function entityIndexGenerationsOff(regionOff: number): number;
74
+ /** Byte offset of the archetype-id column. */
75
+ export declare function entityIndexArchetypesOff(regionOff: number, capacity: number): number;
76
+ /** Byte offset of the row column. */
77
+ export declare function entityIndexRowsOff(regionOff: number, capacity: number): number;
78
+ export declare class EntityIndexError extends Error {
79
+ constructor(message: string);
80
+ }
81
+ /** Initialise the region header at `regionOff`. Sets `length=0` and
82
+ * `capacity=<arg>`. The i32 arrays past the header are left untouched
83
+ * (callers normally allocate the region on a fresh, zero-initialised
84
+ * SAB; generation 0 is the INITIAL_GENERATION sentinel, archetype/row
85
+ * sentinels of 0 are caught by `length=0` so no read reaches them). */
86
+ export declare function initEntityIndexRegion(view: DataView, regionOff: number, capacity: number): void;
87
+ /** Read the region's current length (high-water index, ≤ capacity). */
88
+ export declare function entityIndexLength(view: DataView, regionOff: number): number;
89
+ /** Read the region's current capacity (backing-array length). */
90
+ export declare function entityIndexCapacity(view: DataView, regionOff: number): number;
91
+ /** Update the region's length (called from the engine's Store on every
92
+ * entity allocation that pushes `entityHighWater` past its previous
93
+ * value). */
94
+ export declare function setEntityIndexLength(view: DataView, regionOff: number, length: number): void;
95
+ /** Materialise the three Int32Array views over the region's column data
96
+ * for use by the engine's Store. The views live as long as the
97
+ * SharedArrayBuffer hasn't been reallocated — refresh on `view_stamp`
98
+ * bump (the engine's `_onBufferResized` callback).
99
+ *
100
+ * Three separate views (not a single struct-of-arrays object) because
101
+ * the engine's hot paths benefit from each being a direct typed-array
102
+ * read; V8 specialises `arr[i] = v` aggressively for typed arrays. */
103
+ export declare function buildEntityIndexViews(buffer: ArrayBufferLike, regionOff: number, capacity: number): {
104
+ readonly generations: Int32Array;
105
+ readonly archetypes: Int32Array;
106
+ readonly rows: Int32Array;
107
+ };
108
+ //# sourceMappingURL=entity_index.d.ts.map