@forgeax/engine-app 0.1.2

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 (354) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +418 -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__/app-rebuild.test.d.ts +2 -0
  7. package/dist/__tests__/app-rebuild.test.d.ts.map +1 -0
  8. package/dist/__tests__/asset-registry-types.test-d.d.ts +2 -0
  9. package/dist/__tests__/asset-registry-types.test-d.d.ts.map +1 -0
  10. package/dist/__tests__/assets-runtime-assembly.integration.test.d.ts +2 -0
  11. package/dist/__tests__/assets-runtime-assembly.integration.test.d.ts.map +1 -0
  12. package/dist/__tests__/callback-deletion.unit.test.d.ts +2 -0
  13. package/dist/__tests__/callback-deletion.unit.test.d.ts.map +1 -0
  14. package/dist/__tests__/callback-inference.test-d.d.ts +2 -0
  15. package/dist/__tests__/callback-inference.test-d.d.ts.map +1 -0
  16. package/dist/__tests__/component-introspection.unit.test.d.ts +2 -0
  17. package/dist/__tests__/component-introspection.unit.test.d.ts.map +1 -0
  18. package/dist/__tests__/consumer-migration.unit.test.d.ts +2 -0
  19. package/dist/__tests__/consumer-migration.unit.test.d.ts.map +1 -0
  20. package/dist/__tests__/create-app-features.test.d.ts +2 -0
  21. package/dist/__tests__/create-app-features.test.d.ts.map +1 -0
  22. package/dist/__tests__/create-app-listener-sync.test.d.ts +2 -0
  23. package/dist/__tests__/create-app-listener-sync.test.d.ts.map +1 -0
  24. package/dist/__tests__/create-app-lock-provider.test.d.ts +2 -0
  25. package/dist/__tests__/create-app-lock-provider.test.d.ts.map +1 -0
  26. package/dist/__tests__/create-app-plugin.test.d.ts +2 -0
  27. package/dist/__tests__/create-app-plugin.test.d.ts.map +1 -0
  28. package/dist/__tests__/create-app-stop.test.d.ts +2 -0
  29. package/dist/__tests__/create-app-stop.test.d.ts.map +1 -0
  30. package/dist/__tests__/create-app-time.unit.test.d.ts +2 -0
  31. package/dist/__tests__/create-app-time.unit.test.d.ts.map +1 -0
  32. package/dist/__tests__/create-app.test-d.d.ts +2 -0
  33. package/dist/__tests__/create-app.test-d.d.ts.map +1 -0
  34. package/dist/__tests__/create-app.test.d.ts +2 -0
  35. package/dist/__tests__/create-app.test.d.ts.map +1 -0
  36. package/dist/__tests__/create-renderer-error.integration.test.d.ts +2 -0
  37. package/dist/__tests__/create-renderer-error.integration.test.d.ts.map +1 -0
  38. package/dist/__tests__/create-renderer-lifecycle.integration.test.d.ts +2 -0
  39. package/dist/__tests__/create-renderer-lifecycle.integration.test.d.ts.map +1 -0
  40. package/dist/__tests__/docs-gate-green.unit.test.d.ts +2 -0
  41. package/dist/__tests__/docs-gate-green.unit.test.d.ts.map +1 -0
  42. package/dist/__tests__/draw-source-headless-full-chain.test.d.ts +2 -0
  43. package/dist/__tests__/draw-source-headless-full-chain.test.d.ts.map +1 -0
  44. package/dist/__tests__/draw-source-injected-world-update-visibility.test.d.ts +2 -0
  45. package/dist/__tests__/draw-source-injected-world-update-visibility.test.d.ts.map +1 -0
  46. package/dist/__tests__/draw-source-single-world-regression.test.d.ts +2 -0
  47. package/dist/__tests__/draw-source-single-world-regression.test.d.ts.map +1 -0
  48. package/dist/__tests__/ecs-import.unit.test.d.ts +2 -0
  49. package/dist/__tests__/ecs-import.unit.test.d.ts.map +1 -0
  50. package/dist/__tests__/engine-worker-bootstrap-channel.unit.test.d.ts +2 -0
  51. package/dist/__tests__/engine-worker-bootstrap-channel.unit.test.d.ts.map +1 -0
  52. package/dist/__tests__/engine-worker-world-swap.unit.test.d.ts +2 -0
  53. package/dist/__tests__/engine-worker-world-swap.unit.test.d.ts.map +1 -0
  54. package/dist/__tests__/errors-pointer-lock-failed.test.d.ts +2 -0
  55. package/dist/__tests__/errors-pointer-lock-failed.test.d.ts.map +1 -0
  56. package/dist/__tests__/execution-bootstrap-isolation.unit.test.d.ts +2 -0
  57. package/dist/__tests__/execution-bootstrap-isolation.unit.test.d.ts.map +1 -0
  58. package/dist/__tests__/execution-error-detail.test-d.d.ts +2 -0
  59. package/dist/__tests__/execution-error-detail.test-d.d.ts.map +1 -0
  60. package/dist/__tests__/execution-error-detail.unit.test.d.ts +2 -0
  61. package/dist/__tests__/execution-error-detail.unit.test.d.ts.map +1 -0
  62. package/dist/__tests__/execution-frame-order.unit.test.d.ts +2 -0
  63. package/dist/__tests__/execution-frame-order.unit.test.d.ts.map +1 -0
  64. package/dist/__tests__/execution-frame-protocol.unit.test.d.ts +2 -0
  65. package/dist/__tests__/execution-frame-protocol.unit.test.d.ts.map +1 -0
  66. package/dist/__tests__/execution-input-sample.contract.test.d.ts +2 -0
  67. package/dist/__tests__/execution-input-sample.contract.test.d.ts.map +1 -0
  68. package/dist/__tests__/execution-measurement.unit.test.d.ts +2 -0
  69. package/dist/__tests__/execution-measurement.unit.test.d.ts.map +1 -0
  70. package/dist/__tests__/execution-public-api.test-d.d.ts +2 -0
  71. package/dist/__tests__/execution-public-api.test-d.d.ts.map +1 -0
  72. package/dist/__tests__/execution-public-api.unit.test.d.ts +2 -0
  73. package/dist/__tests__/execution-public-api.unit.test.d.ts.map +1 -0
  74. package/dist/__tests__/execution-rebuild.contract.test.d.ts +2 -0
  75. package/dist/__tests__/execution-rebuild.contract.test.d.ts.map +1 -0
  76. package/dist/__tests__/execution-report.contract.test.d.ts +2 -0
  77. package/dist/__tests__/execution-report.contract.test.d.ts.map +1 -0
  78. package/dist/__tests__/execution-report.types.test-d.d.ts +2 -0
  79. package/dist/__tests__/execution-report.types.test-d.d.ts.map +1 -0
  80. package/dist/__tests__/execution-schema-freshness.unit.test.d.ts +2 -0
  81. package/dist/__tests__/execution-schema-freshness.unit.test.d.ts.map +1 -0
  82. package/dist/__tests__/execution-selector.unit.test.d.ts +2 -0
  83. package/dist/__tests__/execution-selector.unit.test.d.ts.map +1 -0
  84. package/dist/__tests__/execution-shared-kernel.unit.test.d.ts +2 -0
  85. package/dist/__tests__/execution-shared-kernel.unit.test.d.ts.map +1 -0
  86. package/dist/__tests__/execution-world-health-owner.test-d.d.ts +2 -0
  87. package/dist/__tests__/execution-world-health-owner.test-d.d.ts.map +1 -0
  88. package/dist/__tests__/fallback-camera.unit.test.d.ts +2 -0
  89. package/dist/__tests__/fallback-camera.unit.test.d.ts.map +1 -0
  90. package/dist/__tests__/frame-loop-time-elapsed.test.d.ts +2 -0
  91. package/dist/__tests__/frame-loop-time-elapsed.test.d.ts.map +1 -0
  92. package/dist/__tests__/frame-loop-time.unit.test.d.ts +2 -0
  93. package/dist/__tests__/frame-loop-time.unit.test.d.ts.map +1 -0
  94. package/dist/__tests__/frame-loop-world-array.test.d.ts +2 -0
  95. package/dist/__tests__/frame-loop-world-array.test.d.ts.map +1 -0
  96. package/dist/__tests__/kernel-worker-runtime.unit.test.d.ts +2 -0
  97. package/dist/__tests__/kernel-worker-runtime.unit.test.d.ts.map +1 -0
  98. package/dist/__tests__/migration-surface-inventory.unit.test.d.ts +2 -0
  99. package/dist/__tests__/migration-surface-inventory.unit.test.d.ts.map +1 -0
  100. package/dist/__tests__/plugin-policy.integration.test.d.ts +2 -0
  101. package/dist/__tests__/plugin-policy.integration.test.d.ts.map +1 -0
  102. package/dist/__tests__/plugin-profiles.unit.test.d.ts +2 -0
  103. package/dist/__tests__/plugin-profiles.unit.test.d.ts.map +1 -0
  104. package/dist/__tests__/profiler-default-off.test.d.ts +2 -0
  105. package/dist/__tests__/profiler-default-off.test.d.ts.map +1 -0
  106. package/dist/__tests__/profiler-frame-token.test.d.ts +2 -0
  107. package/dist/__tests__/profiler-frame-token.test.d.ts.map +1 -0
  108. package/dist/__tests__/profiler-phase-catalog.test.d.ts +2 -0
  109. package/dist/__tests__/profiler-phase-catalog.test.d.ts.map +1 -0
  110. package/dist/__tests__/profiler-phase-integration.test.d.ts +2 -0
  111. package/dist/__tests__/profiler-phase-integration.test.d.ts.map +1 -0
  112. package/dist/__tests__/profiler-remote-bridge.test.d.ts +2 -0
  113. package/dist/__tests__/profiler-remote-bridge.test.d.ts.map +1 -0
  114. package/dist/__tests__/remote-integration.test.d.ts +2 -0
  115. package/dist/__tests__/remote-integration.test.d.ts.map +1 -0
  116. package/dist/__tests__/remote-serve-flag.test.d.ts +2 -0
  117. package/dist/__tests__/remote-serve-flag.test.d.ts.map +1 -0
  118. package/dist/__tests__/rhi-capture-capability.test-d.d.ts +2 -0
  119. package/dist/__tests__/rhi-capture-capability.test-d.d.ts.map +1 -0
  120. package/dist/__tests__/rhi-capture-host.integration.test.d.ts +2 -0
  121. package/dist/__tests__/rhi-capture-host.integration.test.d.ts.map +1 -0
  122. package/dist/__tests__/rhi-capture.browser.test.d.ts +2 -0
  123. package/dist/__tests__/rhi-capture.browser.test.d.ts.map +1 -0
  124. package/dist/__tests__/surface-handoff.test.d.ts +2 -0
  125. package/dist/__tests__/surface-handoff.test.d.ts.map +1 -0
  126. package/dist/__tests__/types-m2.test-d.d.ts +2 -0
  127. package/dist/__tests__/types-m2.test-d.d.ts.map +1 -0
  128. package/dist/__tests__/visibility-remote.integration.test.d.ts +2 -0
  129. package/dist/__tests__/visibility-remote.integration.test.d.ts.map +1 -0
  130. package/dist/__tests__/worker-execution-stop.test.d.ts +2 -0
  131. package/dist/__tests__/worker-execution-stop.test.d.ts.map +1 -0
  132. package/dist/animation-asset-lookup.d.ts +8 -0
  133. package/dist/animation-asset-lookup.d.ts.map +1 -0
  134. package/dist/assets-runtime-assembly.d.ts +19 -0
  135. package/dist/assets-runtime-assembly.d.ts.map +1 -0
  136. package/dist/canvas-policy.d.ts +4 -0
  137. package/dist/canvas-policy.d.ts.map +1 -0
  138. package/dist/create-app.d.ts +57 -0
  139. package/dist/create-app.d.ts.map +1 -0
  140. package/dist/engine-worker-runtime.mjs +884 -0
  141. package/dist/engine-worker-runtime.mjs.map +1 -0
  142. package/dist/errors.d.ts +263 -0
  143. package/dist/errors.d.ts.map +1 -0
  144. package/dist/execution/attached-world-swap.d.ts +21 -0
  145. package/dist/execution/attached-world-swap.d.ts.map +1 -0
  146. package/dist/execution/bootstrap-entry.d.ts +30 -0
  147. package/dist/execution/bootstrap-entry.d.ts.map +1 -0
  148. package/dist/execution/capabilities.d.ts +5 -0
  149. package/dist/execution/capabilities.d.ts.map +1 -0
  150. package/dist/execution/control.d.ts +14 -0
  151. package/dist/execution/control.d.ts.map +1 -0
  152. package/dist/execution/engine-worker-runtime.d.ts +2 -0
  153. package/dist/execution/engine-worker-runtime.d.ts.map +1 -0
  154. package/dist/execution/engine-worker.d.ts +23 -0
  155. package/dist/execution/engine-worker.d.ts.map +1 -0
  156. package/dist/execution/host-controller.d.ts +16 -0
  157. package/dist/execution/host-controller.d.ts.map +1 -0
  158. package/dist/execution/index.d.ts +10 -0
  159. package/dist/execution/index.d.ts.map +1 -0
  160. package/dist/execution/kernel-pool.d.ts +14 -0
  161. package/dist/execution/kernel-pool.d.ts.map +1 -0
  162. package/dist/execution/kernel-worker-runtime.d.ts +2 -0
  163. package/dist/execution/kernel-worker-runtime.d.ts.map +1 -0
  164. package/dist/execution/measurement.d.ts +7 -0
  165. package/dist/execution/measurement.d.ts.map +1 -0
  166. package/dist/execution/protocol.d.ts +84 -0
  167. package/dist/execution/protocol.d.ts.map +1 -0
  168. package/dist/execution/report.d.ts +5 -0
  169. package/dist/execution/report.d.ts.map +1 -0
  170. package/dist/execution/schema.d.ts +4 -0
  171. package/dist/execution/schema.d.ts.map +1 -0
  172. package/dist/execution/selector.d.ts +10 -0
  173. package/dist/execution/selector.d.ts.map +1 -0
  174. package/dist/execution/types.d.ts +107 -0
  175. package/dist/execution/types.d.ts.map +1 -0
  176. package/dist/fallback-camera.d.ts +8 -0
  177. package/dist/fallback-camera.d.ts.map +1 -0
  178. package/dist/game-context.d.ts +98 -0
  179. package/dist/game-context.d.ts.map +1 -0
  180. package/dist/index.d.ts +24 -0
  181. package/dist/index.d.ts.map +1 -0
  182. package/dist/index.mjs +3985 -0
  183. package/dist/index.mjs.map +1 -0
  184. package/dist/input-map-plugin.d.ts +5 -0
  185. package/dist/input-map-plugin.d.ts.map +1 -0
  186. package/dist/input-plugin.d.ts +4 -0
  187. package/dist/input-plugin.d.ts.map +1 -0
  188. package/dist/internal/assembled-engine-profile.d.ts +5 -0
  189. package/dist/internal/assembled-engine-profile.d.ts.map +1 -0
  190. package/dist/internal/assets-world-plugin.d.ts +21 -0
  191. package/dist/internal/assets-world-plugin.d.ts.map +1 -0
  192. package/dist/internal/browser-remote-bridge.d.ts +25 -0
  193. package/dist/internal/browser-remote-bridge.d.ts.map +1 -0
  194. package/dist/internal/browser-rhi-debug-runtime.d.ts +18 -0
  195. package/dist/internal/browser-rhi-debug-runtime.d.ts.map +1 -0
  196. package/dist/internal/component-introspection.d.ts +24 -0
  197. package/dist/internal/component-introspection.d.ts.map +1 -0
  198. package/dist/internal/ecs-import.d.ts +6 -0
  199. package/dist/internal/ecs-import.d.ts.map +1 -0
  200. package/dist/internal/engine-profile-common.d.ts +9 -0
  201. package/dist/internal/engine-profile-common.d.ts.map +1 -0
  202. package/dist/internal/error-fanout.d.ts +66 -0
  203. package/dist/internal/error-fanout.d.ts.map +1 -0
  204. package/dist/internal/frame-loop.d.ts +35 -0
  205. package/dist/internal/frame-loop.d.ts.map +1 -0
  206. package/dist/internal/input-attach.d.ts +44 -0
  207. package/dist/internal/input-attach.d.ts.map +1 -0
  208. package/dist/internal/main-engine-profile.d.ts +15 -0
  209. package/dist/internal/main-engine-profile.d.ts.map +1 -0
  210. package/dist/internal/remote-serve-flag.d.ts +23 -0
  211. package/dist/internal/remote-serve-flag.d.ts.map +1 -0
  212. package/dist/internal/remote-server-plugin.d.ts +8 -0
  213. package/dist/internal/remote-server-plugin.d.ts.map +1 -0
  214. package/dist/internal/rhi-capture.d.ts +19 -0
  215. package/dist/internal/rhi-capture.d.ts.map +1 -0
  216. package/dist/internal/rhi-debug-flag.d.ts +18 -0
  217. package/dist/internal/rhi-debug-flag.d.ts.map +1 -0
  218. package/dist/internal/worker-engine-profile.d.ts +13 -0
  219. package/dist/internal/worker-engine-profile.d.ts.map +1 -0
  220. package/dist/kernel-worker-runtime.mjs +69 -0
  221. package/dist/kernel-worker-runtime.mjs.map +1 -0
  222. package/dist/load-game-errors.d.ts +131 -0
  223. package/dist/load-game-errors.d.ts.map +1 -0
  224. package/dist/load-game.d.ts +34 -0
  225. package/dist/load-game.d.ts.map +1 -0
  226. package/dist/plugin-factories.d.ts +3 -0
  227. package/dist/plugin-factories.d.ts.map +1 -0
  228. package/dist/tool-preview/bootstrap.d.ts +113 -0
  229. package/dist/tool-preview/bootstrap.d.ts.map +1 -0
  230. package/dist/tool-preview/evidence.d.ts +20 -0
  231. package/dist/tool-preview/evidence.d.ts.map +1 -0
  232. package/dist/tool-preview/framing.d.ts +13 -0
  233. package/dist/tool-preview/framing.d.ts.map +1 -0
  234. package/dist/tool-preview/recipe.d.ts +54 -0
  235. package/dist/tool-preview/recipe.d.ts.map +1 -0
  236. package/dist/types.d.ts +444 -0
  237. package/dist/types.d.ts.map +1 -0
  238. package/package.json +86 -0
  239. package/schema/execution-report.schema.json +168 -0
  240. package/src/__tests__/animation-asset-lookup.unit.test.ts +20 -0
  241. package/src/__tests__/app-rebuild.test.ts +31 -0
  242. package/src/__tests__/asset-registry-types.test-d.ts +16 -0
  243. package/src/__tests__/assets-runtime-assembly.integration.test.ts +68 -0
  244. package/src/__tests__/callback-deletion.unit.test.ts +14 -0
  245. package/src/__tests__/callback-inference.test-d.ts +32 -0
  246. package/src/__tests__/component-introspection.unit.test.ts +56 -0
  247. package/src/__tests__/consumer-migration.unit.test.ts +21 -0
  248. package/src/__tests__/create-app-features.test.ts +40 -0
  249. package/src/__tests__/create-app-listener-sync.test.ts +377 -0
  250. package/src/__tests__/create-app-lock-provider.test.ts +212 -0
  251. package/src/__tests__/create-app-plugin.test.ts +217 -0
  252. package/src/__tests__/create-app-stop.test.ts +617 -0
  253. package/src/__tests__/create-app-time.unit.test.ts +26 -0
  254. package/src/__tests__/create-app.test-d.ts +82 -0
  255. package/src/__tests__/create-app.test.ts +346 -0
  256. package/src/__tests__/create-renderer-error.integration.test.ts +24 -0
  257. package/src/__tests__/create-renderer-lifecycle.integration.test.ts +19 -0
  258. package/src/__tests__/docs-gate-green.unit.test.ts +61 -0
  259. package/src/__tests__/draw-source-headless-full-chain.test.ts +226 -0
  260. package/src/__tests__/draw-source-injected-world-update-visibility.test.ts +182 -0
  261. package/src/__tests__/draw-source-single-world-regression.test.ts +185 -0
  262. package/src/__tests__/ecs-import.unit.test.ts +40 -0
  263. package/src/__tests__/engine-worker-bootstrap-channel.unit.test.ts +74 -0
  264. package/src/__tests__/engine-worker-world-swap.unit.test.ts +76 -0
  265. package/src/__tests__/errors-pointer-lock-failed.test.ts +268 -0
  266. package/src/__tests__/execution-bootstrap-isolation.unit.test.ts +80 -0
  267. package/src/__tests__/execution-error-detail.test-d.ts +13 -0
  268. package/src/__tests__/execution-error-detail.unit.test.ts +35 -0
  269. package/src/__tests__/execution-frame-order.unit.test.ts +38 -0
  270. package/src/__tests__/execution-frame-protocol.unit.test.ts +61 -0
  271. package/src/__tests__/execution-input-sample.contract.test.ts +23 -0
  272. package/src/__tests__/execution-measurement.unit.test.ts +20 -0
  273. package/src/__tests__/execution-public-api.test-d.ts +10 -0
  274. package/src/__tests__/execution-public-api.unit.test.ts +9 -0
  275. package/src/__tests__/execution-rebuild.contract.test.ts +36 -0
  276. package/src/__tests__/execution-report.contract.test.ts +50 -0
  277. package/src/__tests__/execution-report.types.test-d.ts +16 -0
  278. package/src/__tests__/execution-schema-freshness.unit.test.ts +20 -0
  279. package/src/__tests__/execution-selector.unit.test.ts +72 -0
  280. package/src/__tests__/execution-shared-kernel.unit.test.ts +180 -0
  281. package/src/__tests__/execution-world-health-owner.test-d.ts +9 -0
  282. package/src/__tests__/fallback-camera.unit.test.ts +32 -0
  283. package/src/__tests__/frame-loop-time-elapsed.test.ts +115 -0
  284. package/src/__tests__/frame-loop-time.unit.test.ts +61 -0
  285. package/src/__tests__/frame-loop-world-array.test.ts +441 -0
  286. package/src/__tests__/kernel-worker-runtime.unit.test.ts +50 -0
  287. package/src/__tests__/migration-surface-inventory.unit.test.ts +13 -0
  288. package/src/__tests__/plugin-policy.integration.test.ts +15 -0
  289. package/src/__tests__/plugin-profiles.unit.test.ts +190 -0
  290. package/src/__tests__/profiler-default-off.test.ts +53 -0
  291. package/src/__tests__/profiler-frame-token.test.ts +248 -0
  292. package/src/__tests__/profiler-phase-catalog.test.ts +30 -0
  293. package/src/__tests__/profiler-phase-integration.test.ts +129 -0
  294. package/src/__tests__/profiler-remote-bridge.test.ts +31 -0
  295. package/src/__tests__/remote-integration.test.ts +224 -0
  296. package/src/__tests__/remote-serve-flag.test.ts +24 -0
  297. package/src/__tests__/rhi-capture-capability.test-d.ts +22 -0
  298. package/src/__tests__/rhi-capture-host.integration.test.ts +157 -0
  299. package/src/__tests__/rhi-capture.browser.test.ts +51 -0
  300. package/src/__tests__/surface-handoff.test.ts +92 -0
  301. package/src/__tests__/types-m2.test-d.ts +81 -0
  302. package/src/__tests__/visibility-remote.integration.test.ts +119 -0
  303. package/src/__tests__/worker-execution-stop.test.ts +116 -0
  304. package/src/animation-asset-lookup.ts +13 -0
  305. package/src/assets-runtime-assembly.ts +70 -0
  306. package/src/canvas-policy.ts +16 -0
  307. package/src/create-app.ts +1351 -0
  308. package/src/errors.ts +454 -0
  309. package/src/execution/attached-world-swap.ts +67 -0
  310. package/src/execution/bootstrap-entry.ts +122 -0
  311. package/src/execution/capabilities.ts +102 -0
  312. package/src/execution/control.ts +52 -0
  313. package/src/execution/engine-worker-runtime.ts +400 -0
  314. package/src/execution/engine-worker.ts +161 -0
  315. package/src/execution/host-controller.ts +454 -0
  316. package/src/execution/index.ts +43 -0
  317. package/src/execution/kernel-pool.ts +236 -0
  318. package/src/execution/kernel-worker-runtime.ts +103 -0
  319. package/src/execution/measurement.ts +39 -0
  320. package/src/execution/protocol.ts +131 -0
  321. package/src/execution/report.ts +66 -0
  322. package/src/execution/schema.ts +168 -0
  323. package/src/execution/selector.ts +98 -0
  324. package/src/execution/types.ts +146 -0
  325. package/src/fallback-camera.ts +20 -0
  326. package/src/game-context.ts +126 -0
  327. package/src/index.ts +176 -0
  328. package/src/input-map-plugin.ts +21 -0
  329. package/src/input-plugin.ts +26 -0
  330. package/src/internal/assembled-engine-profile.ts +22 -0
  331. package/src/internal/assets-world-plugin.ts +79 -0
  332. package/src/internal/browser-remote-bridge.ts +267 -0
  333. package/src/internal/browser-rhi-debug-runtime.ts +67 -0
  334. package/src/internal/component-introspection.ts +116 -0
  335. package/src/internal/ecs-import.ts +41 -0
  336. package/src/internal/engine-profile-common.ts +10 -0
  337. package/src/internal/error-fanout.ts +129 -0
  338. package/src/internal/frame-loop.ts +525 -0
  339. package/src/internal/input-attach.ts +104 -0
  340. package/src/internal/main-engine-profile.ts +80 -0
  341. package/src/internal/remote-serve-flag.ts +50 -0
  342. package/src/internal/remote-server-plugin.ts +16 -0
  343. package/src/internal/rhi-capture.ts +211 -0
  344. package/src/internal/rhi-debug-flag.ts +40 -0
  345. package/src/internal/worker-engine-profile.ts +47 -0
  346. package/src/load-game-errors.ts +211 -0
  347. package/src/load-game.ts +115 -0
  348. package/src/plugin-factories.ts +2 -0
  349. package/src/tool-preview/bootstrap.ts +848 -0
  350. package/src/tool-preview/evidence.ts +59 -0
  351. package/src/tool-preview/framing.ts +52 -0
  352. package/src/tool-preview/recipe.ts +137 -0
  353. package/src/types.ts +505 -0
  354. package/src/vite-env.d.ts +12 -0
@@ -0,0 +1,82 @@
1
+ // create-app.test-d.ts -- compile-time assertions for w2 acceptanceCheck.
2
+ //
3
+ // Anchors:
4
+ // - createApp(canvas, opts?) returns
5
+ // Promise<Result<App, AppError | RhiError | EngineEnvironmentError>>
6
+ // per AC-01 (canvas thin wrapper widens the error union with
7
+ // EngineEnvironmentError; M2 plugin-system-unify D-7 widens it with
8
+ // AppError for Cordis activation failures).
9
+ // - createApp({ renderer, world }) returns
10
+ // Promise<Result<App, AppError | RhiError>>
11
+ // per AC-02 (assemble form excludes EngineEnvironmentError -- renderer
12
+ // host-managed).
13
+ // - 'tagName' in arg dispatch lands the right overload at the call site
14
+ // (HTMLCanvasElement -> canvas form; AppAssembleArgs -> assemble form).
15
+ //
16
+ // charter awareness:
17
+ // - P3 explicit failure: Result envelope shape is type-level (no message strings).
18
+ // - P4 consistent abstraction: both routes return the same App handle.
19
+
20
+ import type { World } from '@forgeax/engine-ecs';
21
+ import type { Renderer, RendererOptions } from '@forgeax/engine-render';
22
+ import type { RhiError } from '@forgeax/engine-rhi/errors';
23
+ import type { EngineEnvironmentError } from '@forgeax/engine-runtime';
24
+ import type { Result } from '@forgeax/engine-types';
25
+ import { describe, expectTypeOf, it } from 'vitest';
26
+ import type { AssetRuntimeAssemblyError } from '../assets-runtime-assembly';
27
+ import { type App, type AppError, createApp } from '../index';
28
+ import type { RhiCaptureHook } from '../types';
29
+
30
+ describe('createApp double-SSOT entry signatures (w2 acceptanceCheck)', () => {
31
+ it('canvas form returns the App boundary error union', () => {
32
+ const canvas = null as unknown as HTMLCanvasElement;
33
+ const ret = createApp(canvas);
34
+ expectTypeOf(ret).toEqualTypeOf<
35
+ Promise<Result<App, AppError | RhiError | AssetRuntimeAssemblyError | EngineEnvironmentError>>
36
+ >();
37
+ });
38
+
39
+ it('assemble form returns the App boundary error union', () => {
40
+ const renderer = null as unknown as Renderer;
41
+ const world = null as unknown as World;
42
+ const ret = createApp({ renderer, world });
43
+ expectTypeOf(ret).toEqualTypeOf<
44
+ Promise<Result<App, AppError | RhiError | AssetRuntimeAssemblyError>>
45
+ >();
46
+ });
47
+
48
+ it('app.renderer / app.world are reference-equal types at the call site (AC-09)', async () => {
49
+ const renderer = null as unknown as Renderer;
50
+ const world = null as unknown as World;
51
+ const result = await createApp({ renderer, world });
52
+ if (result.ok) {
53
+ expectTypeOf(result.value.renderer).toEqualTypeOf<Renderer>();
54
+ expectTypeOf(result.value.world).toEqualTypeOf<World>();
55
+ }
56
+ });
57
+
58
+ it('main and Worker execution share the same global capture hook shape', () => {
59
+ const captureFrame = (globalThis as { __forgeax?: RhiCaptureHook }).__forgeax?.captureFrame;
60
+ expectTypeOf(captureFrame).toEqualTypeOf<RhiCaptureHook['captureFrame'] | undefined>();
61
+ });
62
+ });
63
+
64
+ // feat-20260608-create-app-param-surface-trim / M1 / AC-02 + TASK-006:
65
+ // `RendererOptions.clearColor` was deleted in this feat. AI users that
66
+ // pass `{ clearColor: ... }` to `createRenderer` / `createApp` get a
67
+ // TS2353 excess-property error at compile time (no shim, no
68
+ // deprecation window per AGENTS.md §Change stance).
69
+ describe('RendererOptions.clearColor removal (AC-02 + TASK-006)', () => {
70
+ it('RendererOptions does not carry a `clearColor` field', () => {
71
+ // The keyof of RendererOptions must not include 'clearColor'.
72
+ type Keys = keyof RendererOptions;
73
+ type HasClearColor = 'clearColor' extends Keys ? true : false;
74
+ expectTypeOf<HasClearColor>().toEqualTypeOf<false>();
75
+ });
76
+
77
+ it('passing `clearColor` as an object literal is a compile-time error (TS2353)', () => {
78
+ // @ts-expect-error -- 'clearColor' is not assignable to RendererOptions
79
+ const opts: RendererOptions = { clearColor: [0, 0, 0, 1] };
80
+ void opts;
81
+ });
82
+ });
@@ -0,0 +1,346 @@
1
+ // M3 / w15: FORGEAX_ENGINE_RHI_DEBUG guard three-segment dual-source evaluation.
2
+ //
3
+ // The canvas-form guard in create-app.ts reads the flag from two sources with a
4
+ // precise precedence + short-circuit shape (plan-strategy D-4):
5
+ //
6
+ // (typeof import.meta !== 'undefined' && import.meta.env?.FORGEAX_ENGINE_RHI_DEBUG)
7
+ // ?? globalThis.process?.env?.FORGEAX_ENGINE_RHI_DEBUG
8
+ //
9
+ // That expression is extracted into the pure `resolveRhiDebugFlag` helper so the
10
+ // three deployment scenarios can be exercised without a real bundler / runtime:
11
+ // (a) browser: import.meta.env replaced by the vite define -> '1' wins
12
+ // (b) dawn-node: import.meta absent (modelled as undefined env) -> falls
13
+ // through to globalThis.process.env
14
+ // (c) unset: neither source carries '1' -> undefined -> guard does not fire
15
+ //
16
+ // C5 (plan-strategy): the `typeof import.meta !== 'undefined'` prefix must
17
+ // short-circuit before any `.env` access, because under dawn-node `import.meta`
18
+ // itself can be undefined; passing `undefined` for the import.meta.env arg is
19
+ // the unit-level model of that prefix evaluating to false.
20
+ //
21
+ // OOS-1/2: only the DevTools-trigger path is in scope; this test does not
22
+ // exercise external HTTP triggers or in-page buttons.
23
+
24
+ import { World } from '@forgeax/engine-ecs';
25
+ import { Camera, orthographic, perspective } from '@forgeax/engine-render';
26
+ import { describe, expect, it } from 'vitest';
27
+
28
+ import { createApp, syncCameraAspect, syncCanvasDrawingBuffer } from '../create-app';
29
+ import { AppError } from '../errors';
30
+ import { resolveRhiDebugFlag } from '../internal/rhi-debug-flag';
31
+
32
+ describe('create-app.test.ts', () => {
33
+ describe('resolveRhiDebugFlag three-segment dual-source (w15 / D-4)', () => {
34
+ it('(a) browser: import.meta.env.FORGEAX_ENGINE_RHI_DEBUG === "1" wins', () => {
35
+ const flag = resolveRhiDebugFlag(
36
+ { FORGEAX_ENGINE_RHI_DEBUG: '1' },
37
+ { FORGEAX_ENGINE_RHI_DEBUG: undefined },
38
+ );
39
+ expect(flag).toBe('1');
40
+ });
41
+
42
+ it('(b) dawn-node: import.meta absent -> falls through to process.env', () => {
43
+ // `undefined` models `typeof import.meta === 'undefined'` short-circuiting
44
+ // the first operand to a falsy value, so the `??` chain consults process.env.
45
+ const flag = resolveRhiDebugFlag(undefined, { FORGEAX_ENGINE_RHI_DEBUG: '1' });
46
+ expect(flag).toBe('1');
47
+ });
48
+
49
+ it('(b2) import.meta.env present but flag unset -> still falls through to process.env', () => {
50
+ // `import.meta.env` exists (vite always injects it) but the key is absent;
51
+ // the first operand is `undefined`, so `??` consults process.env.
52
+ const flag = resolveRhiDebugFlag({}, { FORGEAX_ENGINE_RHI_DEBUG: '1' });
53
+ expect(flag).toBe('1');
54
+ });
55
+
56
+ it('(c) unset: neither source carries the flag -> undefined (guard does not fire)', () => {
57
+ expect(resolveRhiDebugFlag(undefined, undefined)).toBeUndefined();
58
+ expect(resolveRhiDebugFlag({}, {})).toBeUndefined();
59
+ expect(
60
+ resolveRhiDebugFlag(
61
+ { FORGEAX_ENGINE_RHI_DEBUG: undefined },
62
+ { FORGEAX_ENGINE_RHI_DEBUG: undefined },
63
+ ),
64
+ ).toBeUndefined();
65
+ });
66
+
67
+ it('import.meta.env wins over process.env when both set (precedence)', () => {
68
+ // Browser path takes precedence: the `??` only consults process.env when
69
+ // the first operand is null/undefined, and '1' is neither.
70
+ const flag = resolveRhiDebugFlag(
71
+ { FORGEAX_ENGINE_RHI_DEBUG: '1' },
72
+ { FORGEAX_ENGINE_RHI_DEBUG: '0' },
73
+ );
74
+ expect(flag).toBe('1');
75
+ });
76
+
77
+ it('a non-"1" value is returned verbatim (guard compares === "1" at call site)', () => {
78
+ // resolveRhiDebugFlag returns the raw flag; the create-app guard does the
79
+ // `=== '1'` comparison. A '0' value must therefore not be coerced to fire.
80
+ expect(resolveRhiDebugFlag({ FORGEAX_ENGINE_RHI_DEBUG: '0' }, undefined)).toBe('0');
81
+ });
82
+ });
83
+
84
+ describe('zero-injection when flag unset (c) (w15 / F-3)', () => {
85
+ it('globalThis.__forgeax is undefined before any flagged createApp runs', () => {
86
+ // With the flag unset (the default unit-test environment: no vite define,
87
+ // no process.env override), the canvas form never touches globalThis, so
88
+ // the capture entry point does not exist. A DevTools caller invoking it
89
+ // would hit a TypeError -- the explicit-failure contract (charter P3).
90
+ const g = globalThis as { __forgeax?: { captureFrame?: unknown } };
91
+ expect(g.__forgeax).toBeUndefined();
92
+ expect(() => {
93
+ // Reading .captureFrame off an undefined __forgeax throws synchronously.
94
+ (g.__forgeax as { captureFrame: (n: number) => unknown }).captureFrame(1);
95
+ }).toThrow(TypeError);
96
+ });
97
+ });
98
+
99
+ describe('syncCanvasDrawingBuffer host boundary', () => {
100
+ it('matches the physical drawing buffer to CSS size at the device pixel ratio', () => {
101
+ const canvas = {
102
+ clientWidth: 800,
103
+ clientHeight: 400,
104
+ width: 300,
105
+ height: 150,
106
+ style: { width: '800px', height: '400px' },
107
+ };
108
+ const before = globalThis.devicePixelRatio;
109
+ Object.defineProperty(globalThis, 'devicePixelRatio', { configurable: true, value: 2 });
110
+ try {
111
+ syncCanvasDrawingBuffer(canvas);
112
+ expect(canvas.width).toBe(1600);
113
+ expect(canvas.height).toBe(800);
114
+ } finally {
115
+ Object.defineProperty(globalThis, 'devicePixelRatio', {
116
+ configurable: true,
117
+ value: before,
118
+ });
119
+ }
120
+ });
121
+
122
+ it('applies a Host pixel-ratio cap without changing the default path', () => {
123
+ const canvas = {
124
+ clientWidth: 800,
125
+ clientHeight: 400,
126
+ width: 300,
127
+ height: 150,
128
+ style: { width: '800px', height: '400px' },
129
+ };
130
+ const before = globalThis.devicePixelRatio;
131
+ Object.defineProperty(globalThis, 'devicePixelRatio', { configurable: true, value: 2 });
132
+ try {
133
+ syncCanvasDrawingBuffer(canvas, 1.5);
134
+ expect(canvas.width).toBe(1200);
135
+ expect(canvas.height).toBe(600);
136
+ } finally {
137
+ Object.defineProperty(globalThis, 'devicePixelRatio', {
138
+ configurable: true,
139
+ value: before,
140
+ });
141
+ }
142
+ });
143
+
144
+ it('allows the Host to downsample a GPU-bound canvas below CSS pixel density', () => {
145
+ const canvas = {
146
+ clientWidth: 800,
147
+ clientHeight: 400,
148
+ width: 300,
149
+ height: 150,
150
+ style: { width: '800px', height: '400px' },
151
+ };
152
+ const before = globalThis.devicePixelRatio;
153
+ Object.defineProperty(globalThis, 'devicePixelRatio', { configurable: true, value: 2 });
154
+ try {
155
+ syncCanvasDrawingBuffer(canvas, 0.875);
156
+ expect(canvas.width).toBe(700);
157
+ expect(canvas.height).toBe(350);
158
+ } finally {
159
+ Object.defineProperty(globalThis, 'devicePixelRatio', {
160
+ configurable: true,
161
+ value: before,
162
+ });
163
+ }
164
+ });
165
+
166
+ it('rejects an invalid Host pixel-ratio cap before touching a detached canvas', async () => {
167
+ const result = await createApp(
168
+ { tagName: 'CANVAS', isConnected: false } as unknown as HTMLCanvasElement,
169
+ { maxCanvasPixelRatio: 0 },
170
+ );
171
+ expect(result.ok).toBe(false);
172
+ if (!result.ok && result.error instanceof AppError) {
173
+ expect(result.error.code).toBe('app-invalid-canvas-pixel-ratio');
174
+ if (result.error.code === 'app-invalid-canvas-pixel-ratio') {
175
+ expect(result.error.detail.value).toBe(0);
176
+ }
177
+ }
178
+ });
179
+
180
+ it('leaves a zero-sized host untouched', () => {
181
+ const canvas = { clientWidth: 0, clientHeight: 400, width: 300, height: 150 };
182
+ syncCanvasDrawingBuffer(canvas);
183
+ expect(canvas.width).toBe(300);
184
+ expect(canvas.height).toBe(150);
185
+ });
186
+
187
+ it('leaves a headless canvas without CSS dimensions untouched', () => {
188
+ const canvas = { width: 320, height: 180 } as HTMLCanvasElement;
189
+ syncCanvasDrawingBuffer(canvas);
190
+ expect(canvas.width).toBe(320);
191
+ expect(canvas.height).toBe(180);
192
+ });
193
+
194
+ it('preserves an intrinsic canvas drawing buffer without CSS dimensions', () => {
195
+ let width = 256;
196
+ let height = 256;
197
+ const canvas = {
198
+ get clientWidth() {
199
+ return width;
200
+ },
201
+ get clientHeight() {
202
+ return height;
203
+ },
204
+ get width() {
205
+ return width;
206
+ },
207
+ set width(value: number) {
208
+ width = value;
209
+ },
210
+ get height() {
211
+ return height;
212
+ },
213
+ set height(value: number) {
214
+ height = value;
215
+ },
216
+ };
217
+ const before = globalThis.devicePixelRatio;
218
+ Object.defineProperty(globalThis, 'devicePixelRatio', { configurable: true, value: 2 });
219
+ try {
220
+ syncCanvasDrawingBuffer(canvas);
221
+ syncCanvasDrawingBuffer(canvas);
222
+ expect(width).toBe(256);
223
+ expect(height).toBe(256);
224
+ } finally {
225
+ Object.defineProperty(globalThis, 'devicePixelRatio', {
226
+ configurable: true,
227
+ value: before,
228
+ });
229
+ }
230
+ });
231
+
232
+ it('does not repeatedly scale a CSS-sized canvas when layout follows width writes', () => {
233
+ let width = 256;
234
+ let height = 256;
235
+ const canvas = {
236
+ clientWidth: 256,
237
+ clientHeight: 256,
238
+ style: { width: '256px', height: '256px' },
239
+ get width() {
240
+ return width;
241
+ },
242
+ set width(value: number) {
243
+ width = value;
244
+ canvas.clientWidth = value;
245
+ },
246
+ get height() {
247
+ return height;
248
+ },
249
+ set height(value: number) {
250
+ height = value;
251
+ canvas.clientHeight = value;
252
+ },
253
+ };
254
+ const before = globalThis.devicePixelRatio;
255
+ Object.defineProperty(globalThis, 'devicePixelRatio', { configurable: true, value: 2 });
256
+ try {
257
+ syncCanvasDrawingBuffer(canvas);
258
+ syncCanvasDrawingBuffer(canvas);
259
+ expect(width).toBe(512);
260
+ expect(height).toBe(512);
261
+ } finally {
262
+ Object.defineProperty(globalThis, 'devicePixelRatio', {
263
+ configurable: true,
264
+ value: before,
265
+ });
266
+ }
267
+ });
268
+ });
269
+
270
+ // feat-20260617 M3 / w10: aspect-sync sidecar (createApp path only).
271
+ // syncCameraAspect is the canvas-form per-frame Update system body
272
+ // closure calls with the live canvas width/height. It walks Camera entities via
273
+ // world.get (NOT the query bundle: a bool column read off the bundle returns a
274
+ // raw 0/1 number, so `!== 0` is always true -- the
275
+ // bool-field-compared-with-not-equal-zero-always-true trap; D-5/Finding 2). It
276
+ // writes canvas.width/height into Camera.aspect for perspective + autoAspect=true
277
+ // cameras only.
278
+ describe('syncCameraAspect sidecar (w10 / AC-07)', () => {
279
+ it('canvas resize -> perspective autoAspect=true camera aspect becomes w/h', () => {
280
+ const world = new World();
281
+ const cam = world
282
+ .spawn({ component: Camera, data: perspective({ fov: 1, aspect: 1 }) })
283
+ .unwrap();
284
+
285
+ syncCameraAspect(world, 800, 400);
286
+
287
+ const r = world.get(cam, Camera);
288
+ expect(r.ok).toBe(true);
289
+ if (!r.ok) return;
290
+ expect(r.value.aspect).toBeCloseTo(2, 5);
291
+ });
292
+
293
+ it('autoAspect=false camera is left untouched', () => {
294
+ const world = new World();
295
+ const cam = world
296
+ .spawn({
297
+ component: Camera,
298
+ data: perspective({ fov: 1, aspect: 1.234, autoAspect: false }),
299
+ })
300
+ .unwrap();
301
+
302
+ syncCameraAspect(world, 800, 400);
303
+
304
+ const r = world.get(cam, Camera);
305
+ expect(r.ok).toBe(true);
306
+ if (!r.ok) return;
307
+ expect(r.value.aspect).toBeCloseTo(1.234, 5);
308
+ });
309
+
310
+ it('canvas size 0 is skipped (no NaN / 0 written into aspect)', () => {
311
+ const world = new World();
312
+ const cam = world
313
+ .spawn({ component: Camera, data: perspective({ fov: 1, aspect: 1.5 }) })
314
+ .unwrap();
315
+
316
+ syncCameraAspect(world, 0, 0);
317
+
318
+ const r = world.get(cam, Camera);
319
+ expect(r.ok).toBe(true);
320
+ if (!r.ok) return;
321
+ expect(r.value.aspect).toBeCloseTo(1.5, 5);
322
+ expect(Number.isNaN(r.value.aspect)).toBe(false);
323
+ });
324
+
325
+ it('orthographic camera is left untouched', () => {
326
+ const world = new World();
327
+ const cam = world
328
+ .spawn({
329
+ component: Camera,
330
+ data: orthographic({ left: -1, right: 1, bottom: -1, top: 1 }),
331
+ })
332
+ .unwrap();
333
+ const before = world.get(cam, Camera);
334
+ expect(before.ok).toBe(true);
335
+ if (!before.ok) return;
336
+ const aspectBefore = before.value.aspect;
337
+
338
+ syncCameraAspect(world, 800, 400);
339
+
340
+ const r = world.get(cam, Camera);
341
+ expect(r.ok).toBe(true);
342
+ if (!r.ok) return;
343
+ expect(r.value.aspect).toBeCloseTo(aspectBefore, 5);
344
+ });
345
+ });
346
+ });
@@ -0,0 +1,24 @@
1
+ import { createRenderer, EngineEnvironmentError } from '@forgeax/engine-runtime';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { createApp } from '../create-app';
4
+
5
+ describe('app renderer construction boundary', () => {
6
+ it('keeps construction failures catchable', async () => {
7
+ await expect(createRenderer(null as never)).rejects.toBeDefined();
8
+ });
9
+
10
+ it('returns EngineEnvironmentError through the canvas Result path', async () => {
11
+ const canvas = {
12
+ tagName: 'CANVAS',
13
+ isConnected: true,
14
+ width: 16,
15
+ height: 16,
16
+ getContext: () => null,
17
+ addEventListener: () => undefined,
18
+ removeEventListener: () => undefined,
19
+ } as unknown as HTMLCanvasElement;
20
+ const result = await createApp(canvas, { rhi: undefined });
21
+ expect(result.ok).toBe(false);
22
+ if (!result.ok) expect(result.error).toBeInstanceOf(EngineEnvironmentError);
23
+ });
24
+ });
@@ -0,0 +1,19 @@
1
+ import type { Renderer } from '@forgeax/engine-render';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ describe('app-facing renderer lifecycle', () => {
5
+ it('consumes the three lifecycle Result boundaries without casts', async () => {
6
+ const events: string[] = [];
7
+ const renderer = {
8
+ ready: Promise.resolve({ ok: true as const, value: undefined }),
9
+ draw: () => ({ ok: true as const, value: undefined }),
10
+ dispose: () => events.push('dispose'),
11
+ } as unknown as Renderer;
12
+ const ready = await renderer.ready;
13
+ expect(ready.ok).toBe(true);
14
+ expect(renderer.draw([], { cameraOwner: 0, resourceOwner: 0 }).ok).toBe(true);
15
+ renderer.dispose();
16
+ renderer.dispose();
17
+ expect(events).toEqual(['dispose', 'dispose']);
18
+ });
19
+ });
@@ -0,0 +1,61 @@
1
+ // @perf-budget-skip: intentional documentation subprocess gate.
2
+
3
+ import { execFileSync } from 'node:child_process';
4
+ import { existsSync } from 'node:fs';
5
+ import { resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { describe, expect, it } from 'vitest';
8
+
9
+ const repoRoot = resolve(fileURLToPath(new URL('../../../..', import.meta.url)));
10
+ const docsGate = resolve(repoRoot, 'scripts/forgeax/check-token-first-docs.mjs');
11
+
12
+ function runDocsGate(): { readonly status: number; readonly output: string } {
13
+ try {
14
+ const output = execFileSync(process.execPath, [docsGate], {
15
+ cwd: repoRoot,
16
+ encoding: 'utf8',
17
+ stdio: ['ignore', 'pipe', 'pipe'],
18
+ });
19
+ return { status: 0, output };
20
+ } catch (error: unknown) {
21
+ const result = error as {
22
+ readonly status?: number;
23
+ readonly stdout?: string;
24
+ readonly stderr?: string;
25
+ };
26
+ return {
27
+ status: result.status ?? 1,
28
+ output: `${result.stdout ?? ''}${result.stderr ?? ''}`,
29
+ };
30
+ }
31
+ }
32
+
33
+ describe('M5 active-documentation token-first gate', () => {
34
+ it('keeps the engine-owned active documentation surface present', () => {
35
+ for (const path of [
36
+ 'packages/ecs/README.md',
37
+ 'packages/app/README.md',
38
+ 'packages/audio-webaudio/README.md',
39
+ 'packages/input/README.md',
40
+ 'packages/runtime/README.md',
41
+ 'packages/state/README.md',
42
+ 'skills/forgeax-engine-ecs/SKILL.md',
43
+ 'skills/forgeax-engine-app/SKILL.md',
44
+ 'skills/forgeax-engine-audio/SKILL.md',
45
+ 'skills/forgeax-engine-state/SKILL.md',
46
+ 'apps/hello/sprite-atlas/README.md',
47
+ 'apps/learn-render/1.getting-started/5.transformations/README.md',
48
+ 'apps/learn-render/1.getting-started/7.camera/README.md',
49
+ 'apps/learn-render/2.lighting/3.materials/README.md',
50
+ 'packages/ecs/CHANGELOG.md',
51
+ ]) {
52
+ expect(existsSync(resolve(repoRoot, path)), path).toBe(true);
53
+ }
54
+ });
55
+
56
+ it('accepts only token-first registration and explicit-delta update examples', () => {
57
+ const result = runDocsGate();
58
+ expect(result.status, result.output).toBe(0);
59
+ expect(result.output).toContain('Active-documentation token-first gate passed');
60
+ });
61
+ });