@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
package/README.md CHANGED
@@ -1,289 +1,197 @@
1
1
  # oecs
2
2
 
3
- A fast, minimal, archetype-based Entity Component System for TypeScript.
4
-
5
- ## Features
6
-
7
- - **Archetype-based SoA storage.** Entities sharing a component set share contiguous typed-array columns — cache-friendly loops, no per-entity object allocation.
8
- - **Phantom-typed components.** `ComponentDef<{ x: "f64", y: "f64" }>` is a branded integer at runtime and a fully-typed schema at compile time. Misspelled fields are compile errors.
9
- - **Callback iteration.** `query.for_each(arch => ...)` yields non-empty archetypes; you write the row loop over typed-array columns.
10
- - **Tick-based change detection.** Each `(archetype, component)` tracks a change tick. `query.changed(Pos).for_each(...)` visits only archetypes mutated since the system's last run.
11
- - **Key-based events and resources.** `event_key<F>` / `resource_key<T>` create module-scope symbol handles carrying their schema as a phantom — import the key anywhere.
12
- - **Cached single-entity refs.** `ctx.ref` / `ctx.ref_mut` give ergonomic `pos.x += vel.vx * dt` that compiles to a direct typed-array index op.
13
- - **Deferred structural changes.** `ctx.add_component` / `ctx.remove_component` / `ctx.destroy_entity` buffer until the schedule flushes between phases, so iterators stay valid.
14
- - **Topological scheduler.** Per-phase Kahn's-algorithm sort over a binary heap, with insertion order as a deterministic tiebreaker.
15
- - **Fixed timestep.** Accumulator loop with configurable `fixed_timestep` and spiral-of-death protection.
16
- - **Reusable primitives.** `BitSet`, `SparseSet`, `SparseMap`, `GrowableTypedArray`, `BinaryHeap`, `topological_sort` all exported.
3
+ **A full-featured, archetype-based Entity Component System for TypeScript.**
4
+
5
+ `@oasys/oecs` is a complete ECS — not just storage-and-queries, but the whole toolkit you expect from a
6
+ mature engine: observers, relations with wildcards, sparse storage, system sets and run conditions,
7
+ entity enable/disable, templates, deterministic hashing with snapshot/restore, a typed host→ECS write
8
+ seam, and an optional reactive UI bridge. It is **pure TypeScript and zero-dependency by default** it
9
+ runs over a plain resizable `ArrayBuffer`, so it needs no `SharedArrayBuffer` and no cross-origin
10
+ isolation (COOP/COEP). An opt-in shared-memory profile swaps in a `SharedArrayBuffer` for worker offload
11
+ or a WASM compute backend; both profiles share one core and agree, byte-for-byte, on `stateHash`.
12
+
13
+ - **Fast** struct-of-arrays column storage grouped by archetype; iteration is a tight loop over typed
14
+ arrays with no per-entity object allocation.
15
+ - **Type-safe** component handles are callable defs with stable numeric ids at runtime and fully-typed schemas at compile time;
16
+ misspelled fields are compile errors.
17
+ - **Deterministic** — an opt-in mode gives a backing-agnostic `stateHash` plus snapshot/restore and
18
+ command-log replay.
19
+ - **Complete** — the feature surface below is the whole engine, not a starting point.
17
20
 
18
21
  ## Installation
19
22
 
20
23
  ```bash
21
- pnpm add @oasys/oecs
24
+ pnpm add @oasys/oecs # npm / pnpm / yarn
25
+ # or
26
+ deno add jsr:@oasys/oecs # JSR (Deno)
27
+ # or
28
+ npx jsr add @oasys/oecs # JSR (npm-compatible)
22
29
  ```
23
30
 
31
+ Requires a runtime with resizable `ArrayBuffer` (the default heap profile grows in place):
32
+ Node ≥ 20, Deno ≥ 1.38, Chrome 111+, Firefox 128+, Safari 16.4+.
33
+
24
34
  ## Quick start
25
35
 
26
36
  ```ts
27
- import { ECS, SCHEDULE, event_key, resource_key } from "@oasys/oecs";
28
-
29
- // Keysmodule scope, phantom-typed
30
- const Time = resource_key<{ delta: number; elapsed: number }>("Time");
31
- const DamageEvent = event_key<readonly ["target", "amount"]>("Damage");
32
-
33
- const world = new ECS();
34
-
35
- // Components
36
- const Pos = world.register_component({ x: "f64", y: "f64" });
37
- const Vel = world.register_component(["vx", "vy"] as const);
38
-
39
- // Resources & events
40
- world.register_resource(Time, { delta: 0, elapsed: 0 });
41
- world.register_event(DamageEvent, ["target", "amount"] as const);
42
-
43
- // Entities
44
- const e = world.create_entity();
45
- world.add_components(e, [
46
- { def: Pos, values: { x: 0, y: 0 } },
47
- { def: Vel, values: { vx: 100, vy: 50 } },
48
- ]);
49
-
50
- // System — query resolved once at registration
51
- const moveSys = world.register_system(
52
- (q, ctx, dt) => {
53
- q.for_each((arch) => {
54
- const px = arch.get_column_mut(Pos, "x", ctx.world_tick);
55
- const py = arch.get_column_mut(Pos, "y", ctx.world_tick);
56
- const vx = arch.get_column(Vel, "vx");
57
- const vy = arch.get_column(Vel, "vy");
58
- for (let i = 0; i < arch.entity_count; i++) {
59
- px[i] += vx[i] * dt;
60
- py[i] += vy[i] * dt;
37
+ import { ECS, SCHEDULE } from "@oasys/oecs";
38
+
39
+ const ecs = new ECS(); // pure-TS heap profile no SharedArrayBuffer needed
40
+
41
+ // Components record syntax (per-field type) or array shorthand (defaults to "f64")
42
+ const Pos = ecs.registerComponent({ x: "f64", y: "f64" });
43
+ const Vel = ecs.registerComponent(["vx", "vy"] as const);
44
+
45
+ // A query is a live, cached view over matching archetypes — build it once, reuse it.
46
+ const movers = ecs.query(Pos, Vel);
47
+
48
+ // Systems declare the components they read/write (checked in dev builds).
49
+ const move = ecs.registerSystem({
50
+ reads: [Vel],
51
+ writes: [Pos], // a declared write implies read of the same component
52
+ fn: (ctx, dt) => {
53
+ movers.eachChunk((cols, count) => {
54
+ const { x, y } = cols.mut(Pos); // whole group; stamps Pos's change tick once
55
+ const { vx, vy } = cols.read(Vel); // read-only group
56
+ for (let i = 0; i < count; i++) {
57
+ x[i] += vx[i] * dt;
58
+ y[i] += vy[i] * dt;
61
59
  }
62
60
  });
63
61
  },
64
- (qb) => qb.every(Pos, Vel),
65
- );
66
-
67
- world.add_systems(SCHEDULE.UPDATE, moveSys);
68
- world.startup();
69
-
70
- let last = performance.now();
71
- function frame() {
72
- const now = performance.now();
73
- const dt = (now - last) / 1000;
74
- last = now;
75
- const t = world.resource(Time);
76
- world.set_resource(Time, { delta: dt, elapsed: t.elapsed + dt });
77
- world.update(dt);
78
- requestAnimationFrame(frame);
79
- }
80
- requestAnimationFrame(frame);
81
- ```
82
-
83
- ## World options
84
-
85
- ```ts
86
- const world = new ECS({
87
- initial_capacity: 4096,
88
- fixed_timestep: 1 / 50,
89
- max_fixed_steps: 4,
90
- });
91
- ```
92
-
93
- | Option | Type | Default | Description |
94
- | ------------------ | -------- | ------- | ----------- |
95
- | `initial_capacity` | `number` | `1024` | Starting size of each archetype's entity-ID and column buffers. Buffers double on overflow; pick close to your expected per-archetype entity count to avoid early reallocations. |
96
- | `fixed_timestep` | `number` | `1/60` | Interval (seconds) at which `SCHEDULE.FIXED_UPDATE` systems run. |
97
- | `max_fixed_steps` | `number` | `5` | Hard cap on fixed-update iterations per frame. Protects against spiral of death. |
98
-
99
- ## Components
100
-
101
- Records give per-field type control; array shorthand defaults to `f64`. Tags have no fields.
102
-
103
- ```ts
104
- const Pos = world.register_component({ x: "f64", y: "f64" });
105
- const Health = world.register_component({ current: "i32", max: "i32" });
106
- const Vel = world.register_component(["vx", "vy"] as const);
107
- const IsEnemy = world.register_tag();
108
-
109
- world.add_components(e, [
110
- { def: Pos, values: { x: 0, y: 0 } },
111
- { def: Vel, values: { vx: 1, vy: 0 } },
112
- { def: IsEnemy },
113
- ]);
114
- ```
115
-
116
- Supported tags: `f32`, `f64`, `i8`, `i16`, `i32`, `u8`, `u16`, `u32`.
117
-
118
- See [docs/api/components.md](docs/api/components.md).
119
-
120
- ## Queries
121
-
122
- Live, cached views over matching archetypes. Iterate with `for_each`.
123
-
124
- ```ts
125
- const q = world.query(Pos, Vel);
126
-
127
- q.for_each((arch) => {
128
- const px = arch.get_column(Pos, "x");
129
- const py = arch.get_column(Pos, "y");
130
- for (let i = 0; i < arch.entity_count; i++) { /* ... */ }
131
- });
132
-
133
- // Chaining returns new cached queries
134
- const targets = world.query(Pos).and(Health).not(Shield).any_of(IsEnemy, IsBoss);
135
-
136
- // Change detection — only archetypes whose Pos column changed since last run
137
- q.changed(Pos).for_each((arch) => { /* ... */ });
138
- ```
139
-
140
- See [docs/api/queries.md](docs/api/queries.md) and [docs/api/change-detection.md](docs/api/change-detection.md).
141
-
142
- ## Systems
143
-
144
- Systems are plain functions. Three registration shapes all return a `SystemDescriptor`.
145
-
146
- ```ts
147
- // Bare function
148
- const logSys = world.register_system((ctx, dt) => { /* ... */ });
149
-
150
- // Function + query builder (query resolved once at registration)
151
- const moveSys = world.register_system(
152
- (q, ctx, dt) => { q.for_each((arch) => { /* ... */ }); },
153
- (qb) => qb.every(Pos, Vel),
154
- );
155
-
156
- // Full config — lifecycle hooks, name
157
- const spawnSys = world.register_system({
158
- name: "spawn",
159
- fn(ctx, dt) { /* every frame */ },
160
- on_added(ctx) { /* once during world.startup() */ },
161
- dispose() { /* during world.dispose() */ },
162
- });
163
- ```
164
-
165
- `SystemContext` exposes deferred structural ops, per-entity access, events, resources, and tick bookkeeping (`ctx.world_tick`, `ctx.last_run_tick`).
166
-
167
- See [docs/api/systems.md](docs/api/systems.md).
168
-
169
- ## Resources
170
-
171
- Global singletons keyed by `ResourceKey<T>`. Values can be any type — objects, typed arrays, class instances.
172
-
173
- ```ts
174
- import { resource_key } from "@oasys/oecs";
175
-
176
- const Time = resource_key<{ delta: number; elapsed: number }>("Time");
177
- const Assets = resource_key<Map<string, ImageBitmap>>("Assets");
178
-
179
- world.register_resource(Time, { delta: 0, elapsed: 0 });
180
- world.register_resource(Assets, new Map());
181
-
182
- const t = world.resource(Time); // typed as { delta, elapsed }
183
- world.set_resource(Time, { delta: 0.016, elapsed: 0 }); // swap in a new value
184
- ```
185
-
186
- See [docs/api/resources.md](docs/api/resources.md).
187
-
188
- ## Events
189
-
190
- Fire-and-forget SoA channels. Data events carry typed fields; signals carry only a count. Cleared at the end of each `world.update(dt)`.
191
-
192
- ```ts
193
- import { event_key, signal_key } from "@oasys/oecs";
194
-
195
- const DamageEvent = event_key<readonly ["target", "amount"]>("Damage");
196
- const GameOver = signal_key("GameOver");
197
-
198
- world.register_event(DamageEvent, ["target", "amount"] as const);
199
- world.register_signal(GameOver);
200
-
201
- ctx.emit(DamageEvent, { target: victimId, amount: 25 });
202
- ctx.emit(GameOver);
203
-
204
- const dmg = ctx.read(DamageEvent);
205
- for (let i = 0; i < dmg.length; i++) {
206
- dmg.target[i]; dmg.amount[i]; // number columns
207
- }
208
- if (ctx.read(GameOver).length > 0) { /* fired */ }
209
- ```
210
-
211
- See [docs/api/events.md](docs/api/events.md).
212
-
213
- ## Refs
214
-
215
- Cached single-entity handles — resolve archetype + row + column once, then read/write fields by name.
216
-
217
- ```ts
218
- const pos = ctx.ref_mut(Pos, entity); // writable; bumps Pos change tick
219
- const vel = ctx.ref(Vel, entity); // readonly
220
- pos.x += vel.vx * dt;
221
- pos.y += vel.vy * dt;
222
- ```
223
-
224
- Prefer `ctx.ref` by default; reach for `ctx.ref_mut` at the point of mutation. Do not hold refs across archetype transitions or phase flushes.
225
-
226
- See [docs/api/refs.md](docs/api/refs.md).
227
-
228
- ## Schedule
229
-
230
- Seven phases run in a fixed order:
231
-
232
- | Phase | When | Typical use |
233
- | -------------- | -------------------------------- | ----------------------- |
234
- | `PRE_STARTUP` | Once, before `STARTUP` | Resource loading |
235
- | `STARTUP` | Once | Initial entity spawning |
236
- | `POST_STARTUP` | Once, after `STARTUP` | Validation |
237
- | `FIXED_UPDATE` | Zero+ times per frame (fixed dt) | Physics, simulation |
238
- | `PRE_UPDATE` | Every frame, first | Input, time |
239
- | `UPDATE` | Every frame | Game logic, AI |
240
- | `POST_UPDATE` | Every frame, last | Rendering, cleanup |
241
-
242
- ```ts
243
- world.add_systems(SCHEDULE.UPDATE, moveSys, damageSys, {
244
- system: deathSys,
245
- ordering: { after: [damageSys] },
246
62
  });
247
- ```
248
63
 
249
- Within a phase, systems are topologically sorted by `before` / `after` constraints. `ctx.flush()` runs automatically between phases.
64
+ ecs.addSystems(SCHEDULE.UPDATE, move);
65
+ ecs.startup();
250
66
 
251
- See [docs/api/schedule.md](docs/api/schedule.md).
67
+ const e = ecs.spawn();
68
+ ecs.addComponent(e, Pos, { x: 0, y: 0 });
69
+ ecs.addComponent(e, Vel, { vx: 100, vy: 50 });
252
70
 
253
- ## Entity lifecycle
254
-
255
- ```ts
256
- const e = world.create_entity();
257
- world.is_alive(e); // true
258
- world.destroy_entity_deferred(e);
259
- world.flush();
260
- world.is_alive(e); // false
261
- ```
262
-
263
- `EntityID` is a packed 31-bit integer (20-bit slot index, 11-bit generation). Destroying an entity bumps its slot's generation, so stale handles are detected as dead. Inside systems, use `ctx.create_entity()` (immediate) and `ctx.destroy_entity(e)` (deferred).
264
-
265
- See [docs/api/entities.md](docs/api/entities.md).
266
-
267
- ## Dev vs Prod modes
268
-
269
- A compile-time `__DEV__` flag gates runtime sanity checks: bounds checks, liveness checks, duplicate-system detection, and registration validation. These are tree-shaken out of production bundles by the Vite build. Scheduler cycle detection is always active and throws `ECS_ERROR.CIRCULAR_SYSTEM_DEPENDENCY` on the first offending run.
270
-
271
- ## Development
272
-
273
- ```bash
274
- pnpm install
275
- pnpm test # vitest
276
- pnpm bench # vitest bench
277
- pnpm build # vite library build
278
- pnpm tsc --noEmit # type check
71
+ ecs.update(1 / 60);
72
+ ecs.getField(e, Pos, "x"); // ≈ 1.667
279
73
  ```
280
74
 
281
- ## Guides
75
+ ## Features
282
76
 
283
- - [Getting Started](docs/GETTING_STARTED.md) — step-by-step tutorial.
284
- - [Best Practices](docs/BEST_PRACTICES.md) — component design, query patterns, pitfalls.
285
- - [Architecture](docs/ARCHITECTURE.md)data layout, flush model, cache invalidation.
286
- - API reference:
77
+ **Storage & data model**
78
+
79
+ - **Archetype SoA storage** over a backing-neutral `ColumnStore` entities with the same component set
80
+ share contiguous typed-array columns; cache-friendly loops, no per-entity object allocation.
81
+ - **Phantom-typed components** — `registerComponent({ x: "f64", y: "f64" })` returns a callable
82
+ `ComponentDef` with a stable numeric `.id` at runtime and a fully-typed schema at compile time.
83
+ Record syntax for per-field types, array shorthand for uniform `f64`, and `registerTag()` for
84
+ data-free markers. Field types: `f32 f64 i8 i16 i32 u8 u16 u32`.
85
+ - **Two storage profiles, one core** — pure-TS heap (`ArrayBuffer`) by default; opt-in
86
+ `SharedArrayBuffer` for workers / WASM. Same code path, same `stateHash`, sized through a single
87
+ `memory` surface (entity budget, byte cap, or pinned capacity).
88
+
89
+ **Queries**
90
+
91
+ - **Live, cached queries** — `ecs.query(Pos, Vel)` refined with `.and()` / `.without()` / `.anyOf()`;
92
+ new matching archetypes are pushed in automatically.
93
+ - **Two iteration verbs** — `forEach(arch => …)` for read-only archetype iteration, `eachChunk((cols, count) => …)`
94
+ for the mutable hot path (`cols.mut` / `cols.read` resolve a whole component's columns at once).
95
+ - **Change detection** — per-`(archetype, component)` change ticks; `query.changed(Pos)` visits only
96
+ archetypes written since the system's threshold tick.
97
+ - **Relation & hierarchy queries** — `(R, *)` / `(*, T)` wildcards, `forEachRelatedTo`, and
98
+ `query.hierarchy(rel, depth)`. **Sparse queries** via `query.withSparse(...)`; disabled entities are
99
+ skipped unless you opt in with `query.includeDisabled()`.
100
+
101
+ **Systems & scheduling**
102
+
103
+ - **Declarative systems** — plain functions in a `SystemConfig` declaring `reads` / `writes`, enforced by
104
+ a dev-mode access checker (tree-shaken in production). Bare `(ctx, dt)` and `(q, ctx, dt)` +
105
+ query-builder overloads exist for no-access glue; lifecycle hooks `onAdded` / `onRemoved` / `dispose`;
106
+ `exclusive: true` for full-world setup/teardown.
107
+ - **Topological scheduler** — seven phases (`PRE_STARTUP` → `STARTUP` → `POST_STARTUP`, `FIXED_UPDATE`,
108
+ `PRE_UPDATE` → `UPDATE` → `POST_UPDATE`); per-phase Kahn sort by `before` / `after`, with insertion
109
+ order as a deterministic tiebreaker. Always-on cycle detection.
110
+ - **Fixed timestep** — accumulator loop with configurable `fixedTimestep` and spiral-of-death protection.
111
+ - **System sets & run conditions** — `systemSet(...)` + `configureSet(...)`; `runIfResourceEq`,
112
+ `runEveryNTicks`, `runIfAnyMatch`, and custom `RunCondition`s.
113
+
114
+ **Structural changes**
115
+
116
+ - **Deferred inside systems, immediate on the host** — `ctx.commands` (a Bevy-`Commands`-style
117
+ facade) buffers spawn / add / remove / despawn / enable / disable until the phase flush, so
118
+ iterators stay valid. Every host-side mutation (`ecs.addComponent` / `removeComponent` /
119
+ `despawn` / `disable` / `enable`) applies immediately.
120
+ - **Entity enable/disable** — `disable` / `enable` / `isDisabled`; disabled rows sit in a partitioned
121
+ tail and are skipped by default queries.
122
+ - **Templates & bundles** — `ecs.template([...])` blueprints consumed by `spawn` /
123
+ `spawnMany` for zero-transition spawns; `bundle(...)` + `spawnBundle(...)`.
124
+
125
+ **Reactivity & relationships**
126
+
127
+ - **Observers** — `ecs.observe(...)` for `onAdd` / `onRemove` / `onSet` / `onEnable` / `onDisable`,
128
+ structural or per-entity.
129
+ - **Relations** — `(relation, target)` pairs with `ChildOf` / `IsA` presets, exclusive / multi arities,
130
+ bidirectional queries (`targetOf` / `sourcesOf` / `ancestorsOf` / `rootOf` / `cascadeOf`), and
131
+ configurable on-delete cleanup (`delete` / `clear` / `orphan`). Stored sparsely — no archetype
132
+ transition, no identity bit.
133
+ - **Sparse storage** — `registerSparseComponent` / `registerSparseTag`, `addSparse` / `removeSparse` for
134
+ churny or rare data that shouldn't cause archetype transitions.
135
+ - **Resources** — typed global singletons via `resourceKey<T>`. **Events** — fire-and-forget SoA channels
136
+ via `eventKey<F>` / `signalKey`, cleared at the end of each `update`.
137
+ - **Cached refs** — `ctx.ref(def, e)` (mutable, bumps the change tick) / `ctx.refRead(def, e)`
138
+ (read-only): resolve archetype + row + column once, then `pos.x += vel.vx * dt`.
139
+
140
+ **Determinism, persistence & integration**
141
+
142
+ - **Determinism** (opt-in) — `new ECS({ deterministic: true })`, then `ecs.snapshots.stateHash()` (FNV-1a over
143
+ live dense bytes, sparse stores, and multi-relation target sets), `ecs.snapshots.capture()` /
144
+ `ecs.snapshots.restore(...)`, plus sparse variants. Backing-agnostic: a heap world and a shared world with identical history produce
145
+ identical hashes.
146
+ - **Host → ECS write seam** — `installHostCommandSeam(ecs)` applies typed `HostCommand`s off-schedule
147
+ via a blessed `exclusive` system, with record/replay (`HostCommandRecorder`, `replayCommandLog`) and a
148
+ cross-thread ring transport.
149
+ - **Reactive UI seam** (optional) — a zero-dep signals kernel (`@oasys/oecs/reactive`), an ECS→reactive
150
+ bridge that publishes only dirty entities/columns (`@oasys/oecs/reactive-sync`), and a SolidJS adapter
151
+ (`@oasys/oecs/solid`).
152
+ - **Editor layer** — undo/redo + field handles over the write seam (`@oasys/oecs/editor`).
153
+ - **Frame tracing** — `ecs.setTrace(sink)` + `FrameTraceRecorder` for a structured per-frame event
154
+ stream (dev-gated).
155
+ - **Compute backend seam** — `ecs.attachBackend(...)` to run a system body on a compiled backend (WASM,
156
+ …) instead of its TS closure.
157
+
158
+ **Reference**
159
+
160
+ - **Typed errors** — an `ECSError` taxonomy with a `category` enum and an `isEcsError` guard, all exported.
161
+ - **Reusable primitives** (`@oasys/oecs/primitives`) — `BitSet`, `SparseSet`, `SparseMap`,
162
+ `GrowableTypedArray`, `BinaryHeap`, and `topologicalSort`, usable standalone.
163
+
164
+ ## Entry points
165
+
166
+ The core is `@oasys/oecs`; everything else is opt-in and costs nothing until imported.
167
+
168
+ | Import | What it is |
169
+ | --- | --- |
170
+ | `@oasys/oecs` | the ECS — pure-TS heap profile by default |
171
+ | `@oasys/oecs/shared` | opt-in `SharedArrayBuffer` allocators for worker offload / a WASM backend (needs COOP/COEP) |
172
+ | `@oasys/oecs/reactive` | zero-dependency reactive kernel (`signal`/`computed`/`effect`, reactive collections) |
173
+ | `@oasys/oecs/reactive-sync` | ECS→reactive bridge — publishes only dirty entities/columns |
174
+ | `@oasys/oecs/editor` | undo/redo + field-handle layer over the host-write seam |
175
+ | `@oasys/oecs/solid` | SolidJS adapter (`solid-js` is an **optional** peer dependency) |
176
+ | `@oasys/oecs/primitives` | the standalone data structures oecs is built on |
177
+
178
+ ## Dev vs prod
179
+
180
+ A compile-time `__DEV__` flag gates every runtime check — bounds and liveness checks, duplicate-system
181
+ detection, registration validation, and the system access checker (`reads`/`writes`). These are
182
+ **tree-shaken out of production builds**, so treat "throws in dev" as a development tripwire, not a
183
+ production guarantee. The scheduler's cycle detection is the one check that is always active.
184
+
185
+ ## Documentation
186
+
187
+ - **New to oecs?** Start with the [Getting Started](docs/GETTING_STARTED.md) tutorial, then
188
+ [Best Practices](docs/BEST_PRACTICES.md) and the [Architecture](docs/ARCHITECTURE.md) overview.
189
+ - **Using optional extensions?** See the [Extensions guide](docs/EXTENSIONS.md) for reactive UI,
190
+ editor, Solid, shared-memory, and primitives usage.
191
+ - **Upgrading from 0.4?** See the [Migration guide (0.4 → 0.5)](docs/MIGRATION-0.4-to-0.5.md) and the
192
+ [CHANGELOG](CHANGELOG.md).
193
+ - **Upgrading from 0.3?** See the [Migration guide (0.3 → 0.4)](docs/MIGRATION-0.3-to-0.4.md).
194
+ - **Full API reference** — start at the [reference index](docs/api/index.md):
287
195
  [components](docs/api/components.md) ·
288
196
  [entities](docs/api/entities.md) ·
289
197
  [queries](docs/api/queries.md) ·
@@ -293,8 +201,40 @@ pnpm tsc --noEmit # type check
293
201
  [events](docs/api/events.md) ·
294
202
  [refs](docs/api/refs.md) ·
295
203
  [change detection](docs/api/change-detection.md) ·
296
- [type primitives](docs/api/type-primitives.md)
204
+ [observers](docs/api/observers.md) ·
205
+ [relations](docs/api/relations.md) ·
206
+ [sparse storage](docs/api/sparse-storage.md) ·
207
+ [determinism](docs/api/determinism.md) ·
208
+ [memory](docs/api/memory.md) ·
209
+ [WASM backends](docs/api/wasm.md) ·
210
+ [parallelism](docs/api/parallel.md) ·
211
+ [host-write seam](docs/api/host-write-seam.md) ·
212
+ [reactive](docs/api/reactive.md) ·
213
+ [editor](docs/api/editor.md) ·
214
+ [tracing](docs/api/tracing.md) ·
215
+ [primitives](docs/api/primitives.md) ·
216
+ [errors](docs/api/errors.md)
217
+
218
+ ## Development
219
+
220
+ ```bash
221
+ pnpm install
222
+ pnpm test # vitest
223
+ pnpm bench # vitest bench
224
+ pnpm build # vite library build (multi-entry → dist/)
225
+ pnpm exec tsc --noEmit # type check
226
+ ```
227
+
228
+ ## Acknowledgements
229
+
230
+ oecs stands on the shoulders of the ECS community. Special thanks to:
231
+
232
+ - **[Bevy](https://bevyengine.org)**, **[Flecs](https://github.com/SanderMertens/flecs)**, and
233
+ **[bitECS](https://github.com/NateTheGreatt/bitECS)** — a constant source of inspiration; their
234
+ designs shaped how oecs approaches archetypes, relations, scheduling, and change detection.
235
+ - **[@clinuxrulz](https://github.com/clinuxrulz)** — for his amazing showcase and invaluable input on
236
+ the ECS.
297
237
 
298
238
  ## License
299
239
 
300
- MIT
240
+ [MIT](LICENSE)
@@ -0,0 +1,77 @@
1
+ import { ComponentDef, ComponentHandle } from './component.cjs';
2
+ import { SparseComponentDef } from './sparse_store.cjs';
3
+ import { RelationDef } from './relation.cjs';
4
+ import { ResourceKey } from './resource.cjs';
5
+ import { SystemDescriptor } from './system.cjs';
6
+ interface AccessSets {
7
+ reads: Set<number>;
8
+ writes: Set<number>;
9
+ addAllowed: Set<number>;
10
+ removeAllowed: Set<number>;
11
+ hasDespawns: boolean;
12
+ resourceReads: Set<symbol>;
13
+ resourceWrites: Set<symbol>;
14
+ sparseReads: Set<number>;
15
+ sparseWrites: Set<number>;
16
+ relationReads: Set<number>;
17
+ relationWrites: Set<number>;
18
+ }
19
+ declare function setsFor(desc: SystemDescriptor): AccessSets;
20
+ /** The reads-only access surface a run condition declares (#576). A condition
21
+ * can only `reads` components (via a captured query) and `resourceReads`; every
22
+ * mutation set is empty by construction, so the same `check_*` machinery rejects
23
+ * any write/structural/resource-write a misbehaving predicate attempts. */
24
+ interface ConditionAccess {
25
+ readonly name: string;
26
+ readonly reads?: readonly ComponentDef[];
27
+ readonly resourceReads?: readonly ResourceKey<any>[];
28
+ }
29
+ declare class AccessCheck {
30
+ private active;
31
+ private sets;
32
+ private activeName;
33
+ private exclusive;
34
+ enter(desc: SystemDescriptor): void;
35
+ /** Open a reads-only span for a run condition (#576). No descriptor — a
36
+ * condition can gate a whole SystemSet, so it isn't attributable to one
37
+ * system — just its declared reads/resource_reads and a name for diagnostics.
38
+ * Paired with `leave()`. */
39
+ enterCondition(cond: ConditionAccess): void;
40
+ leave(): void;
41
+ isActive(): boolean;
42
+ /** Current system descriptor, if any. Null during a run-condition span. */
43
+ current(): SystemDescriptor | null;
44
+ checkRead(def: ComponentHandle): void;
45
+ checkWrite(def: ComponentHandle): void;
46
+ checkAdd(def: ComponentHandle): void;
47
+ checkRemove(def: ComponentHandle): void;
48
+ checkDestroy(): void;
49
+ checkResourceRead(key: ResourceKey<any>): void;
50
+ checkResourceWrite(key: ResourceKey<any>): void;
51
+ checkSparseRead(def: SparseComponentDef): void;
52
+ checkSparseWrite(def: SparseComponentDef): void;
53
+ checkRelationRead(def: RelationDef): void;
54
+ checkRelationWrite(def: RelationDef): void;
55
+ /** A `(*, T)` wildcard (`Query.forEachRelatedTo`, #579) reads every
56
+ * relation's reverse index, so it can't name a specific relation — it is
57
+ * authorised by the `ANY_RELATION` sentinel in `relationReads`. Honoured here
58
+ * exactly like a per-relation read, just keyed on the reserved sentinel id
59
+ * (which computeSets folds into `relationReads` like any other entry). */
60
+ checkRelationReadAny(): void;
61
+ private optionalScopes;
62
+ enterOptionalScope(optional: readonly number[]): void;
63
+ leaveOptionalScope(): void;
64
+ checkOptionalFetch(def: ComponentHandle): void;
65
+ private failComponent;
66
+ private failSparse;
67
+ private failRelation;
68
+ private failResource;
69
+ }
70
+ export declare const accessCheck: AccessCheck;
71
+ /** @internal — test seam for unit tests that need a fresh tracker. */
72
+ export declare const _accessCheckInternals: {
73
+ create: () => AccessCheck;
74
+ setsFor: typeof setsFor;
75
+ };
76
+ export {};
77
+ //# sourceMappingURL=access_check.d.ts.map
@@ -0,0 +1,77 @@
1
+ import { ComponentDef, ComponentHandle } from './component.js';
2
+ import { SparseComponentDef } from './sparse_store.js';
3
+ import { RelationDef } from './relation.js';
4
+ import { ResourceKey } from './resource.js';
5
+ import { SystemDescriptor } from './system.js';
6
+ interface AccessSets {
7
+ reads: Set<number>;
8
+ writes: Set<number>;
9
+ addAllowed: Set<number>;
10
+ removeAllowed: Set<number>;
11
+ hasDespawns: boolean;
12
+ resourceReads: Set<symbol>;
13
+ resourceWrites: Set<symbol>;
14
+ sparseReads: Set<number>;
15
+ sparseWrites: Set<number>;
16
+ relationReads: Set<number>;
17
+ relationWrites: Set<number>;
18
+ }
19
+ declare function setsFor(desc: SystemDescriptor): AccessSets;
20
+ /** The reads-only access surface a run condition declares (#576). A condition
21
+ * can only `reads` components (via a captured query) and `resourceReads`; every
22
+ * mutation set is empty by construction, so the same `check_*` machinery rejects
23
+ * any write/structural/resource-write a misbehaving predicate attempts. */
24
+ interface ConditionAccess {
25
+ readonly name: string;
26
+ readonly reads?: readonly ComponentDef[];
27
+ readonly resourceReads?: readonly ResourceKey<any>[];
28
+ }
29
+ declare class AccessCheck {
30
+ private active;
31
+ private sets;
32
+ private activeName;
33
+ private exclusive;
34
+ enter(desc: SystemDescriptor): void;
35
+ /** Open a reads-only span for a run condition (#576). No descriptor — a
36
+ * condition can gate a whole SystemSet, so it isn't attributable to one
37
+ * system — just its declared reads/resource_reads and a name for diagnostics.
38
+ * Paired with `leave()`. */
39
+ enterCondition(cond: ConditionAccess): void;
40
+ leave(): void;
41
+ isActive(): boolean;
42
+ /** Current system descriptor, if any. Null during a run-condition span. */
43
+ current(): SystemDescriptor | null;
44
+ checkRead(def: ComponentHandle): void;
45
+ checkWrite(def: ComponentHandle): void;
46
+ checkAdd(def: ComponentHandle): void;
47
+ checkRemove(def: ComponentHandle): void;
48
+ checkDestroy(): void;
49
+ checkResourceRead(key: ResourceKey<any>): void;
50
+ checkResourceWrite(key: ResourceKey<any>): void;
51
+ checkSparseRead(def: SparseComponentDef): void;
52
+ checkSparseWrite(def: SparseComponentDef): void;
53
+ checkRelationRead(def: RelationDef): void;
54
+ checkRelationWrite(def: RelationDef): void;
55
+ /** A `(*, T)` wildcard (`Query.forEachRelatedTo`, #579) reads every
56
+ * relation's reverse index, so it can't name a specific relation — it is
57
+ * authorised by the `ANY_RELATION` sentinel in `relationReads`. Honoured here
58
+ * exactly like a per-relation read, just keyed on the reserved sentinel id
59
+ * (which computeSets folds into `relationReads` like any other entry). */
60
+ checkRelationReadAny(): void;
61
+ private optionalScopes;
62
+ enterOptionalScope(optional: readonly number[]): void;
63
+ leaveOptionalScope(): void;
64
+ checkOptionalFetch(def: ComponentHandle): void;
65
+ private failComponent;
66
+ private failSparse;
67
+ private failRelation;
68
+ private failResource;
69
+ }
70
+ export declare const accessCheck: AccessCheck;
71
+ /** @internal — test seam for unit tests that need a fresh tracker. */
72
+ export declare const _accessCheckInternals: {
73
+ create: () => AccessCheck;
74
+ setsFor: typeof setsFor;
75
+ };
76
+ export {};
77
+ //# sourceMappingURL=access_check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access_check.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/access_check.ts"],"names":[],"mappings":"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"}