@forgeax/engine-app 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 (348) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +424 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/animation-asset-lookup.unit.test.d.ts +2 -0
  5. package/dist/__tests__/animation-asset-lookup.unit.test.d.ts.map +1 -0
  6. package/dist/__tests__/callback-deletion.unit.test.d.ts +2 -0
  7. package/dist/__tests__/callback-deletion.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/callback-inference.test-d.d.ts +2 -0
  9. package/dist/__tests__/callback-inference.test-d.d.ts.map +1 -0
  10. package/dist/__tests__/component-introspection.unit.test.d.ts +2 -0
  11. package/dist/__tests__/component-introspection.unit.test.d.ts.map +1 -0
  12. package/dist/__tests__/consumer-migration.unit.test.d.ts +2 -0
  13. package/dist/__tests__/consumer-migration.unit.test.d.ts.map +1 -0
  14. package/dist/__tests__/create-app-error-retention.integration.test.d.ts +2 -0
  15. package/dist/__tests__/create-app-error-retention.integration.test.d.ts.map +1 -0
  16. package/dist/__tests__/create-app-features.test.d.ts +2 -0
  17. package/dist/__tests__/create-app-features.test.d.ts.map +1 -0
  18. package/dist/__tests__/create-app-listener-sync.test.d.ts +2 -0
  19. package/dist/__tests__/create-app-listener-sync.test.d.ts.map +1 -0
  20. package/dist/__tests__/create-app-lock-provider.test.d.ts +2 -0
  21. package/dist/__tests__/create-app-lock-provider.test.d.ts.map +1 -0
  22. package/dist/__tests__/create-app-physics-plugin-failure.integration.test.d.ts +2 -0
  23. package/dist/__tests__/create-app-physics-plugin-failure.integration.test.d.ts.map +1 -0
  24. package/dist/__tests__/create-app-plugin.test.d.ts +2 -0
  25. package/dist/__tests__/create-app-plugin.test.d.ts.map +1 -0
  26. package/dist/__tests__/create-app-stop.test.d.ts +2 -0
  27. package/dist/__tests__/create-app-stop.test.d.ts.map +1 -0
  28. package/dist/__tests__/create-app-time.unit.test.d.ts +2 -0
  29. package/dist/__tests__/create-app-time.unit.test.d.ts.map +1 -0
  30. package/dist/__tests__/create-app.test-d.d.ts +2 -0
  31. package/dist/__tests__/create-app.test-d.d.ts.map +1 -0
  32. package/dist/__tests__/create-app.test.d.ts +2 -0
  33. package/dist/__tests__/create-app.test.d.ts.map +1 -0
  34. package/dist/__tests__/create-renderer-error.integration.test.d.ts +2 -0
  35. package/dist/__tests__/create-renderer-error.integration.test.d.ts.map +1 -0
  36. package/dist/__tests__/create-renderer-lifecycle.integration.test.d.ts +2 -0
  37. package/dist/__tests__/create-renderer-lifecycle.integration.test.d.ts.map +1 -0
  38. package/dist/__tests__/docs-gate-green.unit.test.d.ts +2 -0
  39. package/dist/__tests__/docs-gate-green.unit.test.d.ts.map +1 -0
  40. package/dist/__tests__/draw-source-injected-world-update-visibility.test.d.ts +2 -0
  41. package/dist/__tests__/draw-source-injected-world-update-visibility.test.d.ts.map +1 -0
  42. package/dist/__tests__/draw-source-single-world-regression.test.d.ts +2 -0
  43. package/dist/__tests__/draw-source-single-world-regression.test.d.ts.map +1 -0
  44. package/dist/__tests__/engine-worker-bootstrap-channel.unit.test.d.ts +2 -0
  45. package/dist/__tests__/engine-worker-bootstrap-channel.unit.test.d.ts.map +1 -0
  46. package/dist/__tests__/engine-worker-world-swap.unit.test.d.ts +2 -0
  47. package/dist/__tests__/engine-worker-world-swap.unit.test.d.ts.map +1 -0
  48. package/dist/__tests__/errors-pointer-lock-failed.test.d.ts +2 -0
  49. package/dist/__tests__/errors-pointer-lock-failed.test.d.ts.map +1 -0
  50. package/dist/__tests__/execution-bootstrap-isolation.unit.test.d.ts +2 -0
  51. package/dist/__tests__/execution-bootstrap-isolation.unit.test.d.ts.map +1 -0
  52. package/dist/__tests__/execution-error-detail.test-d.d.ts +2 -0
  53. package/dist/__tests__/execution-error-detail.test-d.d.ts.map +1 -0
  54. package/dist/__tests__/execution-error-detail.unit.test.d.ts +2 -0
  55. package/dist/__tests__/execution-error-detail.unit.test.d.ts.map +1 -0
  56. package/dist/__tests__/execution-frame-order.unit.test.d.ts +2 -0
  57. package/dist/__tests__/execution-frame-order.unit.test.d.ts.map +1 -0
  58. package/dist/__tests__/execution-frame-protocol.unit.test.d.ts +2 -0
  59. package/dist/__tests__/execution-frame-protocol.unit.test.d.ts.map +1 -0
  60. package/dist/__tests__/execution-input-sample.contract.test.d.ts +2 -0
  61. package/dist/__tests__/execution-input-sample.contract.test.d.ts.map +1 -0
  62. package/dist/__tests__/execution-measurement.unit.test.d.ts +2 -0
  63. package/dist/__tests__/execution-measurement.unit.test.d.ts.map +1 -0
  64. package/dist/__tests__/execution-public-api.test-d.d.ts +2 -0
  65. package/dist/__tests__/execution-public-api.test-d.d.ts.map +1 -0
  66. package/dist/__tests__/execution-public-api.unit.test.d.ts +2 -0
  67. package/dist/__tests__/execution-public-api.unit.test.d.ts.map +1 -0
  68. package/dist/__tests__/execution-rebuild.contract.test.d.ts +2 -0
  69. package/dist/__tests__/execution-rebuild.contract.test.d.ts.map +1 -0
  70. package/dist/__tests__/execution-report.contract.test.d.ts +2 -0
  71. package/dist/__tests__/execution-report.contract.test.d.ts.map +1 -0
  72. package/dist/__tests__/execution-report.types.test-d.d.ts +2 -0
  73. package/dist/__tests__/execution-report.types.test-d.d.ts.map +1 -0
  74. package/dist/__tests__/execution-schema-freshness.unit.test.d.ts +2 -0
  75. package/dist/__tests__/execution-schema-freshness.unit.test.d.ts.map +1 -0
  76. package/dist/__tests__/execution-selector.unit.test.d.ts +2 -0
  77. package/dist/__tests__/execution-selector.unit.test.d.ts.map +1 -0
  78. package/dist/__tests__/execution-shared-kernel.unit.test.d.ts +2 -0
  79. package/dist/__tests__/execution-shared-kernel.unit.test.d.ts.map +1 -0
  80. package/dist/__tests__/fallback-camera.unit.test.d.ts +2 -0
  81. package/dist/__tests__/fallback-camera.unit.test.d.ts.map +1 -0
  82. package/dist/__tests__/frame-loop-time-elapsed.test.d.ts +2 -0
  83. package/dist/__tests__/frame-loop-time-elapsed.test.d.ts.map +1 -0
  84. package/dist/__tests__/frame-loop-time.unit.test.d.ts +2 -0
  85. package/dist/__tests__/frame-loop-time.unit.test.d.ts.map +1 -0
  86. package/dist/__tests__/frame-loop-world-array.test.d.ts +2 -0
  87. package/dist/__tests__/frame-loop-world-array.test.d.ts.map +1 -0
  88. package/dist/__tests__/kernel-worker-runtime.unit.test.d.ts +2 -0
  89. package/dist/__tests__/kernel-worker-runtime.unit.test.d.ts.map +1 -0
  90. package/dist/__tests__/migration-surface-inventory.unit.test.d.ts +2 -0
  91. package/dist/__tests__/migration-surface-inventory.unit.test.d.ts.map +1 -0
  92. package/dist/__tests__/plugin-policy.integration.test.d.ts +2 -0
  93. package/dist/__tests__/plugin-policy.integration.test.d.ts.map +1 -0
  94. package/dist/__tests__/plugin-profiles.unit.test.d.ts +2 -0
  95. package/dist/__tests__/plugin-profiles.unit.test.d.ts.map +1 -0
  96. package/dist/__tests__/profiler-default-off.test.d.ts +2 -0
  97. package/dist/__tests__/profiler-default-off.test.d.ts.map +1 -0
  98. package/dist/__tests__/profiler-frame-token.test.d.ts +2 -0
  99. package/dist/__tests__/profiler-frame-token.test.d.ts.map +1 -0
  100. package/dist/__tests__/profiler-phase-catalog.test.d.ts +2 -0
  101. package/dist/__tests__/profiler-phase-catalog.test.d.ts.map +1 -0
  102. package/dist/__tests__/profiler-phase-integration.test.d.ts +2 -0
  103. package/dist/__tests__/profiler-phase-integration.test.d.ts.map +1 -0
  104. package/dist/__tests__/profiler-remote-bridge.test.d.ts +2 -0
  105. package/dist/__tests__/profiler-remote-bridge.test.d.ts.map +1 -0
  106. package/dist/__tests__/remote-integration.test.d.ts +2 -0
  107. package/dist/__tests__/remote-integration.test.d.ts.map +1 -0
  108. package/dist/__tests__/rhi-capture-capability.test-d.d.ts +2 -0
  109. package/dist/__tests__/rhi-capture-capability.test-d.d.ts.map +1 -0
  110. package/dist/__tests__/rhi-capture-host.integration.test.d.ts +2 -0
  111. package/dist/__tests__/rhi-capture-host.integration.test.d.ts.map +1 -0
  112. package/dist/__tests__/rhi-capture.browser.test.d.ts +2 -0
  113. package/dist/__tests__/rhi-capture.browser.test.d.ts.map +1 -0
  114. package/dist/__tests__/surface-handoff.test.d.ts +2 -0
  115. package/dist/__tests__/surface-handoff.test.d.ts.map +1 -0
  116. package/dist/__tests__/types-m2.test-d.d.ts +2 -0
  117. package/dist/__tests__/types-m2.test-d.d.ts.map +1 -0
  118. package/dist/__tests__/visibility-remote.integration.test.d.ts +2 -0
  119. package/dist/__tests__/visibility-remote.integration.test.d.ts.map +1 -0
  120. package/dist/__tests__/worker-execution-stop.test.d.ts +2 -0
  121. package/dist/__tests__/worker-execution-stop.test.d.ts.map +1 -0
  122. package/dist/animation-asset-lookup.d.ts +7 -0
  123. package/dist/animation-asset-lookup.d.ts.map +1 -0
  124. package/dist/browser-frame-signal.d.ts +17 -0
  125. package/dist/browser-frame-signal.d.ts.map +1 -0
  126. package/dist/canvas-policy.d.ts +4 -0
  127. package/dist/canvas-policy.d.ts.map +1 -0
  128. package/dist/create-app.d.ts +78 -0
  129. package/dist/create-app.d.ts.map +1 -0
  130. package/dist/engine-worker-runtime.mjs +827 -0
  131. package/dist/engine-worker-runtime.mjs.map +1 -0
  132. package/dist/errors.d.ts +254 -0
  133. package/dist/errors.d.ts.map +1 -0
  134. package/dist/execution/attached-world-swap.d.ts +10 -0
  135. package/dist/execution/attached-world-swap.d.ts.map +1 -0
  136. package/dist/execution/bootstrap-entry.d.ts +30 -0
  137. package/dist/execution/bootstrap-entry.d.ts.map +1 -0
  138. package/dist/execution/bootstrap-url.d.ts +5 -0
  139. package/dist/execution/bootstrap-url.d.ts.map +1 -0
  140. package/dist/execution/capabilities.d.ts +5 -0
  141. package/dist/execution/capabilities.d.ts.map +1 -0
  142. package/dist/execution/control.d.ts +12 -0
  143. package/dist/execution/control.d.ts.map +1 -0
  144. package/dist/execution/engine-worker-runtime.d.ts +2 -0
  145. package/dist/execution/engine-worker-runtime.d.ts.map +1 -0
  146. package/dist/execution/engine-worker.d.ts +24 -0
  147. package/dist/execution/engine-worker.d.ts.map +1 -0
  148. package/dist/execution/host-controller.d.ts +16 -0
  149. package/dist/execution/host-controller.d.ts.map +1 -0
  150. package/dist/execution/index.d.ts +10 -0
  151. package/dist/execution/index.d.ts.map +1 -0
  152. package/dist/execution/kernel-pool.d.ts +14 -0
  153. package/dist/execution/kernel-pool.d.ts.map +1 -0
  154. package/dist/execution/kernel-worker-runtime.d.ts +2 -0
  155. package/dist/execution/kernel-worker-runtime.d.ts.map +1 -0
  156. package/dist/execution/measurement.d.ts +7 -0
  157. package/dist/execution/measurement.d.ts.map +1 -0
  158. package/dist/execution/protocol.d.ts +87 -0
  159. package/dist/execution/protocol.d.ts.map +1 -0
  160. package/dist/execution/report.d.ts +5 -0
  161. package/dist/execution/report.d.ts.map +1 -0
  162. package/dist/execution/schema.d.ts +4 -0
  163. package/dist/execution/schema.d.ts.map +1 -0
  164. package/dist/execution/selector.d.ts +10 -0
  165. package/dist/execution/selector.d.ts.map +1 -0
  166. package/dist/execution/types.d.ts +123 -0
  167. package/dist/execution/types.d.ts.map +1 -0
  168. package/dist/fallback-camera.d.ts +8 -0
  169. package/dist/fallback-camera.d.ts.map +1 -0
  170. package/dist/game-context.d.ts +98 -0
  171. package/dist/game-context.d.ts.map +1 -0
  172. package/dist/index.d.ts +27 -0
  173. package/dist/index.d.ts.map +1 -0
  174. package/dist/index.mjs +3980 -0
  175. package/dist/index.mjs.map +1 -0
  176. package/dist/input-map-plugin.d.ts +5 -0
  177. package/dist/input-map-plugin.d.ts.map +1 -0
  178. package/dist/input-plugin.d.ts +4 -0
  179. package/dist/input-plugin.d.ts.map +1 -0
  180. package/dist/internal/assembled-engine-profile.d.ts +5 -0
  181. package/dist/internal/assembled-engine-profile.d.ts.map +1 -0
  182. package/dist/internal/assets-world-plugin.d.ts +7 -0
  183. package/dist/internal/assets-world-plugin.d.ts.map +1 -0
  184. package/dist/internal/browser-remote-bridge.d.ts +24 -0
  185. package/dist/internal/browser-remote-bridge.d.ts.map +1 -0
  186. package/dist/internal/browser-rhi-debug-runtime.d.ts +18 -0
  187. package/dist/internal/browser-rhi-debug-runtime.d.ts.map +1 -0
  188. package/dist/internal/component-introspection.d.ts +24 -0
  189. package/dist/internal/component-introspection.d.ts.map +1 -0
  190. package/dist/internal/debug-draw.d.ts +9 -0
  191. package/dist/internal/debug-draw.d.ts.map +1 -0
  192. package/dist/internal/ecs-import.d.ts +6 -0
  193. package/dist/internal/ecs-import.d.ts.map +1 -0
  194. package/dist/internal/engine-profile-common.d.ts +9 -0
  195. package/dist/internal/engine-profile-common.d.ts.map +1 -0
  196. package/dist/internal/error-fanout.d.ts +66 -0
  197. package/dist/internal/error-fanout.d.ts.map +1 -0
  198. package/dist/internal/frame-loop.d.ts +38 -0
  199. package/dist/internal/frame-loop.d.ts.map +1 -0
  200. package/dist/internal/input-attach.d.ts +44 -0
  201. package/dist/internal/input-attach.d.ts.map +1 -0
  202. package/dist/internal/main-engine-profile.d.ts +19 -0
  203. package/dist/internal/main-engine-profile.d.ts.map +1 -0
  204. package/dist/internal/remote-serve-flag.d.ts +19 -0
  205. package/dist/internal/remote-serve-flag.d.ts.map +1 -0
  206. package/dist/internal/remote-server-plugin.d.ts +8 -0
  207. package/dist/internal/remote-server-plugin.d.ts.map +1 -0
  208. package/dist/internal/rhi-capture.d.ts +19 -0
  209. package/dist/internal/rhi-capture.d.ts.map +1 -0
  210. package/dist/internal/rhi-debug-flag.d.ts +18 -0
  211. package/dist/internal/rhi-debug-flag.d.ts.map +1 -0
  212. package/dist/internal/worker-engine-profile.d.ts +15 -0
  213. package/dist/internal/worker-engine-profile.d.ts.map +1 -0
  214. package/dist/kernel-worker-runtime.mjs +69 -0
  215. package/dist/kernel-worker-runtime.mjs.map +1 -0
  216. package/dist/load-game-errors.d.ts +131 -0
  217. package/dist/load-game-errors.d.ts.map +1 -0
  218. package/dist/load-game.d.ts +34 -0
  219. package/dist/load-game.d.ts.map +1 -0
  220. package/dist/plugin-factories.d.ts +3 -0
  221. package/dist/plugin-factories.d.ts.map +1 -0
  222. package/dist/renderer-plugin.d.ts +25 -0
  223. package/dist/renderer-plugin.d.ts.map +1 -0
  224. package/dist/tool-preview/bootstrap.d.ts +113 -0
  225. package/dist/tool-preview/bootstrap.d.ts.map +1 -0
  226. package/dist/tool-preview/evidence.d.ts +20 -0
  227. package/dist/tool-preview/evidence.d.ts.map +1 -0
  228. package/dist/tool-preview/framing.d.ts +13 -0
  229. package/dist/tool-preview/framing.d.ts.map +1 -0
  230. package/dist/tool-preview/recipe.d.ts +54 -0
  231. package/dist/tool-preview/recipe.d.ts.map +1 -0
  232. package/dist/types.d.ts +404 -0
  233. package/dist/types.d.ts.map +1 -0
  234. package/package.json +85 -0
  235. package/schema/execution-report.schema.json +168 -0
  236. package/src/__tests__/animation-asset-lookup.unit.test.ts +20 -0
  237. package/src/__tests__/callback-deletion.unit.test.ts +14 -0
  238. package/src/__tests__/callback-inference.test-d.ts +36 -0
  239. package/src/__tests__/component-introspection.unit.test.ts +56 -0
  240. package/src/__tests__/consumer-migration.unit.test.ts +34 -0
  241. package/src/__tests__/create-app-error-retention.integration.test.ts +125 -0
  242. package/src/__tests__/create-app-features.test.ts +51 -0
  243. package/src/__tests__/create-app-listener-sync.test.ts +381 -0
  244. package/src/__tests__/create-app-lock-provider.test.ts +212 -0
  245. package/src/__tests__/create-app-physics-plugin-failure.integration.test.ts +142 -0
  246. package/src/__tests__/create-app-plugin.test.ts +217 -0
  247. package/src/__tests__/create-app-stop.test.ts +609 -0
  248. package/src/__tests__/create-app-time.unit.test.ts +26 -0
  249. package/src/__tests__/create-app.test-d.ts +83 -0
  250. package/src/__tests__/create-app.test.ts +287 -0
  251. package/src/__tests__/create-renderer-error.integration.test.ts +25 -0
  252. package/src/__tests__/create-renderer-lifecycle.integration.test.ts +24 -0
  253. package/src/__tests__/docs-gate-green.unit.test.ts +61 -0
  254. package/src/__tests__/draw-source-injected-world-update-visibility.test.ts +176 -0
  255. package/src/__tests__/draw-source-single-world-regression.test.ts +184 -0
  256. package/src/__tests__/engine-worker-bootstrap-channel.unit.test.ts +82 -0
  257. package/src/__tests__/engine-worker-world-swap.unit.test.ts +69 -0
  258. package/src/__tests__/errors-pointer-lock-failed.test.ts +265 -0
  259. package/src/__tests__/execution-bootstrap-isolation.unit.test.ts +79 -0
  260. package/src/__tests__/execution-error-detail.test-d.ts +13 -0
  261. package/src/__tests__/execution-error-detail.unit.test.ts +35 -0
  262. package/src/__tests__/execution-frame-order.unit.test.ts +38 -0
  263. package/src/__tests__/execution-frame-protocol.unit.test.ts +60 -0
  264. package/src/__tests__/execution-input-sample.contract.test.ts +23 -0
  265. package/src/__tests__/execution-measurement.unit.test.ts +20 -0
  266. package/src/__tests__/execution-public-api.test-d.ts +14 -0
  267. package/src/__tests__/execution-public-api.unit.test.ts +9 -0
  268. package/src/__tests__/execution-rebuild.contract.test.ts +36 -0
  269. package/src/__tests__/execution-report.contract.test.ts +50 -0
  270. package/src/__tests__/execution-report.types.test-d.ts +16 -0
  271. package/src/__tests__/execution-schema-freshness.unit.test.ts +20 -0
  272. package/src/__tests__/execution-selector.unit.test.ts +72 -0
  273. package/src/__tests__/execution-shared-kernel.unit.test.ts +180 -0
  274. package/src/__tests__/fallback-camera.unit.test.ts +32 -0
  275. package/src/__tests__/frame-loop-time-elapsed.test.ts +115 -0
  276. package/src/__tests__/frame-loop-time.unit.test.ts +61 -0
  277. package/src/__tests__/frame-loop-world-array.test.ts +450 -0
  278. package/src/__tests__/kernel-worker-runtime.unit.test.ts +50 -0
  279. package/src/__tests__/migration-surface-inventory.unit.test.ts +13 -0
  280. package/src/__tests__/plugin-policy.integration.test.ts +15 -0
  281. package/src/__tests__/plugin-profiles.unit.test.ts +190 -0
  282. package/src/__tests__/profiler-default-off.test.ts +53 -0
  283. package/src/__tests__/profiler-frame-token.test.ts +235 -0
  284. package/src/__tests__/profiler-phase-catalog.test.ts +30 -0
  285. package/src/__tests__/profiler-phase-integration.test.ts +117 -0
  286. package/src/__tests__/profiler-remote-bridge.test.ts +31 -0
  287. package/src/__tests__/remote-integration.test.ts +224 -0
  288. package/src/__tests__/rhi-capture-capability.test-d.ts +22 -0
  289. package/src/__tests__/rhi-capture-host.integration.test.ts +157 -0
  290. package/src/__tests__/rhi-capture.browser.test.ts +51 -0
  291. package/src/__tests__/surface-handoff.test.ts +89 -0
  292. package/src/__tests__/types-m2.test-d.ts +81 -0
  293. package/src/__tests__/visibility-remote.integration.test.ts +116 -0
  294. package/src/__tests__/worker-execution-stop.test.ts +173 -0
  295. package/src/animation-asset-lookup.ts +13 -0
  296. package/src/browser-frame-signal.ts +44 -0
  297. package/src/canvas-policy.ts +16 -0
  298. package/src/create-app.ts +1264 -0
  299. package/src/errors.ts +442 -0
  300. package/src/execution/attached-world-swap.ts +33 -0
  301. package/src/execution/bootstrap-entry.ts +122 -0
  302. package/src/execution/bootstrap-url.ts +25 -0
  303. package/src/execution/capabilities.ts +102 -0
  304. package/src/execution/control.ts +48 -0
  305. package/src/execution/engine-worker-runtime.ts +383 -0
  306. package/src/execution/engine-worker.ts +163 -0
  307. package/src/execution/host-controller.ts +460 -0
  308. package/src/execution/index.ts +44 -0
  309. package/src/execution/kernel-pool.ts +236 -0
  310. package/src/execution/kernel-worker-runtime.ts +103 -0
  311. package/src/execution/measurement.ts +39 -0
  312. package/src/execution/protocol.ts +134 -0
  313. package/src/execution/report.ts +66 -0
  314. package/src/execution/schema.ts +168 -0
  315. package/src/execution/selector.ts +98 -0
  316. package/src/execution/types.ts +163 -0
  317. package/src/fallback-camera.ts +20 -0
  318. package/src/game-context.ts +126 -0
  319. package/src/index.ts +190 -0
  320. package/src/input-map-plugin.ts +21 -0
  321. package/src/input-plugin.ts +26 -0
  322. package/src/internal/assembled-engine-profile.ts +14 -0
  323. package/src/internal/assets-world-plugin.ts +33 -0
  324. package/src/internal/browser-remote-bridge.ts +269 -0
  325. package/src/internal/browser-rhi-debug-runtime.ts +67 -0
  326. package/src/internal/component-introspection.ts +116 -0
  327. package/src/internal/debug-draw.ts +38 -0
  328. package/src/internal/ecs-import.ts +41 -0
  329. package/src/internal/engine-profile-common.ts +9 -0
  330. package/src/internal/error-fanout.ts +129 -0
  331. package/src/internal/frame-loop.ts +539 -0
  332. package/src/internal/input-attach.ts +104 -0
  333. package/src/internal/main-engine-profile.ts +84 -0
  334. package/src/internal/remote-serve-flag.ts +41 -0
  335. package/src/internal/remote-server-plugin.ts +16 -0
  336. package/src/internal/rhi-capture.ts +211 -0
  337. package/src/internal/rhi-debug-flag.ts +40 -0
  338. package/src/internal/worker-engine-profile.ts +47 -0
  339. package/src/load-game-errors.ts +211 -0
  340. package/src/load-game.ts +115 -0
  341. package/src/plugin-factories.ts +2 -0
  342. package/src/renderer-plugin.ts +96 -0
  343. package/src/tool-preview/bootstrap.ts +835 -0
  344. package/src/tool-preview/evidence.ts +59 -0
  345. package/src/tool-preview/framing.ts +52 -0
  346. package/src/tool-preview/recipe.ts +137 -0
  347. package/src/types.ts +450 -0
  348. package/src/vite-env.d.ts +10 -0
@@ -0,0 +1,13 @@
1
+ import { expectTypeOf, it } from 'vitest';
2
+ import type { AppError } from '../index';
3
+
4
+ it('narrows execution error detail by code', () => {
5
+ const error = null as unknown as AppError;
6
+ if (error.code === 'app-execution-kernel-failed') {
7
+ expectTypeOf(error.detail.partialWrite).toEqualTypeOf<true>();
8
+ expectTypeOf(error.detail.retryable).toEqualTypeOf<false>();
9
+ }
10
+ if (error.code === 'app-execution-deadline-exceeded') {
11
+ expectTypeOf(error.detail.phase).toEqualTypeOf<'startup' | 'handshake' | 'frame'>();
12
+ }
13
+ });
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { APP_ERROR_HINTS, APP_EXPECTED, AppError, type AppErrorCode } from '../index';
3
+
4
+ const EXECUTION_CODES = [
5
+ 'app-execution-tier-unavailable',
6
+ 'app-execution-bootstrap-failed',
7
+ 'app-execution-deadline-exceeded',
8
+ 'app-execution-kernel-failed',
9
+ 'app-execution-stale-world',
10
+ 'app-execution-rebuild-failed',
11
+ ] as const satisfies readonly AppErrorCode[];
12
+
13
+ describe('execution errors', () => {
14
+ it('has executable expected and hint tables for every execution code', () => {
15
+ for (const code of EXECUTION_CODES) {
16
+ expect(APP_EXPECTED[code].length).toBeGreaterThan(0);
17
+ expect(APP_ERROR_HINTS[code].length).toBeGreaterThan(0);
18
+ }
19
+ });
20
+
21
+ it('exposes unavailable facts without message parsing', () => {
22
+ const error = new AppError({
23
+ code: 'app-execution-tier-unavailable',
24
+ expected: APP_EXPECTED['app-execution-tier-unavailable'],
25
+ hint: APP_ERROR_HINTS['app-execution-tier-unavailable'],
26
+ detail: {
27
+ requestedTier: 'shared',
28
+ missingCapabilities: ['sharedArrayBuffer'],
29
+ sharedEvidencePassed: true,
30
+ },
31
+ });
32
+ expect(error.detail.requestedTier).toBe('shared');
33
+ expect(error.detail.missingCapabilities).toEqual(['sharedArrayBuffer']);
34
+ });
35
+ });
@@ -0,0 +1,38 @@
1
+ import type { InputBackendSample } from '@forgeax/engine-input';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { FrameCreditLedger } from '../execution/protocol';
4
+
5
+ describe('execution frame ordering', () => {
6
+ it('samples once and cannot issue the next credit before update, draw and completion', () => {
7
+ const trace: string[] = [];
8
+ let samples = 0;
9
+ const sample = (): InputBackendSample => {
10
+ samples += 1;
11
+ trace.push('sample');
12
+ return {
13
+ downKeys: new Set(),
14
+ upKeys: new Set(),
15
+ buttons: [false, false, false],
16
+ movementX: 0,
17
+ movementY: 0,
18
+ wheelDelta: 0,
19
+ focused: true,
20
+ pointerLocked: false,
21
+ };
22
+ };
23
+ const ledger = new FrameCreditLedger('world');
24
+ const frame = ledger.issue(1 / 60, sample);
25
+ expect(ledger.issue(1 / 60, sample)).toBeUndefined();
26
+ trace.push('update', 'draw');
27
+ ledger.complete({
28
+ kind: 'frame-complete',
29
+ worldIdentity: 'world',
30
+ frameId: frame?.frameId ?? 0,
31
+ engineUpdateMs: 1,
32
+ kernelWaitMs: 0,
33
+ });
34
+ trace.push('complete');
35
+ expect(trace).toEqual(['sample', 'update', 'draw', 'complete']);
36
+ expect(samples).toBe(1);
37
+ });
38
+ });
@@ -0,0 +1,60 @@
1
+ import type { InputBackendSample } from '@forgeax/engine-input';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { FrameCreditLedger } from '../execution/protocol';
4
+
5
+ const sample: InputBackendSample = {
6
+ downKeys: new Set(),
7
+ upKeys: new Set(),
8
+ buttons: [false, false, false],
9
+ movementX: 0,
10
+ movementY: 0,
11
+ wheelDelta: 0,
12
+ focused: true,
13
+ pointerLocked: false,
14
+ };
15
+
16
+ describe('frame credit protocol', () => {
17
+ it('allows one in-flight frame and handles duplicate, late and old identities without writes', () => {
18
+ const ledger = new FrameCreditLedger('world-1');
19
+ const first = ledger.issue(0.016, () => sample);
20
+ expect(first?.frameId).toBe(1);
21
+ expect(ledger.issue(0.016, () => sample)).toBeUndefined();
22
+ expect(
23
+ ledger.complete({
24
+ kind: 'frame-complete',
25
+ worldIdentity: 'old',
26
+ frameId: 1,
27
+ engineUpdateMs: 1,
28
+ kernelWaitMs: 0,
29
+ }),
30
+ ).toBe('stale-world');
31
+ expect(
32
+ ledger.complete({
33
+ kind: 'frame-complete',
34
+ worldIdentity: 'world-1',
35
+ frameId: 2,
36
+ engineUpdateMs: 1,
37
+ kernelWaitMs: 0,
38
+ }),
39
+ ).toBe('late');
40
+ expect(
41
+ ledger.complete({
42
+ kind: 'frame-complete',
43
+ worldIdentity: 'world-1',
44
+ frameId: 1,
45
+ engineUpdateMs: 1,
46
+ kernelWaitMs: 0,
47
+ }),
48
+ ).toBe('accepted');
49
+ expect(
50
+ ledger.complete({
51
+ kind: 'frame-complete',
52
+ worldIdentity: 'world-1',
53
+ frameId: 1,
54
+ engineUpdateMs: 1,
55
+ kernelWaitMs: 0,
56
+ }),
57
+ ).toBe('duplicate');
58
+ expect(ledger.issue(0.016, () => sample)?.frameId).toBe(2);
59
+ });
60
+ });
@@ -0,0 +1,23 @@
1
+ import { type InputBackendSample, snapshotFromSample } from '@forgeax/engine-input';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ describe('Host to Engine input boundary', () => {
5
+ it('reconstructs the existing InputSnapshot from one structured-cloneable sample', () => {
6
+ const sample: InputBackendSample = {
7
+ downKeys: new Set(['w']),
8
+ upKeys: new Set(),
9
+ buttons: [false, false, false],
10
+ movementX: 4,
11
+ movementY: -2,
12
+ wheelDelta: 1,
13
+ focused: true,
14
+ pointerLocked: true,
15
+ };
16
+ const clone = structuredClone(sample);
17
+ const snapshot = snapshotFromSample(clone);
18
+ expect(snapshot.keyboard.down('w')).toBe(true);
19
+ expect(snapshot.mouse.movementDelta).toEqual({ x: 4, y: -2 });
20
+ expect(clone).not.toHaveProperty('window');
21
+ expect(clone).not.toHaveProperty('document');
22
+ });
23
+ });
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { createMeasurementSeries } from '../execution/measurement';
3
+
4
+ describe('execution measurement series', () => {
5
+ it('reports bounded percentiles while retaining the total sample count', () => {
6
+ const series = createMeasurementSeries(3);
7
+ series.add(100);
8
+ series.add(2);
9
+ series.add(3);
10
+ const report = series.add(4);
11
+ expect(report).toEqual({ samples: 4, p50: 3, p95: 4, p99: 4, jitter: 1 });
12
+ });
13
+
14
+ it('clears samples for a rebuilt World identity', () => {
15
+ const series = createMeasurementSeries();
16
+ series.add(1);
17
+ series.clear();
18
+ expect(series.add(5).samples).toBe(1);
19
+ });
20
+ });
@@ -0,0 +1,14 @@
1
+ import { expectTypeOf, it } from 'vitest';
2
+ import type { App, CreateAppOptions, ExecutionControl, ExecutionReport } from '../index';
3
+
4
+ it('discovers execution from the public App surface', () => {
5
+ const app = null as unknown as App;
6
+ const options = null as unknown as CreateAppOptions;
7
+ expectTypeOf(app.execution).toEqualTypeOf<ExecutionControl>();
8
+ expectTypeOf(app.execution.report()).toEqualTypeOf<ExecutionReport>();
9
+ expectTypeOf(options.execution?.bootstrap).toEqualTypeOf<string | URL | undefined>();
10
+ expectTypeOf(options.execution?.assetCatalog?.url).toEqualTypeOf<string | undefined>();
11
+ expectTypeOf(options.execution?.assetCatalog?.expectedScope).toEqualTypeOf<
12
+ { readonly scopeId: string; readonly generation: number } | undefined
13
+ >();
14
+ });
@@ -0,0 +1,9 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { EXECUTION_REQUESTED_TIERS, EXECUTION_TIERS } from '../index';
3
+
4
+ describe('execution public affordance', () => {
5
+ it('advertises only stable tier names', () => {
6
+ expect(EXECUTION_TIERS).toEqual(['main-serial', 'engine-worker', 'shared']);
7
+ expect(EXECUTION_REQUESTED_TIERS).toEqual(['auto', ...EXECUTION_TIERS]);
8
+ });
9
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { createExecutionReport, unavailableExecutionCapabilities } from '../execution';
3
+ import { createLocalExecutionControl } from '../execution/control';
4
+
5
+ describe('execution rebuild contract', () => {
6
+ it('keeps low-level local assembly explicit when no bootstrap exists', async () => {
7
+ const control = createLocalExecutionControl(
8
+ createExecutionReport('main-serial', unavailableExecutionCapabilities('local')),
9
+ );
10
+ const before = control.report();
11
+ const result = await control.rebuild();
12
+ expect(result.ok).toBe(false);
13
+ if (!result.ok) expect(result.error.code).toBe('app-execution-rebuild-failed');
14
+ expect(control.report()).toEqual(before);
15
+ });
16
+
17
+ it('projects live local lifecycle and World state without a duplicate ledger', () => {
18
+ let world: ReturnType<typeof createExecutionReport>['world'] = {
19
+ identity: 'world-1',
20
+ health: 'healthy',
21
+ partialWrite: false,
22
+ retryable: true,
23
+ };
24
+ const control = createLocalExecutionControl(
25
+ createExecutionReport('main-serial', unavailableExecutionCapabilities('local')),
26
+ { world: () => world },
27
+ );
28
+
29
+ control.setEngineHealth('running');
30
+ expect(control.report().engine.health).toBe('running');
31
+ world = { ...world, health: 'poisoned', partialWrite: true, retryable: false };
32
+ expect(control.report().world).toEqual(world);
33
+ control.setEngineHealth('stopped');
34
+ expect(control.report().engine.health).toBe('stopped');
35
+ });
36
+ });
@@ -0,0 +1,50 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ createExecutionReport,
4
+ EXECUTION_CAPABILITY_NAMES,
5
+ type ExecutionCapabilities,
6
+ isExecutionReport,
7
+ selectExecutionTier,
8
+ } from '../index';
9
+
10
+ function capabilities(available = true): ExecutionCapabilities {
11
+ return Object.fromEntries(
12
+ EXECUTION_CAPABILITY_NAMES.map((name) => [
13
+ name,
14
+ { available, reason: available ? 'observed' : 'missing' },
15
+ ]),
16
+ ) as unknown as ExecutionCapabilities;
17
+ }
18
+
19
+ describe('ExecutionReport contract', () => {
20
+ it('is the closed requested/actual/capability/health/performance/fault snapshot', () => {
21
+ const selection = selectExecutionTier({
22
+ requestedTier: 'auto',
23
+ capabilities: capabilities(),
24
+ sharedEvidencePassed: true,
25
+ }).unwrap();
26
+ const report = createExecutionReport('auto', capabilities(), selection);
27
+ expect(report.requestedTier).toBe('auto');
28
+ expect(report.actualTier).toBe('shared');
29
+ expect(report.capabilities.worker.available).toBe(true);
30
+ expect(report.engine.health).toBe('idle');
31
+ expect(report.world.health).toBe('healthy');
32
+ expect(report.kernelDispatch.reason).toBe('no-eligible-kernel');
33
+ expect(report.performance.kernelWaitMs).toBeNull();
34
+ expect(report.audio).toEqual({
35
+ owner: 'host',
36
+ contextState: 'suspended',
37
+ activeSourceCount: 0,
38
+ lastError: null,
39
+ });
40
+ expect(report.fault).toBeNull();
41
+ expect(isExecutionReport(report)).toBe(true);
42
+ });
43
+
44
+ it('rejects missing and extra schema fields', () => {
45
+ const report = createExecutionReport('main-serial', capabilities(false));
46
+ const { world: _world, ...missing } = report;
47
+ expect(isExecutionReport(missing)).toBe(false);
48
+ expect(isExecutionReport({ ...report, workerId: 1 })).toBe(false);
49
+ });
50
+ });
@@ -0,0 +1,16 @@
1
+ import { expectTypeOf, it } from 'vitest';
2
+ import type { ExecutionReport, ExecutionTier } from '../index';
3
+
4
+ it('exposes stable report fields without transport internals', () => {
5
+ const report = null as unknown as ExecutionReport;
6
+ expectTypeOf(report.actualTier).toEqualTypeOf<ExecutionTier | null>();
7
+ expectTypeOf(report.world.partialWrite).toBeBoolean();
8
+ expectTypeOf(report.world.retryable).toBeBoolean();
9
+ expectTypeOf(report.kernelDispatch.dispatched).toBeNumber();
10
+ expectTypeOf(report.performance.hostFrameMs).toMatchTypeOf<object | null>();
11
+ expectTypeOf(report.audio.owner).toEqualTypeOf<'host'>();
12
+ // @ts-expect-error transport identities are private
13
+ void report.workerId;
14
+ // @ts-expect-error SAB shards are private
15
+ void report.shards;
16
+ });
@@ -0,0 +1,20 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { describe, expect, it } from 'vitest';
3
+ import {
4
+ EXECUTION_CAPABILITY_NAMES,
5
+ EXECUTION_REPORT_SCHEMA_VERSION,
6
+ EXECUTION_REQUESTED_TIERS,
7
+ EXECUTION_TIERS,
8
+ } from '../index';
9
+
10
+ describe('execution schema freshness', () => {
11
+ it('keeps runtime catalogs aligned with the JSON schema authority', () => {
12
+ const schema = JSON.parse(
13
+ readFileSync(new URL('../../schema/execution-report.schema.json', import.meta.url), 'utf8'),
14
+ ) as { properties: Record<string, { const?: number; enum?: unknown[]; required?: string[] }> };
15
+ expect(schema.properties.schemaVersion?.const).toBe(EXECUTION_REPORT_SCHEMA_VERSION);
16
+ expect(schema.properties.requestedTier?.enum).toEqual(EXECUTION_REQUESTED_TIERS);
17
+ expect(schema.properties.actualTier?.enum).toEqual([null, ...EXECUTION_TIERS]);
18
+ expect(schema.properties.capabilities?.required).toEqual(EXECUTION_CAPABILITY_NAMES);
19
+ });
20
+ });
@@ -0,0 +1,72 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ EXECUTION_CAPABILITY_NAMES,
4
+ type ExecutionCapabilities,
5
+ type ExecutionCapabilityName,
6
+ selectExecutionTier,
7
+ } from '../index';
8
+
9
+ function facts(missing: readonly ExecutionCapabilityName[] = []): ExecutionCapabilities {
10
+ return Object.fromEntries(
11
+ EXECUTION_CAPABILITY_NAMES.map((name) => [
12
+ name,
13
+ {
14
+ available: !missing.includes(name),
15
+ reason: missing.includes(name) ? 'fixture missing' : 'fixture observed',
16
+ },
17
+ ]),
18
+ ) as unknown as ExecutionCapabilities;
19
+ }
20
+
21
+ describe('execution tier selector', () => {
22
+ it('orders auto shared to engine-worker to main-serial', () => {
23
+ expect(
24
+ selectExecutionTier({
25
+ requestedTier: 'auto',
26
+ capabilities: facts(),
27
+ sharedEvidencePassed: true,
28
+ }).unwrap().actualTier,
29
+ ).toBe('shared');
30
+ expect(
31
+ selectExecutionTier({
32
+ requestedTier: 'auto',
33
+ capabilities: facts(['sharedArrayBuffer']),
34
+ sharedEvidencePassed: true,
35
+ }).unwrap().actualTier,
36
+ ).toBe('engine-worker');
37
+ expect(
38
+ selectExecutionTier({
39
+ requestedTier: 'auto',
40
+ capabilities: facts(['workerWebGpu']),
41
+ sharedEvidencePassed: true,
42
+ }).unwrap().actualTier,
43
+ ).toBe('main-serial');
44
+ });
45
+
46
+ it('does not conflate failed shipped evidence with a missing browser capability', () => {
47
+ const result = selectExecutionTier({
48
+ requestedTier: 'shared',
49
+ capabilities: facts(),
50
+ sharedEvidencePassed: false,
51
+ });
52
+ expect(result.ok).toBe(false);
53
+ if (!result.ok) {
54
+ if (result.error.code !== 'app-execution-tier-unavailable') throw result.error;
55
+ expect(result.error.detail.missingCapabilities).toEqual([]);
56
+ expect(result.error.detail.sharedEvidencePassed).toBe(false);
57
+ }
58
+ });
59
+
60
+ it('never silently downgrades an explicit tier', () => {
61
+ const result = selectExecutionTier({
62
+ requestedTier: 'engine-worker',
63
+ capabilities: facts(['offscreenCanvas']),
64
+ sharedEvidencePassed: true,
65
+ });
66
+ expect(result.ok).toBe(false);
67
+ if (!result.ok) {
68
+ if (result.error.code !== 'app-execution-tier-unavailable') throw result.error;
69
+ expect(result.error.detail.missingCapabilities).toEqual(['offscreenCanvas']);
70
+ }
71
+ });
72
+ });
@@ -0,0 +1,180 @@
1
+ import { defineComponent, World } from '@forgeax/engine-ecs';
2
+ import { defineSharedKernel, type KernelDispatchSpan } from '@forgeax/engine-ecs/shared';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { createKernelPool } from '../execution/kernel-pool';
5
+
6
+ const Position = defineComponent('ExecutionKernelPosition', { x: 'f32' });
7
+
8
+ function runPositionKernel(spans: readonly import('@forgeax/engine-ecs').QuerySpan[]): void {
9
+ for (const span of spans) {
10
+ const position = span.mut(Position);
11
+ for (let index = 0; index < span.length; index += 1) {
12
+ position.x[index] = (position.x[index] ?? 0) + 2;
13
+ }
14
+ }
15
+ }
16
+
17
+ const kernel = defineSharedKernel('https://example.test/position-kernel.mjs', {
18
+ name: 'execution-position-kernel',
19
+ queries: [{ write: [Position] }],
20
+ minimumRows: 1,
21
+ run: runPositionKernel,
22
+ });
23
+
24
+ interface TestMessage {
25
+ readonly kind: 'kernel-init' | 'kernel-preload' | 'kernel-job';
26
+ readonly ready?: Int32Array;
27
+ readonly control?: Int32Array;
28
+ readonly status?: Int32Array;
29
+ readonly jobIndex?: number;
30
+ }
31
+
32
+ interface TestJob {
33
+ readonly kind: 'kernel-job';
34
+ readonly binding: {
35
+ readonly length: number;
36
+ readonly write: Readonly<Record<string, Readonly<Record<string, Float32Array>>>>;
37
+ };
38
+ readonly control: Int32Array;
39
+ readonly status: Int32Array;
40
+ readonly jobIndex: number;
41
+ }
42
+
43
+ function sharedDispatchSpans(): {
44
+ readonly world: World;
45
+ readonly spans: readonly KernelDispatchSpan[];
46
+ } {
47
+ const world = new World({ storage: 'shared' });
48
+ for (let index = 0; index < 8; index += 1) {
49
+ world.spawn({ component: Position, data: { x: index } }).unwrap();
50
+ }
51
+ const query = world.query({ write: [Position] }).unwrap();
52
+ return {
53
+ world,
54
+ spans: [...query.spans().unwrap()].map((span) => ({ queryIndex: 0, span })),
55
+ };
56
+ }
57
+
58
+ function workerFactory(run: (job: TestJob) => void, preloadStatus = 1): () => Worker {
59
+ return () =>
60
+ ({
61
+ postMessage(message: TestMessage): void {
62
+ if (message.kind === 'kernel-init' && message.ready !== undefined) {
63
+ Atomics.add(message.ready, 0, 1);
64
+ Atomics.notify(message.ready, 0);
65
+ return;
66
+ }
67
+ if (
68
+ message.kind === 'kernel-preload' &&
69
+ message.control !== undefined &&
70
+ message.status !== undefined &&
71
+ message.jobIndex !== undefined
72
+ ) {
73
+ Atomics.store(message.status, message.jobIndex, preloadStatus);
74
+ Atomics.add(message.control, 0, 1);
75
+ Atomics.notify(message.control, 0);
76
+ return;
77
+ }
78
+ run(message as TestJob);
79
+ Atomics.add(message.control as Int32Array, 0, 1);
80
+ Atomics.notify(message.control as Int32Array, 0);
81
+ },
82
+ terminate(): void {},
83
+ }) as unknown as Worker;
84
+ }
85
+
86
+ describe('shared kernel pool', () => {
87
+ it('shards SAB spans without copying and joins all lanes', () => {
88
+ const { world, spans } = sharedDispatchSpans();
89
+ const pool = createKernelPool({
90
+ lanes: 2,
91
+ workerFactory: workerFactory((job) => {
92
+ const x = job.binding.write.ExecutionKernelPosition?.x;
93
+ if (x === undefined) throw new Error('position binding missing');
94
+ for (let index = 0; index < job.binding.length; index += 1) {
95
+ x[index] = (x[index] ?? 0) + 2;
96
+ }
97
+ Atomics.store(job.status, job.jobIndex, 1);
98
+ }),
99
+ });
100
+ pool.warmup?.(kernel);
101
+
102
+ const result = pool.execute(kernel, spans);
103
+ expect(result).toMatchObject({ mode: 'shared', dispatched: 2, completed: 2 });
104
+ expect(pool.takeLastDispatch()).toEqual(result);
105
+ expect(pool.takeLastDispatch()).toBeNull();
106
+ expect(
107
+ Array.from(world.query({ read: [Position] }).unwrap(), (row) => row.get(Position).x),
108
+ ).toEqual([2, 3, 4, 5, 6, 7, 8, 9]);
109
+ pool.dispose();
110
+ });
111
+
112
+ it('reports a partial write when one lane fails after another completes', () => {
113
+ const { spans } = sharedDispatchSpans();
114
+ const pool = createKernelPool({
115
+ lanes: 2,
116
+ workerFactory: workerFactory((job) => {
117
+ Atomics.store(job.status, job.jobIndex, job.jobIndex === 0 ? 1 : -1);
118
+ }),
119
+ });
120
+ pool.warmup?.(kernel);
121
+
122
+ expect(pool.execute(kernel, spans)).toMatchObject({
123
+ dispatched: 2,
124
+ completed: 1,
125
+ partialWrite: true,
126
+ });
127
+ pool.dispose();
128
+ });
129
+
130
+ it('fails a non-responsive lane at the bounded Atomics deadline', () => {
131
+ const { spans } = sharedDispatchSpans();
132
+ const pool = createKernelPool({
133
+ lanes: 1,
134
+ timeoutMs: 1,
135
+ workerFactory: () =>
136
+ ({
137
+ postMessage(message: TestMessage): void {
138
+ if (message.kind === 'kernel-init' && message.ready !== undefined) {
139
+ Atomics.add(message.ready, 0, 1);
140
+ Atomics.notify(message.ready, 0);
141
+ }
142
+ if (
143
+ message.kind === 'kernel-preload' &&
144
+ message.control !== undefined &&
145
+ message.status !== undefined &&
146
+ message.jobIndex !== undefined
147
+ ) {
148
+ Atomics.store(message.status, message.jobIndex, 1);
149
+ Atomics.add(message.control, 0, 1);
150
+ Atomics.notify(message.control, 0);
151
+ }
152
+ },
153
+ terminate(): void {},
154
+ }) as unknown as Worker,
155
+ });
156
+ pool.warmup?.(kernel);
157
+
158
+ expect(pool.execute(kernel, spans)).toMatchObject({
159
+ dispatched: 1,
160
+ completed: 0,
161
+ partialWrite: true,
162
+ });
163
+ pool.dispose();
164
+ });
165
+
166
+ it('rejects an invalid module during readiness before any kernel dispatch', async () => {
167
+ let dispatched = false;
168
+ const pool = createKernelPool({
169
+ lanes: 1,
170
+ workerFactory: workerFactory(() => {
171
+ dispatched = true;
172
+ }, -1),
173
+ });
174
+ pool.warmup?.(kernel);
175
+
176
+ await expect(pool.ready()).rejects.toThrow('module preflight failed');
177
+ expect(dispatched).toBe(false);
178
+ pool.dispose();
179
+ });
180
+ });
@@ -0,0 +1,32 @@
1
+ import { World } from '@forgeax/engine-ecs';
2
+ import { Camera } from '@forgeax/engine-render';
3
+ import { Transform } from '@forgeax/engine-scene';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { ensureFallbackCamera } from '../fallback-camera';
6
+
7
+ describe('ensureFallbackCamera', () => {
8
+ it('installs one neutral host fallback with the requested aspect', () => {
9
+ const world = new World();
10
+ const spawned = ensureFallbackCamera(world, 16 / 9);
11
+ expect(spawned).toBeDefined();
12
+ if (spawned === undefined) return;
13
+ expect(spawned.ok).toBe(true);
14
+ if (!spawned.ok) return;
15
+
16
+ expect(world.get(spawned.value, Transform).unwrap().pos).toEqual(new Float32Array([0, 0.6, 5]));
17
+ expect(world.get(spawned.value, Camera).unwrap().aspect).toBeCloseTo(16 / 9);
18
+ expect([...world.query({ with: [Camera] }).unwrap()]).toHaveLength(1);
19
+ });
20
+
21
+ it('preserves an authored scene camera', () => {
22
+ const world = new World();
23
+ const authored = world.spawn(
24
+ { component: Transform, data: { pos: [1, 2, 3] } },
25
+ { component: Camera, data: {} },
26
+ );
27
+ expect(authored.ok).toBe(true);
28
+
29
+ expect(ensureFallbackCamera(world, 16 / 9)).toBeUndefined();
30
+ expect([...world.query({ with: [Camera] }).unwrap()]).toHaveLength(1);
31
+ });
32
+ });