@forgeax/engine-ecs 0.0.0-dev.8d955ade1c79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +278 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__fixtures__/field-shape-fixture.d.ts +16 -0
  5. package/dist/__fixtures__/field-shape-fixture.d.ts.map +1 -0
  6. package/dist/__tests__/__fixtures__/inspect-scripts.snapshot.d.ts +3 -0
  7. package/dist/__tests__/__fixtures__/inspect-scripts.snapshot.d.ts.map +1 -0
  8. package/dist/__tests__/archetype.unit.test.d.ts +2 -0
  9. package/dist/__tests__/archetype.unit.test.d.ts.map +1 -0
  10. package/dist/__tests__/array-handle-element.test-d.d.ts +2 -0
  11. package/dist/__tests__/array-handle-element.test-d.d.ts.map +1 -0
  12. package/dist/__tests__/buffer-array-vocab.test-d.d.ts +2 -0
  13. package/dist/__tests__/buffer-array-vocab.test-d.d.ts.map +1 -0
  14. package/dist/__tests__/buffer-field-input-widen.unit.test.d.ts +2 -0
  15. package/dist/__tests__/buffer-field-input-widen.unit.test.d.ts.map +1 -0
  16. package/dist/__tests__/buffer-pool.test-d.d.ts +2 -0
  17. package/dist/__tests__/buffer-pool.test-d.d.ts.map +1 -0
  18. package/dist/__tests__/change-detection.unit.test.d.ts +2 -0
  19. package/dist/__tests__/change-detection.unit.test.d.ts.map +1 -0
  20. package/dist/__tests__/command-buffer.test.d.ts +2 -0
  21. package/dist/__tests__/command-buffer.test.d.ts.map +1 -0
  22. package/dist/__tests__/commands-deferred.unit.test.d.ts +2 -0
  23. package/dist/__tests__/commands-deferred.unit.test.d.ts.map +1 -0
  24. package/dist/__tests__/component-catalog.test.d.ts +2 -0
  25. package/dist/__tests__/component-catalog.test.d.ts.map +1 -0
  26. package/dist/__tests__/component-definition-identity.unit.test.d.ts +2 -0
  27. package/dist/__tests__/component-definition-identity.unit.test.d.ts.map +1 -0
  28. package/dist/__tests__/component-nan-preflight.unit.test.d.ts +2 -0
  29. package/dist/__tests__/component-nan-preflight.unit.test.d.ts.map +1 -0
  30. package/dist/__tests__/component-schema.test-d.d.ts +2 -0
  31. package/dist/__tests__/component-schema.test-d.d.ts.map +1 -0
  32. package/dist/__tests__/component-schema.test.d.ts +2 -0
  33. package/dist/__tests__/component-schema.test.d.ts.map +1 -0
  34. package/dist/__tests__/define-component-options.test-d.d.ts +2 -0
  35. package/dist/__tests__/define-component-options.test-d.d.ts.map +1 -0
  36. package/dist/__tests__/deprecated-vocab-rejection.unit.test.d.ts +2 -0
  37. package/dist/__tests__/deprecated-vocab-rejection.unit.test.d.ts.map +1 -0
  38. package/dist/__tests__/despawn-all.unit.test.d.ts +2 -0
  39. package/dist/__tests__/despawn-all.unit.test.d.ts.map +1 -0
  40. package/dist/__tests__/ecs-core-reduction.characterization.test.d.ts +2 -0
  41. package/dist/__tests__/ecs-core-reduction.characterization.test.d.ts.map +1 -0
  42. package/dist/__tests__/ecs-core-reduction.perf.test.d.ts +2 -0
  43. package/dist/__tests__/ecs-core-reduction.perf.test.d.ts.map +1 -0
  44. package/dist/__tests__/ecs-error-code-31.test-d.d.ts +2 -0
  45. package/dist/__tests__/ecs-error-code-31.test-d.d.ts.map +1 -0
  46. package/dist/__tests__/entity-disabling.unit.test.d.ts +2 -0
  47. package/dist/__tests__/entity-disabling.unit.test.d.ts.map +1 -0
  48. package/dist/__tests__/entity-handle-codec-parity.unit.test.d.ts +2 -0
  49. package/dist/__tests__/entity-handle-codec-parity.unit.test.d.ts.map +1 -0
  50. package/dist/__tests__/errors.unit.test.d.ts +2 -0
  51. package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
  52. package/dist/__tests__/execution-conflict-boundary.unit.test.d.ts +2 -0
  53. package/dist/__tests__/execution-conflict-boundary.unit.test.d.ts.map +1 -0
  54. package/dist/__tests__/execution-shared-storage.unit.test.d.ts +2 -0
  55. package/dist/__tests__/execution-shared-storage.unit.test.d.ts.map +1 -0
  56. package/dist/__tests__/externalization-portable-value.unit.test.d.ts +2 -0
  57. package/dist/__tests__/externalization-portable-value.unit.test.d.ts.map +1 -0
  58. package/dist/__tests__/externalization-projection.unit.test.d.ts +2 -0
  59. package/dist/__tests__/externalization-projection.unit.test.d.ts.map +1 -0
  60. package/dist/__tests__/externalization-render-read-lease.test-d.d.ts +2 -0
  61. package/dist/__tests__/externalization-render-read-lease.test-d.d.ts.map +1 -0
  62. package/dist/__tests__/externalization-render-read-lease.unit.test.d.ts +2 -0
  63. package/dist/__tests__/externalization-render-read-lease.unit.test.d.ts.map +1 -0
  64. package/dist/__tests__/externalization-root-surface.unit.test.d.ts +2 -0
  65. package/dist/__tests__/externalization-root-surface.unit.test.d.ts.map +1 -0
  66. package/dist/__tests__/field-enum-labels-reflection.test.d.ts +2 -0
  67. package/dist/__tests__/field-enum-labels-reflection.test.d.ts.map +1 -0
  68. package/dist/__tests__/field-shape-fixture.test.d.ts +2 -0
  69. package/dist/__tests__/field-shape-fixture.test.d.ts.map +1 -0
  70. package/dist/__tests__/field-transient-reflection.test.d.ts +2 -0
  71. package/dist/__tests__/field-transient-reflection.test.d.ts.map +1 -0
  72. package/dist/__tests__/field-value-type.test-d.d.ts +2 -0
  73. package/dist/__tests__/field-value-type.test-d.d.ts.map +1 -0
  74. package/dist/__tests__/fixed-update.unit.test.d.ts +2 -0
  75. package/dist/__tests__/fixed-update.unit.test.d.ts.map +1 -0
  76. package/dist/__tests__/handle.test-d.d.ts +2 -0
  77. package/dist/__tests__/handle.test-d.d.ts.map +1 -0
  78. package/dist/__tests__/hierarchy.unit.test.d.ts +2 -0
  79. package/dist/__tests__/hierarchy.unit.test.d.ts.map +1 -0
  80. package/dist/__tests__/managed-array-vocab.test-d.d.ts +2 -0
  81. package/dist/__tests__/managed-array-vocab.test-d.d.ts.map +1 -0
  82. package/dist/__tests__/minimal-example.test-d.d.ts +2 -0
  83. package/dist/__tests__/minimal-example.test-d.d.ts.map +1 -0
  84. package/dist/__tests__/name-read-types.test-d.d.ts +2 -0
  85. package/dist/__tests__/name-read-types.test-d.d.ts.map +1 -0
  86. package/dist/__tests__/plugin-service.test.d.ts +2 -0
  87. package/dist/__tests__/plugin-service.test.d.ts.map +1 -0
  88. package/dist/__tests__/query-combinations.unit.test.d.ts +2 -0
  89. package/dist/__tests__/query-combinations.unit.test.d.ts.map +1 -0
  90. package/dist/__tests__/query-light-extract-flat-column-ratio.perf.test.d.ts +2 -0
  91. package/dist/__tests__/query-light-extract-flat-column-ratio.perf.test.d.ts.map +1 -0
  92. package/dist/__tests__/query-storage-trends.perf.test.d.ts +2 -0
  93. package/dist/__tests__/query-storage-trends.perf.test.d.ts.map +1 -0
  94. package/dist/__tests__/query-trs-flat-column-ratio.perf.test.d.ts +2 -0
  95. package/dist/__tests__/query-trs-flat-column-ratio.perf.test.d.ts.map +1 -0
  96. package/dist/__tests__/query.unit.test.d.ts +2 -0
  97. package/dist/__tests__/query.unit.test.d.ts.map +1 -0
  98. package/dist/__tests__/relationship-index.perf.test.d.ts +2 -0
  99. package/dist/__tests__/relationship-index.perf.test.d.ts.map +1 -0
  100. package/dist/__tests__/relationship-index.test.d.ts +2 -0
  101. package/dist/__tests__/relationship-index.test.d.ts.map +1 -0
  102. package/dist/__tests__/scalar-field-type-owner.test-d.d.ts +2 -0
  103. package/dist/__tests__/scalar-field-type-owner.test-d.d.ts.map +1 -0
  104. package/dist/__tests__/schedule-determinism.unit.test.d.ts +2 -0
  105. package/dist/__tests__/schedule-determinism.unit.test.d.ts.map +1 -0
  106. package/dist/__tests__/schedule-inspection.unit.test.d.ts +2 -0
  107. package/dist/__tests__/schedule-inspection.unit.test.d.ts.map +1 -0
  108. package/dist/__tests__/schedule-scope-error.unit.test.d.ts +2 -0
  109. package/dist/__tests__/schedule-scope-error.unit.test.d.ts.map +1 -0
  110. package/dist/__tests__/schedule-token.unit.test.d.ts +2 -0
  111. package/dist/__tests__/schedule-token.unit.test.d.ts.map +1 -0
  112. package/dist/__tests__/schema-vocab.test-d.d.ts +2 -0
  113. package/dist/__tests__/schema-vocab.test-d.d.ts.map +1 -0
  114. package/dist/__tests__/serialization.unit.test.d.ts +2 -0
  115. package/dist/__tests__/serialization.unit.test.d.ts.map +1 -0
  116. package/dist/__tests__/shared-kernel-eligibility.test-d.d.ts +2 -0
  117. package/dist/__tests__/shared-kernel-eligibility.test-d.d.ts.map +1 -0
  118. package/dist/__tests__/shared-kernel-eligibility.unit.test.d.ts +2 -0
  119. package/dist/__tests__/shared-kernel-eligibility.unit.test.d.ts.map +1 -0
  120. package/dist/__tests__/shared-ref-store.unit.test.d.ts +2 -0
  121. package/dist/__tests__/shared-ref-store.unit.test.d.ts.map +1 -0
  122. package/dist/__tests__/sparse-tag.unit.test.d.ts +2 -0
  123. package/dist/__tests__/sparse-tag.unit.test.d.ts.map +1 -0
  124. package/dist/__tests__/spawn-field-error.test-d.d.ts +2 -0
  125. package/dist/__tests__/spawn-field-error.test-d.d.ts.map +1 -0
  126. package/dist/__tests__/stale-error.test-d.d.ts +2 -0
  127. package/dist/__tests__/stale-error.test-d.d.ts.map +1 -0
  128. package/dist/__tests__/string-view-symmetry.test-d.d.ts +2 -0
  129. package/dist/__tests__/string-view-symmetry.test-d.d.ts.map +1 -0
  130. package/dist/__tests__/string-vocab.test-d.d.ts +2 -0
  131. package/dist/__tests__/string-vocab.test-d.d.ts.map +1 -0
  132. package/dist/__tests__/structure-epoch-boundaries.unit.test.d.ts +2 -0
  133. package/dist/__tests__/structure-epoch-boundaries.unit.test.d.ts.map +1 -0
  134. package/dist/__tests__/structure-epoch.unit.test.d.ts +2 -0
  135. package/dist/__tests__/structure-epoch.unit.test.d.ts.map +1 -0
  136. package/dist/__tests__/table-archetype-mapping.unit.test.d.ts +2 -0
  137. package/dist/__tests__/table-archetype-mapping.unit.test.d.ts.map +1 -0
  138. package/dist/__tests__/time.unit.test.d.ts +2 -0
  139. package/dist/__tests__/time.unit.test.d.ts.map +1 -0
  140. package/dist/__tests__/types.test-d.d.ts +2 -0
  141. package/dist/__tests__/types.test-d.d.ts.map +1 -0
  142. package/dist/__tests__/unique-ref-store.unit.test.d.ts +2 -0
  143. package/dist/__tests__/unique-ref-store.unit.test.d.ts.map +1 -0
  144. package/dist/__tests__/utils/handle-numeric.d.ts +14 -0
  145. package/dist/__tests__/utils/handle-numeric.d.ts.map +1 -0
  146. package/dist/__tests__/validation-root-surface.unit.test.d.ts +2 -0
  147. package/dist/__tests__/validation-root-surface.unit.test.d.ts.map +1 -0
  148. package/dist/__tests__/visibility-atomic-write.unit.test.d.ts +2 -0
  149. package/dist/__tests__/visibility-atomic-write.unit.test.d.ts.map +1 -0
  150. package/dist/__tests__/world-change-journal.unit.test.d.ts +2 -0
  151. package/dist/__tests__/world-change-journal.unit.test.d.ts.map +1 -0
  152. package/dist/__tests__/world-characterization.unit.test.d.ts +2 -0
  153. package/dist/__tests__/world-characterization.unit.test.d.ts.map +1 -0
  154. package/dist/__tests__/world-consumer-inference.test-d.d.ts +2 -0
  155. package/dist/__tests__/world-consumer-inference.test-d.d.ts.map +1 -0
  156. package/dist/__tests__/world-health.contract.test.d.ts +2 -0
  157. package/dist/__tests__/world-health.contract.test.d.ts.map +1 -0
  158. package/dist/__tests__/world-inspection.test.d.ts +2 -0
  159. package/dist/__tests__/world-inspection.test.d.ts.map +1 -0
  160. package/dist/__tests__/world-internal-identity.unit.test.d.ts +2 -0
  161. package/dist/__tests__/world-internal-identity.unit.test.d.ts.map +1 -0
  162. package/dist/__tests__/world-managed-roundtrip.unit.test.d.ts +2 -0
  163. package/dist/__tests__/world-managed-roundtrip.unit.test.d.ts.map +1 -0
  164. package/dist/__tests__/world-scheduling.m4.test.d.ts +2 -0
  165. package/dist/__tests__/world-scheduling.m4.test.d.ts.map +1 -0
  166. package/dist/__tests__/world-unique-roundtrip.unit.test.d.ts +2 -0
  167. package/dist/__tests__/world-unique-roundtrip.unit.test.d.ts.map +1 -0
  168. package/dist/__tests__/world.test-d.d.ts +2 -0
  169. package/dist/__tests__/world.test-d.d.ts.map +1 -0
  170. package/dist/buffer-pool.d.ts +109 -0
  171. package/dist/buffer-pool.d.ts.map +1 -0
  172. package/dist/commands.d.ts +93 -0
  173. package/dist/commands.d.ts.map +1 -0
  174. package/dist/component-default-fallback.d.ts +88 -0
  175. package/dist/component-default-fallback.d.ts.map +1 -0
  176. package/dist/component-schema.d.ts +34 -0
  177. package/dist/component-schema.d.ts.map +1 -0
  178. package/dist/component-value-validate.d.ts +29 -0
  179. package/dist/component-value-validate.d.ts.map +1 -0
  180. package/dist/component.d.ts +581 -0
  181. package/dist/component.d.ts.map +1 -0
  182. package/dist/entity-handle.d.ts +68 -0
  183. package/dist/entity-handle.d.ts.map +1 -0
  184. package/dist/entity.d.ts +86 -0
  185. package/dist/entity.d.ts.map +1 -0
  186. package/dist/errors/query-and-component-errors.d.ts +132 -0
  187. package/dist/errors/query-and-component-errors.d.ts.map +1 -0
  188. package/dist/errors/relationship-errors.d.ts +112 -0
  189. package/dist/errors/relationship-errors.d.ts.map +1 -0
  190. package/dist/errors/sprite-and-shared-errors.d.ts +107 -0
  191. package/dist/errors/sprite-and-shared-errors.d.ts.map +1 -0
  192. package/dist/errors/validation-errors.d.ts +229 -0
  193. package/dist/errors/validation-errors.d.ts.map +1 -0
  194. package/dist/errors.d.ts +763 -0
  195. package/dist/errors.d.ts.map +1 -0
  196. package/dist/execution/shared-kernel.d.ts +106 -0
  197. package/dist/execution/shared-kernel.d.ts.map +1 -0
  198. package/dist/externalization/index.d.ts +31 -0
  199. package/dist/externalization/index.d.ts.map +1 -0
  200. package/dist/externalization/index.mjs +298 -0
  201. package/dist/externalization/index.mjs.map +1 -0
  202. package/dist/index.d.ts +261 -0
  203. package/dist/index.d.ts.map +1 -0
  204. package/dist/index.mjs +7422 -0
  205. package/dist/index.mjs.map +1 -0
  206. package/dist/internal.mjs +479 -0
  207. package/dist/internal.mjs.map +1 -0
  208. package/dist/plugin-service.d.ts +11 -0
  209. package/dist/plugin-service.d.ts.map +1 -0
  210. package/dist/projection/index.d.ts +103 -0
  211. package/dist/projection/index.d.ts.map +1 -0
  212. package/dist/projection/index.mjs +615 -0
  213. package/dist/projection/index.mjs.map +1 -0
  214. package/dist/query/query.d.ts +58 -0
  215. package/dist/query/query.d.ts.map +1 -0
  216. package/dist/relationship-index.d.ts +81 -0
  217. package/dist/relationship-index.d.ts.map +1 -0
  218. package/dist/resource.d.ts +70 -0
  219. package/dist/resource.d.ts.map +1 -0
  220. package/dist/schedule-token.d.ts +16 -0
  221. package/dist/schedule-token.d.ts.map +1 -0
  222. package/dist/schedule.d.ts +271 -0
  223. package/dist/schedule.d.ts.map +1 -0
  224. package/dist/shared-ref-store.d.ts +144 -0
  225. package/dist/shared-ref-store.d.ts.map +1 -0
  226. package/dist/shared.d.ts +3 -0
  227. package/dist/shared.d.ts.map +1 -0
  228. package/dist/shared.mjs +364 -0
  229. package/dist/shared.mjs.map +1 -0
  230. package/dist/storage/archetype-graph.d.ts +54 -0
  231. package/dist/storage/archetype-graph.d.ts.map +1 -0
  232. package/dist/storage/archetype.d.ts +23 -0
  233. package/dist/storage/archetype.d.ts.map +1 -0
  234. package/dist/storage/change-detection.d.ts +40 -0
  235. package/dist/storage/change-detection.d.ts.map +1 -0
  236. package/dist/storage/column.d.ts +111 -0
  237. package/dist/storage/column.d.ts.map +1 -0
  238. package/dist/storage/table.d.ts +29 -0
  239. package/dist/storage/table.d.ts.map +1 -0
  240. package/dist/time.d.ts +57 -0
  241. package/dist/time.d.ts.map +1 -0
  242. package/dist/unique-ref-store.d.ts +105 -0
  243. package/dist/unique-ref-store.d.ts.map +1 -0
  244. package/dist/world-change-journal.d.ts +34 -0
  245. package/dist/world-change-journal.d.ts.map +1 -0
  246. package/dist/world-component-access.d.ts +265 -0
  247. package/dist/world-component-access.d.ts.map +1 -0
  248. package/dist/world-component-storage.d.ts +282 -0
  249. package/dist/world-component-storage.d.ts.map +1 -0
  250. package/dist/world-core.d.ts +39 -0
  251. package/dist/world-core.d.ts.map +1 -0
  252. package/dist/world-entity-lifecycle.d.ts +30 -0
  253. package/dist/world-entity-lifecycle.d.ts.map +1 -0
  254. package/dist/world-internal.d.ts +14 -0
  255. package/dist/world-internal.d.ts.map +1 -0
  256. package/dist/world-scheduling.d.ts +30 -0
  257. package/dist/world-scheduling.d.ts.map +1 -0
  258. package/dist/world.d.ts +620 -0
  259. package/dist/world.d.ts.map +1 -0
  260. package/package.json +77 -0
  261. package/src/__fixtures__/field-shape-fixture.ts +30 -0
  262. package/src/__tests__/__fixtures__/inspect-scripts.snapshot.ts +2 -0
  263. package/src/__tests__/archetype.unit.test.ts +1837 -0
  264. package/src/__tests__/array-handle-element.test-d.ts +34 -0
  265. package/src/__tests__/buffer-array-vocab.test-d.ts +53 -0
  266. package/src/__tests__/buffer-field-input-widen.unit.test.ts +99 -0
  267. package/src/__tests__/buffer-pool.test-d.ts +32 -0
  268. package/src/__tests__/change-detection.unit.test.ts +86 -0
  269. package/src/__tests__/command-buffer.test.ts +179 -0
  270. package/src/__tests__/commands-deferred.unit.test.ts +68 -0
  271. package/src/__tests__/component-catalog.test.ts +59 -0
  272. package/src/__tests__/component-definition-identity.unit.test.ts +24 -0
  273. package/src/__tests__/component-nan-preflight.unit.test.ts +186 -0
  274. package/src/__tests__/component-schema.test-d.ts +119 -0
  275. package/src/__tests__/component-schema.test.ts +55 -0
  276. package/src/__tests__/define-component-options.test-d.ts +35 -0
  277. package/src/__tests__/deprecated-vocab-rejection.unit.test.ts +59 -0
  278. package/src/__tests__/despawn-all.unit.test.ts +17 -0
  279. package/src/__tests__/ecs-core-reduction.characterization.test.ts +130 -0
  280. package/src/__tests__/ecs-core-reduction.perf.test.ts +260 -0
  281. package/src/__tests__/ecs-error-code-31.test-d.ts +42 -0
  282. package/src/__tests__/entity-disabling.unit.test.ts +37 -0
  283. package/src/__tests__/entity-handle-codec-parity.unit.test.ts +126 -0
  284. package/src/__tests__/errors.unit.test.ts +1456 -0
  285. package/src/__tests__/execution-conflict-boundary.unit.test.ts +123 -0
  286. package/src/__tests__/execution-shared-storage.unit.test.ts +40 -0
  287. package/src/__tests__/externalization-portable-value.unit.test.ts +275 -0
  288. package/src/__tests__/externalization-projection.unit.test.ts +325 -0
  289. package/src/__tests__/externalization-render-read-lease.test-d.ts +30 -0
  290. package/src/__tests__/externalization-render-read-lease.unit.test.ts +79 -0
  291. package/src/__tests__/externalization-root-surface.unit.test.ts +33 -0
  292. package/src/__tests__/field-enum-labels-reflection.test.ts +66 -0
  293. package/src/__tests__/field-shape-fixture.test.ts +61 -0
  294. package/src/__tests__/field-transient-reflection.test.ts +59 -0
  295. package/src/__tests__/field-value-type.test-d.ts +57 -0
  296. package/src/__tests__/fixed-update.unit.test.ts +78 -0
  297. package/src/__tests__/handle.test-d.ts +86 -0
  298. package/src/__tests__/hierarchy.unit.test.ts +2534 -0
  299. package/src/__tests__/managed-array-vocab.test-d.ts +137 -0
  300. package/src/__tests__/minimal-example.test-d.ts +42 -0
  301. package/src/__tests__/name-read-types.test-d.ts +72 -0
  302. package/src/__tests__/plugin-service.test.ts +13 -0
  303. package/src/__tests__/query-combinations.unit.test.ts +42 -0
  304. package/src/__tests__/query-light-extract-flat-column-ratio.perf.test.ts +252 -0
  305. package/src/__tests__/query-storage-trends.perf.test.ts +104 -0
  306. package/src/__tests__/query-trs-flat-column-ratio.perf.test.ts +194 -0
  307. package/src/__tests__/query.unit.test.ts +196 -0
  308. package/src/__tests__/relationship-index.perf.test.ts +126 -0
  309. package/src/__tests__/relationship-index.test.ts +70 -0
  310. package/src/__tests__/scalar-field-type-owner.test-d.ts +71 -0
  311. package/src/__tests__/schedule-determinism.unit.test.ts +36 -0
  312. package/src/__tests__/schedule-inspection.unit.test.ts +81 -0
  313. package/src/__tests__/schedule-scope-error.unit.test.ts +63 -0
  314. package/src/__tests__/schedule-token.unit.test.ts +54 -0
  315. package/src/__tests__/schema-vocab.test-d.ts +103 -0
  316. package/src/__tests__/serialization.unit.test.ts +2740 -0
  317. package/src/__tests__/shared-kernel-eligibility.test-d.ts +18 -0
  318. package/src/__tests__/shared-kernel-eligibility.unit.test.ts +78 -0
  319. package/src/__tests__/shared-ref-store.unit.test.ts +646 -0
  320. package/src/__tests__/sparse-tag.unit.test.ts +201 -0
  321. package/src/__tests__/spawn-field-error.test-d.ts +41 -0
  322. package/src/__tests__/stale-error.test-d.ts +208 -0
  323. package/src/__tests__/string-view-symmetry.test-d.ts +26 -0
  324. package/src/__tests__/string-vocab.test-d.ts +49 -0
  325. package/src/__tests__/structure-epoch-boundaries.unit.test.ts +39 -0
  326. package/src/__tests__/structure-epoch.unit.test.ts +48 -0
  327. package/src/__tests__/table-archetype-mapping.unit.test.ts +78 -0
  328. package/src/__tests__/time.unit.test.ts +79 -0
  329. package/src/__tests__/types.test-d.ts +38 -0
  330. package/src/__tests__/unique-ref-store.unit.test.ts +348 -0
  331. package/src/__tests__/utils/handle-numeric.ts +30 -0
  332. package/src/__tests__/validation-root-surface.unit.test.ts +15 -0
  333. package/src/__tests__/visibility-atomic-write.unit.test.ts +82 -0
  334. package/src/__tests__/world-change-journal.unit.test.ts +97 -0
  335. package/src/__tests__/world-characterization.unit.test.ts +408 -0
  336. package/src/__tests__/world-consumer-inference.test-d.ts +16 -0
  337. package/src/__tests__/world-health.contract.test.ts +87 -0
  338. package/src/__tests__/world-inspection.test.ts +37 -0
  339. package/src/__tests__/world-internal-identity.unit.test.ts +17 -0
  340. package/src/__tests__/world-managed-roundtrip.unit.test.ts +98 -0
  341. package/src/__tests__/world-scheduling.m4.test.ts +52 -0
  342. package/src/__tests__/world-unique-roundtrip.unit.test.ts +357 -0
  343. package/src/__tests__/world.test-d.ts +156 -0
  344. package/src/buffer-pool.ts +357 -0
  345. package/src/commands.ts +644 -0
  346. package/src/component-default-fallback.ts +236 -0
  347. package/src/component-schema.ts +92 -0
  348. package/src/component-value-validate.ts +135 -0
  349. package/src/component.ts +1268 -0
  350. package/src/entity-handle.ts +104 -0
  351. package/src/entity.ts +140 -0
  352. package/src/errors/query-and-component-errors.ts +200 -0
  353. package/src/errors/relationship-errors.ts +198 -0
  354. package/src/errors/sprite-and-shared-errors.ts +231 -0
  355. package/src/errors/validation-errors.ts +506 -0
  356. package/src/errors.ts +1464 -0
  357. package/src/execution/shared-kernel.ts +392 -0
  358. package/src/externalization/index.ts +170 -0
  359. package/src/index.ts +414 -0
  360. package/src/plugin-service.ts +35 -0
  361. package/src/projection/index.ts +318 -0
  362. package/src/query/query.ts +606 -0
  363. package/src/relationship-index.ts +175 -0
  364. package/src/resource.ts +106 -0
  365. package/src/schedule-token.ts +40 -0
  366. package/src/schedule.ts +846 -0
  367. package/src/shared-ref-store.ts +418 -0
  368. package/src/shared.ts +23 -0
  369. package/src/storage/archetype-graph.ts +166 -0
  370. package/src/storage/archetype.ts +78 -0
  371. package/src/storage/change-detection.ts +231 -0
  372. package/src/storage/column.ts +247 -0
  373. package/src/storage/table.ts +153 -0
  374. package/src/time.ts +122 -0
  375. package/src/unique-ref-store.ts +278 -0
  376. package/src/world-change-journal.ts +84 -0
  377. package/src/world-component-access.ts +1421 -0
  378. package/src/world-component-storage.ts +1178 -0
  379. package/src/world-core.ts +74 -0
  380. package/src/world-entity-lifecycle.ts +539 -0
  381. package/src/world-internal.ts +75 -0
  382. package/src/world-scheduling.ts +499 -0
  383. package/src/world.ts +1399 -0
package/src/time.ts ADDED
@@ -0,0 +1,122 @@
1
+ export interface TimeResource {
2
+ readonly delta: number;
3
+ readonly elapsed: number;
4
+ readonly maxDeltaSeconds: number;
5
+ }
6
+
7
+ export interface FixedTimeResource {
8
+ readonly delta: number;
9
+ readonly maxStepsPerUpdate: number;
10
+ readonly tick: number;
11
+ /** Seconds accumulated toward the next fixed update. */
12
+ readonly overstep: number;
13
+ readonly droppedSeconds: number;
14
+ readonly droppedUpdates: number;
15
+ }
16
+
17
+ export type MutableTimeResource = { -readonly [K in keyof TimeResource]: TimeResource[K] };
18
+ export type MutableFixedTimeResource = {
19
+ -readonly [K in keyof FixedTimeResource]: FixedTimeResource[K];
20
+ };
21
+
22
+ /** Scheduler-only mutable clock capability; World never exposes this publicly. */
23
+ export interface ClockWriter {
24
+ readonly time: MutableTimeResource;
25
+ readonly fixed: MutableFixedTimeResource;
26
+ }
27
+
28
+ export interface WorldClock {
29
+ readonly time: TimeResource;
30
+ readonly fixed: FixedTimeResource;
31
+ readonly writer: ClockWriter;
32
+ }
33
+
34
+ interface ResourceToken<T> {
35
+ readonly name: string;
36
+ readonly __resourceType?: T;
37
+ }
38
+
39
+ export type ResourceValue<Key> = Key extends ResourceToken<infer Value> ? Value : never;
40
+
41
+ export interface TimePolicy {
42
+ readonly fixedDeltaSeconds?: number;
43
+ readonly maxStepsPerUpdate?: number;
44
+ readonly maxDeltaSeconds?: number;
45
+ }
46
+
47
+ export interface WorldOptions {
48
+ readonly time?: TimePolicy;
49
+ /** SAB-backed dense numeric columns for a SharedKernel-capable Engine Realm. */
50
+ readonly storage?: 'local' | 'shared';
51
+ }
52
+
53
+ /** World-owned variable-rate clock resource key. */
54
+ export const Time = Object.freeze({ name: 'Time' }) as ResourceToken<TimeResource> & TimeResource;
55
+ /** World-owned fixed-rate clock, policy, and catch-up metric resource key. */
56
+ export const FixedTime = Object.freeze({ name: 'FixedTime' }) as ResourceToken<FixedTimeResource> &
57
+ FixedTimeResource;
58
+
59
+ export const TIME_RESOURCE_KEY = Time.name;
60
+ export const FIXED_TIME_RESOURCE_KEY = FixedTime.name;
61
+
62
+ export const DEFAULT_TIME_POLICY: Required<TimePolicy> = {
63
+ fixedDeltaSeconds: 1 / 60,
64
+ maxStepsPerUpdate: 4,
65
+ maxDeltaSeconds: 0.1,
66
+ };
67
+
68
+ export function createTimeResource(policy: Required<TimePolicy>): TimeResource {
69
+ return { delta: 0, elapsed: 0, maxDeltaSeconds: policy.maxDeltaSeconds };
70
+ }
71
+
72
+ export function createFixedTimeResource(policy: Required<TimePolicy>): FixedTimeResource {
73
+ return {
74
+ delta: policy.fixedDeltaSeconds,
75
+ maxStepsPerUpdate: policy.maxStepsPerUpdate,
76
+ tick: 0,
77
+ overstep: 0,
78
+ droppedSeconds: 0,
79
+ droppedUpdates: 0,
80
+ };
81
+ }
82
+
83
+ export function createWorldClock(policy: Required<TimePolicy>): WorldClock {
84
+ const time = createTimeResource(policy) as MutableTimeResource;
85
+ const fixed = createFixedTimeResource(policy) as MutableFixedTimeResource;
86
+ const timeView: TimeResource = Object.freeze({
87
+ get delta() {
88
+ return time.delta;
89
+ },
90
+ get elapsed() {
91
+ return time.elapsed;
92
+ },
93
+ get maxDeltaSeconds() {
94
+ return time.maxDeltaSeconds;
95
+ },
96
+ });
97
+ const fixedView: FixedTimeResource = Object.freeze({
98
+ get delta() {
99
+ return fixed.delta;
100
+ },
101
+ get maxStepsPerUpdate() {
102
+ return fixed.maxStepsPerUpdate;
103
+ },
104
+ get tick() {
105
+ return fixed.tick;
106
+ },
107
+ get overstep() {
108
+ return fixed.overstep;
109
+ },
110
+ get droppedSeconds() {
111
+ return fixed.droppedSeconds;
112
+ },
113
+ get droppedUpdates() {
114
+ return fixed.droppedUpdates;
115
+ },
116
+ });
117
+ return {
118
+ time: timeView,
119
+ fixed: fixedView,
120
+ writer: { time, fixed },
121
+ };
122
+ }
@@ -0,0 +1,278 @@
1
+ // @forgeax/engine-ecs - UniqueRefStore.
2
+ //
3
+ // Per-slot singleton store for ECS-managed asset handles. Schema vocab
4
+ // `ref<T>` fields derive `Handle<T, 'unique'>` whose lifecycle is owned by
5
+ // the ECS - World hooks `despawn` / `removeComponent` / `set` into
6
+ // `UniqueRefStore.release` so dropping a holder entity reliably releases the
7
+ // referenced asset slot. AssetRegistry handles (`handle<T>` schema vocab) take
8
+ // the orthogonal `'shared'` mode and never enter this store.
9
+ //
10
+ // §contract — handles carry a generation; stale-by-reuse is detected
11
+ // The handle u32 packs `(generation << 24) | slot` via the shared codec in
12
+ // `@forgeax/engine-types` (the same SSOT codec ECS `EntityHandle` uses). The
13
+ // slot occupies the low 24 bits (capped at MAX_SLOT == ENTITY_MAX_INDEX);
14
+ // the generation occupies the high 8 bits. `alloc` welds the slot's current
15
+ // generation into the returned handle; `resolve` / `release` compare the
16
+ // handle's generation against the store's `_generations[slot]` BEFORE any
17
+ // payload work, so a handle whose slot was released and re-allocated is
18
+ // caught deterministically and returns `UniqueRefStaleError` ('unique-ref-
19
+ // stale') — it no longer silently resolves to the next payload.
20
+ //
21
+ // Generation advances on release and retires the slot when gen would exceed
22
+ // MAX_GEN (255) — gen 255 is still usable; the bump to 256 triggers retire —
23
+ // rather than wrapping, so a re-used slot can never collide with an old
24
+ // handle's generation (mirrors EntityHandle via the isRetiredSlot SSOT).
25
+ //
26
+ // AC-06 (unique-ref-double-release) remains payload-presence detected
27
+ // (`payloads.has(raw)`) for a handle whose generation still matches; the
28
+ // generation check catches the orthogonal stale-by-reuse case first.
29
+ //
30
+ // Storage shape:
31
+ // - `payloads: Map<number, unknown>` - key = handle u32 = slot index.
32
+ // Strong reference; deleted on release.
33
+ // Type-erased (the store never inspects
34
+ // payload fields); `Handle<T, 'unique'>`
35
+ // carries `T` at the type level and the
36
+ // single read site (World.get's ref path)
37
+ // narrows `unknown -> T` at the boundary.
38
+ // - `freeSlots: number[]` - LIFO stack of recyclable slot indices.
39
+ // - `releaseCallbacks: Map<...>` - per-slot onRelease cb cleared before fire.
40
+ // - `nextSlot` - bump counter for the never-recycled tail.
41
+ //
42
+ // 24 bits = 16_777_215 simultaneous live managed handles - the same ceiling
43
+ // as Entity, so an ECS that hit the entity ceiling cannot grow past the
44
+ // managed-ref ceiling either. Ceiling-exhaust handling is a future concern;
45
+ // the store deliberately panics on overflow to fail fast rather than
46
+ // silently wrap.
47
+ //
48
+ // Release path (D-1 codes):
49
+ // - resolve(h): err(UniqueRefReleasedError) if payload absent.
50
+ // - release(h): err(UniqueRefDoubleReleaseError) on second release.
51
+ //
52
+ // Object.is identity invariant (AC-04 prelude):
53
+ // resolve(h) returns the SAME payload object on every call until release
54
+ // - the store does not clone, wrap, or proxy the payload. M4 carry-over
55
+ // reuses this invariant: archetype migrate moves the row index, not the
56
+ // payload reference, so wrapper identity survives migration.
57
+
58
+ import type { Handle } from '@forgeax/engine-types';
59
+ import {
60
+ err,
61
+ handleGeneration,
62
+ handleSlot,
63
+ isRetiredSlot,
64
+ MAX_SLOT,
65
+ ok,
66
+ pack,
67
+ type Result,
68
+ toUnique,
69
+ unwrapHandle,
70
+ } from '@forgeax/engine-types';
71
+ import { UniqueRefDoubleReleaseError, UniqueRefReleasedError, UniqueRefStaleError } from './errors';
72
+
73
+ // MAX_SLOT is now imported from @forgeax/engine-types (codec SSOT, D-1).
74
+ // The local constant is removed to avoid drift (AC-15).
75
+
76
+ /**
77
+ * Per-slot singleton store for ECS-managed `Handle<T, 'unique'>` lifecycles.
78
+ * World hooks the three release paths (despawn, removeComponent, set) into
79
+ * `release(handle)`; `resolve(handle)` is the read path AI users reach
80
+ * indirectly through `world.get(e, C)` for `ref<T>` schema fields.
81
+ *
82
+ * §contract — handles carry a generation; stale-by-reuse is detected: the
83
+ * handle u32 packs `(gen << 24) | slot` via the shared codec; `resolve` /
84
+ * `release` compare the handle generation against `_generations[slot]` and
85
+ * return `UniqueRefStaleError` ('unique-ref-stale') when a released slot has
86
+ * been re-allocated, instead of silently resolving to the next payload.
87
+ * README §"Managed handles carry a generation" is the canonical AI-user-
88
+ * facing statement.
89
+ *
90
+ * Identity invariant (AC-04 prelude): the payload returned by `resolve` is
91
+ * the same object reference on every call until `release`. Carry-over (M4)
92
+ * relies on this - archetype migrate moves the row index, not the payload.
93
+ *
94
+ * Storage is type-erased (`unknown`): the store routes lifecycle without ever
95
+ * inspecting payload fields. Per-call payload typing flows through method
96
+ * generics: `alloc<T>` accepts a `T` payload + matching `onRelease(T)`, and
97
+ * `resolve<T>` returns `Result<T, ...>`. Both narrow at the call boundary
98
+ * (the single read site is World.get's ref path); making the class generic
99
+ * on `T` was decoration with no internal use, so the parameter was dropped
100
+ * (architecture-principles.md §1 SSOT).
101
+ */
102
+ export class UniqueRefStore {
103
+ private readonly payloads = new Map<number, unknown>();
104
+ private readonly freeSlots: number[] = [];
105
+ private readonly releaseCallbacks = new Map<number, ((payload: unknown) => void) | undefined>();
106
+ private nextSlot = 1;
107
+
108
+ /**
109
+ * Generation table indexed by slot (D-6). Each entry tracks the current
110
+ * generation for the slot — written to during alloc (welded into the
111
+ * returned handle via pack) and incremented on release (M4).
112
+ *
113
+ * Slot 0 is the sentinel and always has gen=0 — it is never allocated
114
+ * and never incremented (R6).
115
+ *
116
+ * @internal
117
+ */
118
+ // biome-ignore lint/style/useNamingConvention: internal field — @internal JSDoc suppresses lint:internal gate
119
+ private readonly _generations: number[] = [];
120
+
121
+ /**
122
+ * Allocate a fresh managed handle for `payload`, branded against `target`.
123
+ *
124
+ * Slot 0 is reserved as the "null/unset" sentinel - schema-vocab `ref<T>`
125
+ * fields default to this sentinel before the first write, and World's
126
+ * release loop uses it to short-circuit (`shouldRelease(0) === false`).
127
+ *
128
+ * The returned handle carries a generation tag welded via codec.pack
129
+ * (D-8, OOS-2): first allocation gen=0 (AC-06), reused slot gen = the
130
+ * current generation from _generations[slot]. The toUnique brand cast
131
+ * happens internally — external callers no longer construct Handle<...>
132
+ * directly.
133
+ *
134
+ * @returns a `Handle<T, 'unique'>` u32. The branded number is safe to
135
+ * widen to `number` for GPU upload (charter consistent abstraction).
136
+ */
137
+ alloc<Target extends string, T = unknown>(
138
+ target: Target,
139
+ payload: T,
140
+ onRelease?: (payload: T) => void,
141
+ ): Handle<Target, 'unique'> {
142
+ void target; // target is a phantom - tag flows only at the type level via Handle<Target,_>.
143
+ const slot = this.freeSlots.pop() ?? this.nextSlot++;
144
+ if (slot > MAX_SLOT) {
145
+ throw new RangeError(
146
+ `UniqueRefStore: slot index ${slot} exceeds 24-bit max (${MAX_SLOT}). ` +
147
+ 'Reduce simultaneous managed handles or investigate handle leaks.',
148
+ );
149
+ }
150
+ const gen = this._generations[slot] ?? 0;
151
+ const raw = pack(slot, gen);
152
+ this.payloads.set(raw, payload);
153
+ if (onRelease !== undefined) {
154
+ this.releaseCallbacks.set(raw, onRelease as (payload: unknown) => void);
155
+ }
156
+ return toUnique(raw);
157
+ }
158
+
159
+ /**
160
+ * Look up `payload` by handle. Returns `err(unique-ref-released)` when
161
+ * the handle's slot has no live payload (release happened in between, and
162
+ * no re-alloc has filled the slot). Resolves the same payload object on
163
+ * every call - identity-stable until release.
164
+ *
165
+ * §contract: a stale handle whose slot has been released and re-allocated
166
+ * returns `err(unique-ref-stale)` — the generation welded into the handle
167
+ * no longer matches `_generations[slot]`. The gen check runs before the
168
+ * payload lookup, so stale-by-reuse is caught deterministically — see
169
+ * README §"Managed handles carry a generation".
170
+ *
171
+ * `T` flows from the caller's `Handle<T, 'unique'>` type; the store erases
172
+ * payload types at the storage layer and the call boundary re-narrows.
173
+ */
174
+ resolve<Target extends string, T = unknown>(
175
+ handle: Handle<Target, 'unique'>,
176
+ ): Result<T, UniqueRefReleasedError | UniqueRefStaleError> {
177
+ const raw = unwrapHandle(handle);
178
+ // Gen comparison (M4): extract slot + handle gen, compare against
179
+ // store's current gen. Runs BEFORE payload lookup.
180
+ const slot = handleSlot(handle);
181
+ const handleGen = handleGeneration(handle);
182
+ const storeGen = this._generations[slot] ?? 0;
183
+ if (handleGen !== storeGen) {
184
+ return err(new UniqueRefStaleError(slot, handleGen, storeGen));
185
+ }
186
+ const payload = this.payloads.get(raw);
187
+ if (payload === undefined) {
188
+ // Slot 0 (sentinel) and any handle whose payload was already removed
189
+ // both surface as released. The phantom Target is unavailable at
190
+ // runtime - the error carries '<unknown>' to keep the structured
191
+ // shape consistent.
192
+ return err(new UniqueRefReleasedError(raw, '<unknown>'));
193
+ }
194
+ return ok(payload as T);
195
+ }
196
+
197
+ /**
198
+ * Release `handle` - drop the payload and push the slot onto the free list.
199
+ * Releasing the same handle twice surfaces `unique-ref-double-release`
200
+ * (D-1) so World's release loop can route the second release to Layer 3
201
+ * ErrorHandler without aborting the despawn chain. Detection is by
202
+ * payload-presence, not generation (§contract — managed handles are
203
+ * operational, not persistent).
204
+ *
205
+ * Releasing the slot-0 sentinel is a no-op - World's release loop calls
206
+ * `shouldRelease` first to filter sentinels.
207
+ */
208
+ release<Target extends string>(
209
+ handle: Handle<Target, 'unique'>,
210
+ ): Result<void, UniqueRefDoubleReleaseError | UniqueRefStaleError> {
211
+ const raw = unwrapHandle(handle);
212
+ // Gen comparison runs FIRST (q12: all operations compare gen first).
213
+ const slot = handleSlot(handle);
214
+ const handleGen = handleGeneration(handle);
215
+ const storeGen = this._generations[slot] ?? 0;
216
+ if (handleGen !== storeGen) {
217
+ // AC-03: stale release MUST NOT drop payload / fire callback / touch freeSlots.
218
+ return err(new UniqueRefStaleError(slot, handleGen, storeGen));
219
+ }
220
+ const payload = this.payloads.get(raw);
221
+ if (payload === undefined) {
222
+ return err(new UniqueRefDoubleReleaseError(raw, '<unknown>'));
223
+ }
224
+ // Order (feat-20260614 M1 D-1): clean up ALL store state BEFORE invoking
225
+ // onRelease. The payload is captured on the stack above so cb still
226
+ // observes the value (D-5 RAII-equivalent semantics preserved); both
227
+ // releaseCallbacks and payloads entries are dropped first, then the
228
+ // freelist push happens, and only then is cb invoked. A throwing cb
229
+ // leaves the store fully consistent: a second release sees
230
+ // `payloads.has(raw) === false` and returns UniqueRefDoubleReleaseError
231
+ // without re-firing the callback (AC-01, AC-02, AC-06). try/finally is
232
+ // explicitly NOT used (D-1 alternative (a) rejected — would swallow the
233
+ // first-release throw and violate spec section 3.1).
234
+ const cb = this.releaseCallbacks.get(raw);
235
+ this.releaseCallbacks.delete(raw);
236
+ this.payloads.delete(raw);
237
+ // Gen increment + retire (AC-07): bump gen; once it would exceed MAX_GEN
238
+ // (gen 255 is still usable; the bump to 256 triggers retire) the slot is
239
+ // permanently retired — NOT pushed to freeSlots.
240
+ this._generations[slot] = storeGen + 1;
241
+ if (!isRetiredSlot(this._generations[slot])) {
242
+ this.freeSlots.push(slot);
243
+ }
244
+ // else: slot retired (gen exceeded MAX_GEN) — never returns to freeSlots.
245
+ if (cb !== undefined) {
246
+ cb(payload);
247
+ }
248
+ return ok(undefined);
249
+ }
250
+
251
+ /** @internal Replace the payload of a live slot without changing its handle. */
252
+ _setPayload<Target extends string, T>(handle: Handle<Target, 'unique'>, payload: T): void {
253
+ const raw = unwrapHandle(handle);
254
+ if (!this.payloads.has(raw)) {
255
+ throw new Error(`UniqueRefStore: cannot replace released payload ${raw}.`);
256
+ }
257
+ this.payloads.set(raw, payload);
258
+ }
259
+
260
+ /**
261
+ * `true` if `handle` references a live slot - used by World's release loop
262
+ * to decide whether to call `release` (skips sentinel + already-released).
263
+ *
264
+ * The release loop differentiates "expected sentinel" (no error) from
265
+ * "leaked already-released handle" (`unique-ref-double-release`) at the
266
+ * caller layer; this helper handles only the no-op short-circuit.
267
+ */
268
+ isLive<Target extends string>(handle: Handle<Target, 'unique'>): boolean {
269
+ const raw = unwrapHandle(handle);
270
+ if (raw === 0) return false;
271
+ return this.payloads.has(raw);
272
+ }
273
+
274
+ /** @internal Diagnostic count of live slots. Exposed for tests + inspector. */
275
+ _liveCount(): number {
276
+ return this.payloads.size;
277
+ }
278
+ }
@@ -0,0 +1,84 @@
1
+ import type { EntityHandle } from './entity-handle';
2
+
3
+ export type WorldChangeKind =
4
+ | 'component-added'
5
+ | 'component-changed'
6
+ | 'component-removed'
7
+ | 'derived-component-changed'
8
+ | 'entity-removed';
9
+
10
+ export interface WorldChangeRecord {
11
+ readonly sequence: number;
12
+ readonly kind: WorldChangeKind;
13
+ readonly entity: EntityHandle;
14
+ readonly componentId?: number;
15
+ }
16
+
17
+ export type WorldChange = Omit<WorldChangeRecord, 'sequence'>;
18
+
19
+ export type WorldChangeRead =
20
+ | {
21
+ readonly status: 'ok';
22
+ readonly cursor: number;
23
+ readonly records: readonly WorldChangeRecord[];
24
+ }
25
+ | {
26
+ readonly status: 'overflow';
27
+ readonly cursor: number;
28
+ readonly oldestAvailable: number;
29
+ };
30
+
31
+ const EMPTY_CHANGES: readonly WorldChangeRecord[] = Object.freeze([]);
32
+
33
+ /**
34
+ * Bounded mutation evidence for engine-owned projections.
35
+ *
36
+ * Readers retain a cursor and rebuild their projection when the ring has
37
+ * overwritten unread records. An overflow never returns a partial delta.
38
+ */
39
+ export class WorldChangeJournal {
40
+ private readonly records: Array<WorldChangeRecord | undefined>;
41
+ private nextSequence = 1;
42
+
43
+ constructor(readonly capacity = 65_536) {
44
+ if (!Number.isSafeInteger(capacity) || capacity <= 0) {
45
+ throw new RangeError('WorldChangeJournal capacity must be a positive safe integer');
46
+ }
47
+ this.records = new Array<WorldChangeRecord | undefined>(capacity);
48
+ }
49
+
50
+ cursor(): number {
51
+ return this.nextSequence - 1;
52
+ }
53
+
54
+ append(change: WorldChange): number {
55
+ const sequence = this.nextSequence;
56
+ this.nextSequence += 1;
57
+ this.records[(sequence - 1) % this.capacity] = { ...change, sequence };
58
+ return sequence;
59
+ }
60
+
61
+ readAfter(cursor: number): WorldChangeRead {
62
+ const latest = this.cursor();
63
+ if (!Number.isSafeInteger(cursor) || cursor < 0 || cursor > latest) {
64
+ throw new RangeError(`WorldChangeJournal cursor ${cursor} is outside 0..${latest}`);
65
+ }
66
+ const oldestAvailable = Math.max(1, latest - this.capacity + 1);
67
+ if (cursor < oldestAvailable - 1) {
68
+ return { status: 'overflow', cursor: latest, oldestAvailable };
69
+ }
70
+ if (cursor === latest) {
71
+ return { status: 'ok', cursor: latest, records: EMPTY_CHANGES };
72
+ }
73
+
74
+ const records: WorldChangeRecord[] = [];
75
+ for (let sequence = cursor + 1; sequence <= latest; sequence += 1) {
76
+ const record = this.records[(sequence - 1) % this.capacity];
77
+ if (record === undefined || record.sequence !== sequence) {
78
+ return { status: 'overflow', cursor: latest, oldestAvailable };
79
+ }
80
+ records.push(record);
81
+ }
82
+ return { status: 'ok', cursor: latest, records };
83
+ }
84
+ }