@auraindustry/aurajs 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/README.md +103 -7
  2. package/benchmarks/perf-thresholds.json +54 -0
  3. package/package.json +4 -7
  4. package/src/asset-pack.mjs +5 -1
  5. package/src/authored-project.mjs +1449 -0
  6. package/src/authored-runtime.mjs +2016 -0
  7. package/src/authoring/avatar-animation-graph.mjs +648 -0
  8. package/src/bin-integrity.mjs +272 -0
  9. package/src/build-contract/assets.mjs +130 -0
  10. package/src/build-contract/capabilities.mjs +116 -0
  11. package/src/build-contract/constants.mjs +6 -0
  12. package/src/build-contract/helpers.mjs +44 -0
  13. package/src/build-contract/web-templates.mjs +5993 -0
  14. package/src/build-contract.mjs +27 -2910
  15. package/src/bundler.mjs +188 -55
  16. package/src/cli.mjs +4825 -1512
  17. package/src/commands/project-authoring.mjs +434 -0
  18. package/src/config.mjs +27 -0
  19. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +3309 -0
  20. package/src/conformance/cases/core-runtime-cases.mjs +1431 -0
  21. package/src/conformance/cases/index.mjs +11 -0
  22. package/src/conformance/cases/scene3d-and-media-cases.mjs +2094 -0
  23. package/src/conformance/cases/systems-and-gameplay-cases.mjs +1776 -0
  24. package/src/conformance/shared.mjs +27 -0
  25. package/src/conformance-runner.mjs +25 -13
  26. package/src/conformance.mjs +619 -4020
  27. package/src/cutscene.mjs +362 -5
  28. package/src/dev-cli-action.mjs +249 -0
  29. package/src/dev-cli-inspect.mjs +92 -0
  30. package/src/dev-cli-state.mjs +80 -0
  31. package/src/external-asset-cache.mjs +587 -0
  32. package/src/external-asset-policy.mjs +217 -0
  33. package/src/external-package-surface.mjs +206 -0
  34. package/src/game-action-runtime.mjs +869 -0
  35. package/src/game-state-runtime.mjs +206 -6
  36. package/src/headless-action.mjs +186 -0
  37. package/src/headless-test/runtime-animation.mjs +1173 -0
  38. package/src/headless-test/runtime-coordinator.mjs +1514 -0
  39. package/src/headless-test/runtime-primitives.mjs +320 -0
  40. package/src/headless-test/runtime-world.mjs +2253 -0
  41. package/src/headless-test.mjs +392 -4298
  42. package/src/host-binary.mjs +342 -14
  43. package/src/icon-discovery.mjs +64 -0
  44. package/src/make-catalog.mjs +109 -0
  45. package/src/make.mjs +197 -0
  46. package/src/package-integrity.mjs +586 -0
  47. package/src/perf-benchmark.mjs +353 -0
  48. package/src/postinstall.mjs +5 -5
  49. package/src/prefabs/index.mjs +34 -0
  50. package/src/prefabs/scene-serialization.mjs +184 -0
  51. package/src/project-importer.mjs +620 -0
  52. package/src/project-registry.mjs +24 -0
  53. package/src/publish-command.mjs +195 -0
  54. package/src/publish-env-example.mjs +83 -0
  55. package/src/publish-validation.mjs +708 -0
  56. package/src/retro/assets/compile.mjs +232 -0
  57. package/src/retro/backend-gba/authoring.mjs +1029 -0
  58. package/src/retro/backend-gba/rom.mjs +363 -0
  59. package/src/retro/backend-gbc/rom.mjs +85 -0
  60. package/src/retro/build.mjs +278 -0
  61. package/src/retro/cli/commands.mjs +292 -0
  62. package/src/retro/cli/templates.mjs +84 -0
  63. package/src/retro/diagnostics/catalog.mjs +110 -0
  64. package/src/retro/diagnostics/emit.mjs +72 -0
  65. package/src/retro/emulator/case-overlay.mjs +64 -0
  66. package/src/retro/emulator/discovery.mjs +158 -0
  67. package/src/retro/emulator/macos-case-overlay.swift +220 -0
  68. package/src/retro/emulator/profiles.mjs +146 -0
  69. package/src/retro/emulator/runner.mjs +289 -0
  70. package/src/retro/frontend/load-project.mjs +98 -0
  71. package/src/retro/index.mjs +30 -0
  72. package/src/retro/ir/build-ir.mjs +108 -0
  73. package/src/retro/runtime-gba/contract.mjs +151 -0
  74. package/src/retro/runtime-gbc/contract.mjs +117 -0
  75. package/src/retro/shared/span.mjs +26 -0
  76. package/src/retro/shared/targets.mjs +64 -0
  77. package/src/retro/validator/check-project.mjs +114 -0
  78. package/src/runtime-hotspot-audit.mjs +707 -0
  79. package/src/scaffold/config.mjs +1000 -0
  80. package/src/scaffold/fs.mjs +56 -0
  81. package/src/scaffold/layout.mjs +318 -0
  82. package/src/scaffold/project-docs.mjs +438 -0
  83. package/src/scaffold.mjs +93 -596
  84. package/src/scene-composition/index.mjs +326 -0
  85. package/src/scene-composition/runtime.mjs +751 -0
  86. package/src/self-hosted-assets.mjs +604 -0
  87. package/src/session-client.mjs +750 -0
  88. package/src/session-native-launcher.mjs +74 -0
  89. package/src/session-protocol.mjs +75 -0
  90. package/src/session-runtime.mjs +321 -0
  91. package/src/session-server.mjs +360 -0
  92. package/src/shader-kits/index.mjs +773 -0
  93. package/src/starter-content-registry.mjs +292 -0
  94. package/src/state-artifacts.mjs +662 -24
  95. package/src/state-dev-reload.mjs +99 -2
  96. package/src/terminal-ui.mjs +245 -0
  97. package/src/web-conformance.mjs +219 -0
  98. package/templates/create/2d/config/gameplay/shooter.config.js +26 -0
  99. package/templates/create/2d/content/gameplay/waves.json +26 -0
  100. package/templates/create/2d/content/registries/.gitkeep +1 -0
  101. package/templates/create/2d/docs/design/.gitkeep +1 -0
  102. package/templates/create/2d/docs/design/loop.md +5 -0
  103. package/templates/create/2d/prefabs/enemies.prefab.js +90 -0
  104. package/templates/create/2d/prefabs/enemy-basic.prefab.js +18 -0
  105. package/templates/create/2d/prefabs/player.prefab.js +36 -0
  106. package/templates/create/2d/prefabs/projectiles.prefab.js +35 -0
  107. package/templates/create/2d/scenes/boot.scene.js +12 -0
  108. package/templates/create/2d/scenes/gameplay.scene.js +230 -0
  109. package/templates/create/2d/scenes/menu.scene.js +9 -0
  110. package/templates/create/2d/src/main.js +6 -185
  111. package/templates/create/2d/src/runtime/app.js +49 -0
  112. package/templates/create/2d/src/runtime/capabilities.js +35 -0
  113. package/templates/create/2d/ui/hud.screen.js +40 -0
  114. package/templates/create/2d/ui/pause.screen.js +149 -0
  115. package/templates/create/2d/ui/settings.screen.js +347 -0
  116. package/templates/create/2d/ui/title.screen.js +13 -0
  117. package/templates/create/2d-adventure/aura.config.json +28 -0
  118. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +14 -0
  119. package/templates/create/2d-adventure/content/gameplay/world.js +46 -0
  120. package/templates/create/2d-adventure/content/registries/.gitkeep +1 -0
  121. package/templates/create/2d-adventure/docs/design/loop.md +5 -0
  122. package/templates/create/2d-adventure/prefabs/player.prefab.js +54 -0
  123. package/templates/create/2d-adventure/prefabs/relic.prefab.js +38 -0
  124. package/templates/create/2d-adventure/prefabs/world.prefab.js +125 -0
  125. package/templates/create/2d-adventure/scenes/gameplay.scene.js +256 -0
  126. package/templates/create/2d-adventure/src/runtime/capabilities.js +34 -0
  127. package/templates/create/2d-adventure/ui/hud.screen.js +60 -0
  128. package/templates/create/2d-survivor/config/gameplay/survivor.config.js +33 -0
  129. package/templates/create/2d-survivor/content/gameplay/spawn-zones.json +29 -0
  130. package/templates/create/2d-survivor/content/registries/.gitkeep +1 -0
  131. package/templates/create/2d-survivor/docs/design/.gitkeep +1 -0
  132. package/templates/create/2d-survivor/docs/design/loop.md +5 -0
  133. package/templates/create/2d-survivor/prefabs/enemies.prefab.js +178 -0
  134. package/templates/create/2d-survivor/prefabs/enemy-swarm.prefab.js +18 -0
  135. package/templates/create/2d-survivor/prefabs/player.prefab.js +42 -0
  136. package/templates/create/2d-survivor/prefabs/projectiles.prefab.js +56 -0
  137. package/templates/create/2d-survivor/scenes/boot.scene.js +12 -0
  138. package/templates/create/2d-survivor/scenes/gameplay.scene.js +314 -0
  139. package/templates/create/2d-survivor/scenes/menu.scene.js +9 -0
  140. package/templates/create/2d-survivor/src/main.js +5 -332
  141. package/templates/create/2d-survivor/src/runtime/app.js +49 -0
  142. package/templates/create/2d-survivor/src/runtime/capabilities.js +35 -0
  143. package/templates/create/2d-survivor/ui/hud.screen.js +45 -0
  144. package/templates/create/2d-survivor/ui/title.screen.js +13 -0
  145. package/templates/create/3d/assets/models/starter-avatar.gltf +184 -0
  146. package/templates/create/3d/config/gameplay/.gitkeep +1 -0
  147. package/templates/create/3d/content/gameplay/checkpoints.json +33 -0
  148. package/templates/create/3d/content/gameplay/course.js +40 -0
  149. package/templates/create/3d/content/registries/.gitkeep +1 -0
  150. package/templates/create/3d/docs/design/.gitkeep +1 -0
  151. package/templates/create/3d/docs/design/loop.md +5 -0
  152. package/templates/create/3d/prefabs/checkpoint.prefab.js +15 -0
  153. package/templates/create/3d/prefabs/player.prefab.js +204 -0
  154. package/templates/create/3d/prefabs/world.prefab.js +112 -0
  155. package/templates/create/3d/scenes/boot.scene.js +12 -0
  156. package/templates/create/3d/scenes/checkpoint.scene.js +9 -0
  157. package/templates/create/3d/scenes/gameplay.scene.js +292 -0
  158. package/templates/create/3d/src/main.js +6 -295
  159. package/templates/create/3d/src/runtime/app.js +49 -0
  160. package/templates/create/3d/src/runtime/capabilities.js +53 -0
  161. package/templates/create/3d/src/runtime/materials.js +34 -0
  162. package/templates/create/3d/src/runtime/state.js +39 -0
  163. package/templates/create/3d/ui/hud.screen.js +75 -0
  164. package/templates/create/3d/ui/pause.screen.js +166 -0
  165. package/templates/create/3d/ui/settings.screen.js +387 -0
  166. package/templates/create/3d-adventure/assets/models/starter-avatar.gltf +184 -0
  167. package/templates/create/3d-adventure/aura.config.json +28 -0
  168. package/templates/create/3d-adventure/config/gameplay/adventure.config.js +9 -0
  169. package/templates/create/3d-adventure/content/gameplay/course.js +62 -0
  170. package/templates/create/3d-adventure/content/registries/.gitkeep +1 -0
  171. package/templates/create/3d-adventure/docs/design/loop.md +5 -0
  172. package/templates/create/3d-adventure/prefabs/player.prefab.js +168 -0
  173. package/templates/create/3d-adventure/prefabs/relic.prefab.js +35 -0
  174. package/templates/create/3d-adventure/prefabs/world.prefab.js +119 -0
  175. package/templates/create/3d-adventure/scenes/gameplay.scene.js +358 -0
  176. package/templates/create/3d-adventure/src/runtime/capabilities.js +56 -0
  177. package/templates/create/3d-adventure/src/runtime/materials.js +39 -0
  178. package/templates/create/3d-adventure/src/runtime/state.js +31 -0
  179. package/templates/create/3d-adventure/ui/hud.screen.js +70 -0
  180. package/templates/create/3d-adventure/ui/pause.screen.js +437 -0
  181. package/templates/create/3d-collectathon/assets/models/starter-avatar.gltf +184 -0
  182. package/templates/create/3d-collectathon/config/gameplay/.gitkeep +1 -0
  183. package/templates/create/3d-collectathon/content/gameplay/collectibles.json +26 -0
  184. package/templates/create/3d-collectathon/content/gameplay/course.js +46 -0
  185. package/templates/create/3d-collectathon/content/registries/.gitkeep +1 -0
  186. package/templates/create/3d-collectathon/docs/design/.gitkeep +1 -0
  187. package/templates/create/3d-collectathon/docs/design/loop.md +5 -0
  188. package/templates/create/3d-collectathon/prefabs/collectible.prefab.js +15 -0
  189. package/templates/create/3d-collectathon/prefabs/player.prefab.js +207 -0
  190. package/templates/create/3d-collectathon/prefabs/world.prefab.js +112 -0
  191. package/templates/create/3d-collectathon/scenes/boot.scene.js +12 -0
  192. package/templates/create/3d-collectathon/scenes/checkpoint.scene.js +9 -0
  193. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +200 -0
  194. package/templates/create/3d-collectathon/src/main.js +5 -355
  195. package/templates/create/3d-collectathon/src/runtime/app.js +49 -0
  196. package/templates/create/3d-collectathon/src/runtime/capabilities.js +53 -0
  197. package/templates/create/3d-collectathon/src/runtime/materials.js +34 -0
  198. package/templates/create/3d-collectathon/src/runtime/state.js +27 -0
  199. package/templates/create/3d-collectathon/ui/hud.screen.js +66 -0
  200. package/templates/create/3d-collectathon/ui/pause.screen.js +13 -0
  201. package/templates/create/blank/config/gameplay/.gitkeep +1 -0
  202. package/templates/create/blank/content/gameplay/.gitkeep +1 -0
  203. package/templates/create/blank/content/registries/.gitkeep +1 -0
  204. package/templates/create/blank/docs/design/.gitkeep +1 -0
  205. package/templates/create/blank/docs/design/loop.md +5 -0
  206. package/templates/create/blank/prefabs/.gitkeep +1 -0
  207. package/templates/create/blank/scenes/.gitkeep +1 -0
  208. package/templates/create/blank/src/runtime/.gitkeep +1 -0
  209. package/templates/create/blank/ui/.gitkeep +1 -0
  210. package/templates/create/deckbuilder-2d/assets/audio/.gitkeep +1 -0
  211. package/templates/create/deckbuilder-2d/assets/fonts/.gitkeep +1 -0
  212. package/templates/create/deckbuilder-2d/assets/sprites/.gitkeep +1 -0
  213. package/templates/create/deckbuilder-2d/assets/starter/README.md +11 -0
  214. package/templates/create/deckbuilder-2d/assets/ui/.gitkeep +1 -0
  215. package/templates/create/deckbuilder-2d/aura.config.json +28 -0
  216. package/templates/create/deckbuilder-2d/config/gameplay/deckbuilder.config.js +26 -0
  217. package/templates/create/deckbuilder-2d/content/cards/guard.card.js +19 -0
  218. package/templates/create/deckbuilder-2d/content/cards/spark.card.js +20 -0
  219. package/templates/create/deckbuilder-2d/content/cards/starter.deck.js +69 -0
  220. package/templates/create/deckbuilder-2d/content/cards/strike.card.js +19 -0
  221. package/templates/create/deckbuilder-2d/content/cards/survey.card.js +20 -0
  222. package/templates/create/deckbuilder-2d/content/encounters/training-battle.encounter.js +14 -0
  223. package/templates/create/deckbuilder-2d/content/encounters/training-battle.js +65 -0
  224. package/templates/create/deckbuilder-2d/content/enemies/training-automaton.enemy.js +48 -0
  225. package/templates/create/deckbuilder-2d/content/gameplay/.gitkeep +1 -0
  226. package/templates/create/deckbuilder-2d/content/registries/cards.registry.js +26 -0
  227. package/templates/create/deckbuilder-2d/content/registries/encounters.registry.js +20 -0
  228. package/templates/create/deckbuilder-2d/content/registries/enemies.registry.js +20 -0
  229. package/templates/create/deckbuilder-2d/content/registries/relics.registry.js +20 -0
  230. package/templates/create/deckbuilder-2d/content/relics/ember-charm.relic.js +18 -0
  231. package/templates/create/deckbuilder-2d/docs/design/loop.md +12 -0
  232. package/templates/create/deckbuilder-2d/prefabs/.gitkeep +1 -0
  233. package/templates/create/deckbuilder-2d/scenes/boot.scene.js +84 -0
  234. package/templates/create/deckbuilder-2d/scenes/gameplay.scene.js +641 -0
  235. package/templates/create/deckbuilder-2d/src/components/.gitkeep +1 -0
  236. package/templates/create/deckbuilder-2d/src/main.js +17 -0
  237. package/templates/create/deckbuilder-2d/src/runtime/capabilities.js +22 -0
  238. package/templates/create/deckbuilder-2d/src/shared/.gitkeep +1 -0
  239. package/templates/create/deckbuilder-2d/src/systems/.gitkeep +1 -0
  240. package/templates/create/deckbuilder-2d/tests/smoke/.gitkeep +1 -0
  241. package/templates/create/deckbuilder-2d/ui/hud.screen.js +80 -0
  242. package/templates/create/deckbuilder-2d/ui/pause.screen.js +146 -0
  243. package/templates/create/deckbuilder-2d/ui/settings.screen.js +342 -0
  244. package/templates/create/local-multiplayer/aura.config.json +40 -0
  245. package/templates/create/local-multiplayer/config/gameplay/local-multiplayer.config.js +26 -0
  246. package/templates/create/local-multiplayer/content/gameplay/room-layout.js +13 -0
  247. package/templates/create/local-multiplayer/content/registries/.gitkeep +1 -0
  248. package/templates/create/local-multiplayer/docs/design/loop.md +14 -0
  249. package/templates/create/local-multiplayer/prefabs/player.prefab.js +99 -0
  250. package/templates/create/local-multiplayer/scenes/boot.scene.js +12 -0
  251. package/templates/create/local-multiplayer/scenes/gameplay.scene.js +443 -0
  252. package/templates/create/local-multiplayer/src/main.js +17 -0
  253. package/templates/create/local-multiplayer/src/runtime/capabilities.js +28 -0
  254. package/templates/create/local-multiplayer/ui/hud.screen.js +60 -0
  255. package/templates/create/shared/src/runtime/project-inspector.js +105 -0
  256. package/templates/create/shared/src/runtime/scene-flow.js +290 -0
  257. package/templates/create/shared/src/runtime/screen-shell.js +222 -0
  258. package/templates/create/shared/src/runtime/ui-forms.js +209 -0
  259. package/templates/create/shared/src/runtime/ui-settings.js +237 -0
  260. package/templates/create/shared/src/runtime/ui-theme.js +352 -0
  261. package/templates/create/shared/src/starter-utils/adventure-objectives.js +102 -0
  262. package/templates/create/shared/src/starter-utils/animation-2d.js +337 -0
  263. package/templates/create/shared/src/starter-utils/avatar-3d.js +404 -0
  264. package/templates/create/shared/src/starter-utils/combat-feedback-2d.js +320 -0
  265. package/templates/create/shared/src/starter-utils/core.js +39 -3
  266. package/templates/create/shared/src/starter-utils/index.js +8 -2
  267. package/templates/create/shared/src/starter-utils/platformer-3d.js +34 -3
  268. package/templates/create/shared/src/starter-utils/triggers.js +662 -0
  269. package/templates/create/shared/src/starter-utils/tween-2d.js +615 -0
  270. package/templates/create/video-cutscene/assets/video/.gitkeep +0 -0
  271. package/templates/create/video-cutscene/aura.config.json +28 -0
  272. package/templates/create/video-cutscene/config/gameplay/.gitkeep +0 -0
  273. package/templates/create/video-cutscene/content/gameplay/.gitkeep +0 -0
  274. package/templates/create/video-cutscene/content/registries/.gitkeep +0 -0
  275. package/templates/create/video-cutscene/docs/design/loop.md +22 -0
  276. package/templates/create/video-cutscene/prefabs/.gitkeep +0 -0
  277. package/templates/create/video-cutscene/scenes/boot.scene.js +11 -0
  278. package/templates/create/video-cutscene/scenes/cutscene.scene.js +113 -0
  279. package/templates/create/video-cutscene/scenes/gameplay.scene.js +50 -0
  280. package/templates/create/video-cutscene/src/main.js +17 -0
  281. package/templates/create/video-cutscene/src/runtime/app.js +52 -0
  282. package/templates/create/video-cutscene/src/runtime/capabilities.js +35 -0
  283. package/templates/create/video-cutscene/src/runtime/state.js +13 -0
  284. package/templates/create/video-cutscene/ui/.gitkeep +0 -0
  285. package/templates/create-bin/play.js +1187 -0
  286. package/templates/make/README.md +46 -0
  287. package/templates/make/catalog.json +51 -0
  288. package/templates/make/component/files/{{MAKE_NAME}}.component.js +20 -0
  289. package/templates/make/component/manifest.json +9 -0
  290. package/templates/make/data/files/{{MAKE_NAME}}.json +14 -0
  291. package/templates/make/data/manifest.json +9 -0
  292. package/templates/make/material/files/{{MAKE_NAME}}.material.json +17 -0
  293. package/templates/make/material/manifest.json +9 -0
  294. package/templates/make/prefab/files/{{MAKE_NAME}}.prefab.js +20 -0
  295. package/templates/make/prefab/manifest.json +9 -0
  296. package/templates/make/scene/files/{{MAKE_NAME}}.scene.js +31 -0
  297. package/templates/make/scene/manifest.json +9 -0
  298. package/templates/make/shader/files/{{MAKE_NAME}}.shader.js +23 -0
  299. package/templates/make/shader/manifest.json +9 -0
  300. package/templates/make/system/files/{{MAKE_NAME}}.system.js +15 -0
  301. package/templates/make/system/manifest.json +9 -0
  302. package/templates/make/ui-screen/files/{{MAKE_NAME}}.screen.js +16 -0
  303. package/templates/make/ui-screen/files/{{MAKE_NAME}}.screen.json +23 -0
  304. package/templates/make/ui-screen/manifest.json +10 -0
  305. package/templates/make-starters/deckbuilder-2d/card/files/{{MAKE_NAME}}.card.js +22 -0
  306. package/templates/make-starters/deckbuilder-2d/card/manifest.json +9 -0
  307. package/templates/make-starters/deckbuilder-2d/catalog.json +34 -0
  308. package/templates/make-starters/deckbuilder-2d/encounter/files/{{MAKE_NAME}}.encounter.js +18 -0
  309. package/templates/make-starters/deckbuilder-2d/encounter/manifest.json +9 -0
  310. package/templates/make-starters/deckbuilder-2d/enemy/files/{{MAKE_NAME}}.enemy.js +28 -0
  311. package/templates/make-starters/deckbuilder-2d/enemy/manifest.json +9 -0
  312. package/templates/make-starters/deckbuilder-2d/relic/files/{{MAKE_NAME}}.relic.js +23 -0
  313. package/templates/make-starters/deckbuilder-2d/relic/manifest.json +9 -0
  314. package/templates/retro/platformer/README.md +10 -0
  315. package/templates/retro/platformer/assets/retro/assets.json +91 -0
  316. package/templates/retro/platformer/aura.config.json +7 -0
  317. package/templates/retro/platformer/package.json +5 -0
  318. package/templates/retro/platformer/src/main.js +40 -0
  319. package/templates/retro/puzzle-grid/README.md +10 -0
  320. package/templates/retro/puzzle-grid/assets/retro/assets.json +90 -0
  321. package/templates/retro/puzzle-grid/aura.config.json +7 -0
  322. package/templates/retro/puzzle-grid/package.json +5 -0
  323. package/templates/retro/puzzle-grid/src/main.js +29 -0
  324. package/templates/retro/tactics-grid/README.md +10 -0
  325. package/templates/retro/tactics-grid/assets/retro/assets.json +90 -0
  326. package/templates/retro/tactics-grid/aura.config.json +7 -0
  327. package/templates/retro/tactics-grid/package.json +5 -0
  328. package/templates/retro/tactics-grid/src/main.js +35 -0
  329. package/templates/retro/topdown-adventure/README.md +10 -0
  330. package/templates/retro/topdown-adventure/assets/retro/assets.json +95 -0
  331. package/templates/retro/topdown-adventure/aura.config.json +7 -0
  332. package/templates/retro/topdown-adventure/package.json +5 -0
  333. package/templates/retro/topdown-adventure/src/main.js +29 -0
  334. package/templates/skills/aurajs/SKILL.md +61 -5
@@ -0,0 +1,1431 @@
1
+ export const CORE_RUNTIME_CONFORMANCE_CASES = [
2
+ {
3
+ id: 'lifecycle-window',
4
+ namespaces: ['lifecycle', 'window'],
5
+ functions: [
6
+ 'aura.setup',
7
+ 'aura.update',
8
+ 'aura.draw',
9
+ 'aura.onResize',
10
+ 'aura.onFocus',
11
+ 'aura.onBlur',
12
+ 'aura.window.setTitle',
13
+ 'aura.window.setSize',
14
+ 'aura.window.setFullscreen',
15
+ 'aura.window.setCursorVisible',
16
+ 'aura.window.setCursorLocked',
17
+ 'aura.window.getSize',
18
+ 'aura.window.getPixelRatio',
19
+ 'aura.window.getFPS',
20
+ ],
21
+ nativeChecks: [
22
+ { id: 'window.setTitle', expression: "typeof aura.window?.setTitle === 'function'" },
23
+ { id: 'window.setSize', expression: "typeof aura.window?.setSize === 'function'" },
24
+ { id: 'window.setFullscreen', expression: "typeof aura.window?.setFullscreen === 'function'" },
25
+ { id: 'window.setCursorVisible', expression: "typeof aura.window?.setCursorVisible === 'function'" },
26
+ { id: 'window.setCursorLocked', expression: "typeof aura.window?.setCursorLocked === 'function'" },
27
+ { id: 'window.getSize', expression: "typeof aura.window?.getSize === 'function'" },
28
+ { id: 'window.getPixelRatio', expression: "typeof aura.window?.getPixelRatio === 'function'" },
29
+ { id: 'window.getFPS', expression: "typeof aura.window?.getFPS === 'function'" },
30
+ {
31
+ id: 'lifecycle.onResize.numericArgs',
32
+ expression: "(() => { let calls = 0; let seen = null; aura.onResize = function (w, h) { calls += 1; seen = [typeof w, typeof h, w, h]; }; aura.onResize(1280, 720); return calls === 1 && Array.isArray(seen) && seen[0] === 'number' && seen[1] === 'number' && seen[2] === 1280 && seen[3] === 720; })()",
33
+ },
34
+ {
35
+ id: 'lifecycle.onFocus.callable',
36
+ expression: "(() => { let calls = 0; aura.onFocus = function () { calls += 1; }; aura.onFocus(); return calls === 1; })()",
37
+ },
38
+ {
39
+ id: 'lifecycle.onBlur.callable',
40
+ expression: "(() => { let calls = 0; aura.onBlur = function () { calls += 1; }; aura.onBlur(); return calls === 1; })()",
41
+ },
42
+ ],
43
+ nativePostChecks: [
44
+ { id: 'lifecycle.setup.calledOnce', expression: '__setupCalls === 1' },
45
+ { id: 'lifecycle.update.calledPerFrame', expression: '__updateCalls === 3' },
46
+ { id: 'lifecycle.draw.calledPerFrame', expression: '__drawCalls === 3' },
47
+ {
48
+ id: 'lifecycle.update.dtPositiveNumber',
49
+ expression: "typeof __lastDt === 'number' && Number.isFinite(__lastDt) && __lastDt > 0",
50
+ },
51
+ { id: 'lifecycle.update.afterSetup', expression: '__updateBeforeSetup === false' },
52
+ { id: 'lifecycle.draw.afterSetup', expression: '__drawBeforeSetup === false' },
53
+ ],
54
+ frames: 3,
55
+ nativeFrames: 3,
56
+ source: `
57
+ let setupCount = 0;
58
+ let updateCount = 0;
59
+ let drawCount = 0;
60
+ let resizeCount = 0;
61
+ let focusCount = 0;
62
+ let blurCount = 0;
63
+ let resizeArgs = null;
64
+
65
+ aura.setup = function () {
66
+ setupCount += 1;
67
+ aura.window.setTitle('Conformance');
68
+ aura.window.setSize(640, 360);
69
+ aura.window.setFullscreen(false);
70
+ aura.window.setCursorVisible(true);
71
+ aura.window.setCursorLocked(false);
72
+
73
+ const windowSize = aura.window.getSize();
74
+ aura.test.assert(typeof aura.window.width === 'number', 'window.width should be numeric');
75
+ aura.test.assert(typeof aura.window.height === 'number', 'window.height should be numeric');
76
+ aura.test.equal(windowSize.width, aura.window.width, 'window.getSize width should match snapshot');
77
+ aura.test.equal(windowSize.height, aura.window.height, 'window.getSize height should match snapshot');
78
+ aura.test.assert(typeof aura.window.pixelRatio === 'number', 'window.pixelRatio should be numeric');
79
+ aura.test.assert(typeof aura.window.fps === 'number', 'window.fps should be numeric');
80
+ aura.test.assert(typeof aura.window.close === 'function', 'window.close should exist');
81
+ aura.test.assert(typeof aura.window.setCursorVisible === 'function', 'window.setCursorVisible should exist');
82
+ aura.test.assert(typeof aura.window.setCursorLocked === 'function', 'window.setCursorLocked should exist');
83
+
84
+ aura.onResize = function (width, height) {
85
+ resizeCount += 1;
86
+ resizeArgs = [width, height];
87
+ };
88
+ aura.onFocus = function () {
89
+ focusCount += 1;
90
+ };
91
+ aura.onBlur = function () {
92
+ blurCount += 1;
93
+ };
94
+
95
+ aura.onResize(640, 360);
96
+ aura.onFocus();
97
+ aura.onBlur();
98
+ };
99
+
100
+ aura.update = function (dt) {
101
+ updateCount += 1;
102
+ aura.test.assert(dt > 0, 'dt should be positive');
103
+ };
104
+
105
+ aura.draw = function () {
106
+ drawCount += 1;
107
+ aura.draw2d.clear(10, 20, 30);
108
+
109
+ if (drawCount === 3) {
110
+ aura.test.equal(setupCount, 1, 'setup called once');
111
+ aura.test.equal(updateCount, 3, 'update called once per frame');
112
+ aura.test.equal(resizeCount, 1, 'onResize should be callable');
113
+ aura.test.equal(focusCount, 1, 'onFocus should be callable');
114
+ aura.test.equal(blurCount, 1, 'onBlur should be callable');
115
+ aura.test.equal(resizeArgs[0], 640, 'onResize width arg');
116
+ aura.test.equal(resizeArgs[1], 360, 'onResize height arg');
117
+ }
118
+ };
119
+ `,
120
+ },
121
+ {
122
+ id: 'input-surface',
123
+ namespaces: ['input'],
124
+ functions: [
125
+ 'aura.input.isKeyDown',
126
+ 'aura.input.isKeyPressed',
127
+ 'aura.input.isKeyReleased',
128
+ 'aura.input.getMousePosition',
129
+ 'aura.input.getMouseDelta',
130
+ 'aura.input.isMouseDown',
131
+ 'aura.input.isMousePressed',
132
+ 'aura.input.getMouseWheel',
133
+ 'aura.input.isGamepadConnected',
134
+ 'aura.input.getGamepadAxis',
135
+ 'aura.input.isGamepadButtonDown',
136
+ ],
137
+ nativeChecks: [
138
+ { id: 'input.isKeyDown', expression: "typeof aura.input?.isKeyDown === 'function'" },
139
+ { id: 'input.isKeyPressed', expression: "typeof aura.input?.isKeyPressed === 'function'" },
140
+ { id: 'input.isKeyReleased', expression: "typeof aura.input?.isKeyReleased === 'function'" },
141
+ { id: 'input.getMousePosition', expression: "typeof aura.input?.getMousePosition === 'function'" },
142
+ { id: 'input.getMouseDelta', expression: "typeof aura.input?.getMouseDelta === 'function'" },
143
+ { id: 'input.isMouseDown', expression: "typeof aura.input?.isMouseDown === 'function'" },
144
+ { id: 'input.isMousePressed', expression: "typeof aura.input?.isMousePressed === 'function'" },
145
+ { id: 'input.getMouseWheel', expression: "typeof aura.input?.getMouseWheel === 'function'" },
146
+ { id: 'input.isGamepadConnected', expression: "typeof aura.input?.isGamepadConnected === 'function'" },
147
+ { id: 'input.getGamepadAxis', expression: "typeof aura.input?.getGamepadAxis === 'function'" },
148
+ { id: 'input.isGamepadButtonDown', expression: "typeof aura.input?.isGamepadButtonDown === 'function'" },
149
+ {
150
+ id: 'input.key.invalid-input.safe-false',
151
+ expression: "(() => { const invalid = [42, {}, null, undefined, ['space']]; return invalid.every((key) => aura.input.isKeyDown(key) === false && aura.input.isKeyPressed(key) === false && aura.input.isKeyReleased(key) === false); })()",
152
+ },
153
+ ],
154
+ frames: 1,
155
+ source: `
156
+ aura.setup = function () {
157
+ aura.test.equal(aura.input.isDown('space'), false, 'keyboard isDown default false');
158
+ aura.test.equal(aura.input.isPressed('space'), false, 'keyboard isPressed default false');
159
+ aura.test.equal(aura.input.isReleased('space'), false, 'keyboard isReleased default false');
160
+
161
+ const delta = aura.input.getMouseDelta();
162
+ aura.test.assert(typeof aura.input.mouse.x === 'number', 'mouse.x should be numeric');
163
+ aura.test.assert(typeof aura.input.mouse.y === 'number', 'mouse.y should be numeric');
164
+ aura.test.assert(typeof aura.input.mouse.scroll === 'number', 'mouse.scroll should be numeric');
165
+ aura.test.assert(typeof delta.x === 'number', 'mouse delta x should be numeric');
166
+ aura.test.assert(typeof delta.y === 'number', 'mouse delta y should be numeric');
167
+ aura.test.equal(aura.input.mouse.isDown('left'), false, 'mouse isDown default false');
168
+ aura.test.equal(aura.input.mouse.isPressed('left'), false, 'mouse isPressed default false');
169
+ aura.test.equal(aura.input.mouse.isReleased('left'), false, 'mouse isReleased default false');
170
+
171
+ aura.test.equal(aura.input.gamepad.connected, false, 'gamepad default disconnected');
172
+ aura.test.assert(typeof aura.input.gamepad.axis('leftX') === 'number', 'gamepad axis should return number');
173
+ aura.test.equal(aura.input.gamepad.isDown('a'), false, 'gamepad isDown default false');
174
+ aura.test.equal(aura.input.gamepad.isPressed('a'), false, 'gamepad isPressed default false');
175
+ aura.test.equal(aura.input.gamepad.isReleased('a'), false, 'gamepad isReleased default false');
176
+ aura.input.gamepad.rumble(0.2, 20);
177
+ };
178
+ `,
179
+ },
180
+ {
181
+ id: 'rust-host-native-readiness-smoke',
182
+ modes: ['native'],
183
+ namespaces: ['lifecycle', 'input', 'draw2d', 'assets', 'audio', 'net', 'debug'],
184
+ functions: [
185
+ 'aura.setup',
186
+ 'aura.update',
187
+ 'aura.draw',
188
+ 'aura.onResize',
189
+ 'aura.input.isKeyDown',
190
+ 'aura.input.isKeyPressed',
191
+ 'aura.input.isKeyReleased',
192
+ 'aura.draw2d.clear',
193
+ 'aura.draw2d.rect',
194
+ 'aura.debug.inspectorStats',
195
+ 'aura.assets.load',
196
+ 'aura.assets.getFormatSupport',
197
+ 'aura.audio.setBusVolume',
198
+ 'aura.audio.fadeTrack',
199
+ 'aura.net.connect',
200
+ 'aura.net.websocket',
201
+ 'aura.net.fetch',
202
+ 'aura.net.get',
203
+ 'aura.net.post',
204
+ ],
205
+ nativeChecks: [
206
+ {
207
+ id: 'smoke.lifecycle-input.readiness.deterministic',
208
+ expression: "(() => { const runSample = () => { let resizeSeen = null; aura.onResize = (w, h) => { resizeSeen = [typeof w, typeof h, w, h]; }; aura.onResize(640, 360); const setupOk = typeof aura.setup === 'function'; const updateOk = typeof aura.update === 'function'; const drawOk = typeof aura.draw === 'function'; const hasInputFns = typeof aura.input?.isKeyDown === 'function' && typeof aura.input?.isKeyPressed === 'function' && typeof aura.input?.isKeyReleased === 'function'; const keySnapshot = {}; if (hasInputFns) { for (const key of ['left', 'right', 'up', 'down', 'arrowup', 'arrowdown', 'space']) { keySnapshot[key] = { down: aura.input.isKeyDown(key), pressed: aura.input.isKeyPressed(key), released: aura.input.isKeyReleased(key) }; } } const keyBooleans = Object.values(keySnapshot).every((row) => typeof row.down === 'boolean' && typeof row.pressed === 'boolean' && typeof row.released === 'boolean'); const aliasStable = hasInputFns && aura.input.isKeyDown('up') === aura.input.isKeyDown('arrowup') && aura.input.isKeyPressed('up') === aura.input.isKeyPressed('arrowup') && aura.input.isKeyReleased('up') === aura.input.isKeyReleased('arrowup'); const invalidSafe = hasInputFns && [42, {}, null, undefined, ['space']].every((key) => aura.input.isKeyDown(key) === false && aura.input.isKeyPressed(key) === false && aura.input.isKeyReleased(key) === false); return { setupOk, updateOk, drawOk, resizeSeen, hasInputFns, keyBooleans, aliasStable, invalidSafe, keySnapshot }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.setupOk === true && first.updateOk === true && first.drawOk === true && Array.isArray(first.resizeSeen) && first.resizeSeen[0] === 'number' && first.resizeSeen[1] === 'number' && first.resizeSeen[2] === 640 && first.resizeSeen[3] === 360 && first.hasInputFns === true && first.keyBooleans === true && first.aliasStable === true && first.invalidSafe === true; })()",
209
+ },
210
+ {
211
+ id: 'smoke.render.queue.readiness.deterministic',
212
+ expression: "(() => { const runSample = () => { const inspector = aura.debug?.inspectorStats; if (typeof inspector !== 'function') return null; const before = inspector(); const beforeDraw2d = Number(before?.queues?.draw2dPending); const beforeDraw3d = Number(before?.queues?.draw3dPending); const beforeOverlay = Number(before?.queues?.debugOverlayPending); const beforeNumeric = [beforeDraw2d, beforeDraw3d, beforeOverlay].every((value) => Number.isFinite(value)); let drawCallsAccepted = true; try { const white = aura.Color?.WHITE || aura.colors?.white || (typeof aura.rgba === 'function' ? aura.rgba(1, 1, 1, 1) : null); aura.draw2d.clear(0, 0, 0); aura.draw2d.rect(4, 6, 8, 10, white); aura.draw2d.rect(12, 9, 6, 4, white); } catch (_) { drawCallsAccepted = false; } const after = inspector(); const afterDraw2d = Number(after?.queues?.draw2dPending); const afterDraw3d = Number(after?.queues?.draw3dPending); const afterOverlay = Number(after?.queues?.debugOverlayPending); const afterNumeric = [afterDraw2d, afterDraw3d, afterOverlay].every((value) => Number.isFinite(value)); const draw2dDelta = beforeNumeric && afterNumeric ? (afterDraw2d - beforeDraw2d) : NaN; const draw3dDelta = beforeNumeric && afterNumeric ? (afterDraw3d - beforeDraw3d) : NaN; const overlayDelta = beforeNumeric && afterNumeric ? (afterOverlay - beforeOverlay) : NaN; return { drawCallsAccepted, beforeNumeric, afterNumeric, beforeDraw2d, afterDraw2d, draw2dDelta, draw3dDelta, overlayDelta }; }; const first = runSample(); const second = runSample(); return !!first && !!second && JSON.stringify(first) === JSON.stringify(second) && first.drawCallsAccepted === true && first.beforeNumeric === true && first.afterNumeric === true && Number.isFinite(first.draw2dDelta) && Number.isFinite(first.draw3dDelta) && Number.isFinite(first.overlayDelta) && first.draw2dDelta === 0 && first.draw3dDelta === 0 && first.overlayDelta === 0 && first.afterDraw2d === first.beforeDraw2d; })()",
213
+ },
214
+ {
215
+ id: 'smoke.assets-audio.reason-codes.deterministic',
216
+ expression: "(() => { const runSample = () => { const formatInvalidRaw = (() => { try { return aura.assets.getFormatSupport(42); } catch (_) { return null; } })(); const formatSupportedRaw = (() => { try { return aura.assets.getFormatSupport('native-smoke.png'); } catch (_) { return null; } })(); const loadMissing = (() => { try { const result = aura.assets.load('__missing__/native-smoke-missing.png'); return { threw: false, reasonCode: result?.reasonCode || null, message: null, taggedReason: null }; } catch (err) { const message = String(err); const match = /\\[reason:([^\\]]+)\\]/.exec(message); return { threw: true, reasonCode: null, message, taggedReason: match ? match[1] : null }; } })(); const invalidBusRaw = (() => { try { return aura.audio.setBusVolume('', 0.5); } catch (_) { return null; } })(); const invalidTrackRaw = (() => { try { return aura.audio.fadeTrack('invalid', { to: 0.2, duration: 0.1 }); } catch (_) { return null; } })(); return { formatInvalid: { ok: formatInvalidRaw?.ok === false, status: formatInvalidRaw?.status || null, reasonCode: formatInvalidRaw?.reasonCode || null }, formatSupported: { ok: !!formatSupportedRaw && formatSupportedRaw.ok === true, status: formatSupportedRaw?.status || null, reasonCode: formatSupportedRaw?.reasonCode || null }, loadMissing, invalidBus: { ok: invalidBusRaw?.ok === false, reasonCode: invalidBusRaw?.reasonCode || null }, invalidTrack: { ok: invalidTrackRaw?.ok === false, reasonCode: invalidTrackRaw?.reasonCode || null } }; }; const first = runSample(); const second = runSample(); const loadMissingOk = first.loadMissing?.threw === true && typeof first.loadMissing?.message === 'string' && first.loadMissing.message.includes('Asset not found: __missing__/native-smoke-missing.png') && (first.loadMissing.taggedReason === null || first.loadMissing.taggedReason === 'missing_asset'); return JSON.stringify(first) === JSON.stringify(second) && first.formatInvalid.ok === true && first.formatInvalid.status === 'unsupported' && first.formatInvalid.reasonCode === 'invalid_format_probe' && first.formatSupported.ok === true && first.formatSupported.status === 'supported' && first.formatSupported.reasonCode === 'asset_format_supported' && loadMissingOk && first.invalidBus.ok === true && first.invalidBus.reasonCode === 'invalid_bus' && first.invalidTrack.ok === true && first.invalidTrack.reasonCode === 'invalid_track_handle'; })()",
217
+ },
218
+ {
219
+ id: 'smoke.network.readiness.reason-codes.deterministic',
220
+ expression: "(() => { const parseReason = (msg) => { const match = /\\[reason:([^\\]]+)\\]/.exec(msg); return match ? match[1] : null; }; const classifyInvalidConnect = () => { try { aura.net.connect('tcp', '127.0.0.1', 0); return 'unexpected_no_throw'; } catch (err) { const msg = String(err); if (parseReason(msg) === 'optional_module_net_disabled') return 'optional_module_net_disabled'; if (msg.includes('port must be an integer in range 1..65535')) return 'invalid_connect_port'; return 'unexpected_error'; } }; const classifyInvalidWebSocket = () => { try { aura.net.websocket(42); return 'unexpected_no_throw'; } catch (err) { const msg = String(err); if (parseReason(msg) === 'optional_module_net_disabled') return 'optional_module_net_disabled'; if (msg.includes('url must be a websocket URL string')) return 'invalid_websocket_url'; return 'unexpected_error'; } }; const classifyInvalidFetch = () => { try { aura.net.fetch(42); return 'unexpected_no_throw'; } catch (err) { const msg = String(err); if (parseReason(msg) === 'optional_module_net_disabled') return 'optional_module_net_disabled'; if (msg.includes('url must be a string') || msg.includes('url must be an HTTP URL string')) return 'invalid_fetch_url'; return 'unexpected_error'; } }; const classifyRuntimeMode = () => { try { const handle = aura.net.connect('tcp', '127.0.0.1', 7777); const handleLike = !!handle && typeof handle === 'object' && typeof handle.send === 'function' && typeof handle.close === 'function'; if (handleLike) { try { handle.close(); } catch (_) {} return 'enabled_runtime'; } return 'enabled_nonstandard'; } catch (err) { const msg = String(err); if (parseReason(msg) === 'optional_module_net_disabled') return 'disabled'; return 'enabled_runtime_error'; } }; const runSample = () => { const methodsOk = typeof aura.net?.connect === 'function' && typeof aura.net?.websocket === 'function' && typeof aura.net?.fetch === 'function' && typeof aura.net?.get === 'function' && typeof aura.net?.post === 'function'; const mode = classifyRuntimeMode(); const connectReason = classifyInvalidConnect(); const websocketReason = classifyInvalidWebSocket(); const fetchReason = classifyInvalidFetch(); const consistency = mode === 'disabled' ? (connectReason === 'optional_module_net_disabled' && websocketReason === 'optional_module_net_disabled' && fetchReason === 'optional_module_net_disabled') : (connectReason === 'invalid_connect_port' && websocketReason === 'invalid_websocket_url' && fetchReason === 'invalid_fetch_url'); return { methodsOk, mode, connectReason, websocketReason, fetchReason, consistency }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.methodsOk === true && first.consistency === true && ['disabled', 'enabled_runtime', 'enabled_runtime_error', 'enabled_nonstandard'].includes(first.mode); })()",
221
+ },
222
+ ],
223
+ nativeFrames: 1,
224
+ frames: 1,
225
+ source: `aura.setup = function () {};`,
226
+ },
227
+ {
228
+ id: 'draw-audio-assets-storage',
229
+ namespaces: ['draw2d', 'audio', 'assets', 'storage'],
230
+ functions: [
231
+ 'aura.draw2d.clear',
232
+ 'aura.draw2d.rect',
233
+ 'aura.draw2d.rectFill',
234
+ 'aura.draw2d.circle',
235
+ 'aura.draw2d.circleFill',
236
+ 'aura.draw2d.line',
237
+ 'aura.draw2d.sprite',
238
+ 'aura.draw2d.text',
239
+ 'aura.draw2d.measureText',
240
+ 'aura.draw2d.pushTransform',
241
+ 'aura.draw2d.popTransform',
242
+ 'aura.draw2d.translate',
243
+ 'aura.draw2d.rotate',
244
+ 'aura.draw2d.scale',
245
+ 'aura.audio.play',
246
+ 'aura.audio.pause',
247
+ 'aura.audio.resume',
248
+ 'aura.audio.stop',
249
+ 'aura.audio.setVolume',
250
+ 'aura.audio.setMasterVolume',
251
+ 'aura.audio.stopAll',
252
+ 'aura.audio.setBusVolume',
253
+ 'aura.audio.assignBus',
254
+ 'aura.audio.fadeTrack',
255
+ 'aura.audio.fadeBus',
256
+ 'aura.audio.crossfade',
257
+ 'aura.audio.update',
258
+ 'aura.audio.clearEnvelopes',
259
+ 'aura.audio.getMixerState',
260
+ 'aura.assets.load',
261
+ 'aura.assets.exists',
262
+ 'aura.assets.loadJson',
263
+ 'aura.assets.loadText',
264
+ 'aura.assets.loadFont',
265
+ 'aura.assets.loadBitmapFont',
266
+ 'aura.assets.getFormatSupport',
267
+ 'aura.assets.getState',
268
+ 'aura.assets.getStateHistory',
269
+ 'aura.assets.evict',
270
+ 'aura.assets.getCachePolicy',
271
+ 'aura.assets.setCachePolicy',
272
+ 'aura.assets.preload2d',
273
+ 'aura.assets.preload3d',
274
+ 'aura.storage.save',
275
+ 'aura.storage.load',
276
+ 'aura.storage.delete',
277
+ 'aura.storage.keys',
278
+ ],
279
+ nativeChecks: [
280
+ { id: 'draw2d.clear', expression: "typeof aura.draw2d?.clear === 'function'" },
281
+ { id: 'draw2d.rect', expression: "typeof aura.draw2d?.rect === 'function'" },
282
+ { id: 'draw2d.rectFill', expression: "typeof aura.draw2d?.rectFill === 'function'" },
283
+ { id: 'draw2d.circle', expression: "typeof aura.draw2d?.circle === 'function'" },
284
+ { id: 'draw2d.circleFill', expression: "typeof aura.draw2d?.circleFill === 'function'" },
285
+ { id: 'draw2d.line', expression: "typeof aura.draw2d?.line === 'function'" },
286
+ { id: 'draw2d.sprite', expression: "typeof aura.draw2d?.sprite === 'function'" },
287
+ { id: 'draw2d.text', expression: "typeof aura.draw2d?.text === 'function'" },
288
+ { id: 'draw2d.measureText', expression: "typeof aura.draw2d?.measureText === 'function'" },
289
+ { id: 'draw2d.pushTransform', expression: "typeof aura.draw2d?.pushTransform === 'function'" },
290
+ { id: 'draw2d.popTransform', expression: "typeof aura.draw2d?.popTransform === 'function'" },
291
+ { id: 'draw2d.translate', expression: "typeof aura.draw2d?.translate === 'function'" },
292
+ { id: 'draw2d.rotate', expression: "typeof aura.draw2d?.rotate === 'function'" },
293
+ { id: 'draw2d.scale', expression: "typeof aura.draw2d?.scale === 'function'" },
294
+ { id: 'audio.play', expression: "typeof aura.audio?.play === 'function'" },
295
+ { id: 'audio.pause', expression: "typeof aura.audio?.pause === 'function'" },
296
+ { id: 'audio.resume', expression: "typeof aura.audio?.resume === 'function'" },
297
+ { id: 'audio.stop', expression: "typeof aura.audio?.stop === 'function'" },
298
+ { id: 'audio.setVolume', expression: "typeof aura.audio?.setVolume === 'function'" },
299
+ { id: 'audio.setMasterVolume', expression: "typeof aura.audio?.setMasterVolume === 'function'" },
300
+ { id: 'audio.stopAll', expression: "typeof aura.audio?.stopAll === 'function'" },
301
+ { id: 'audio.setBusVolume', expression: "typeof aura.audio?.setBusVolume === 'function'" },
302
+ { id: 'audio.assignBus', expression: "typeof aura.audio?.assignBus === 'function'" },
303
+ { id: 'audio.fadeTrack', expression: "typeof aura.audio?.fadeTrack === 'function'" },
304
+ { id: 'audio.fadeBus', expression: "typeof aura.audio?.fadeBus === 'function'" },
305
+ { id: 'audio.crossfade', expression: "typeof aura.audio?.crossfade === 'function'" },
306
+ { id: 'audio.update', expression: "typeof aura.audio?.update === 'function'" },
307
+ { id: 'audio.clearEnvelopes', expression: "typeof aura.audio?.clearEnvelopes === 'function'" },
308
+ { id: 'audio.getMixerState', expression: "typeof aura.audio?.getMixerState === 'function'" },
309
+ { id: 'assets.load', expression: "typeof aura.assets?.load === 'function'" },
310
+ { id: 'assets.exists', expression: "typeof aura.assets?.exists === 'function'" },
311
+ { id: 'assets.loadJson', expression: "typeof aura.assets?.loadJson === 'function'" },
312
+ { id: 'assets.loadText', expression: "typeof aura.assets?.loadText === 'function'" },
313
+ { id: 'assets.loadFont', expression: "typeof aura.assets?.loadFont === 'function'" },
314
+ { id: 'assets.loadBitmapFont', expression: "typeof aura.assets?.loadBitmapFont === 'function'" },
315
+ { id: 'assets.getFormatSupport', expression: "typeof aura.assets?.getFormatSupport === 'function'" },
316
+ {
317
+ id: 'assets.streaming.surface.methods',
318
+ expression: "(() => ['load','getFormatSupport','getState','getStateHistory','evict','getCachePolicy','setCachePolicy','preload2d','preload3d'].every((name) => typeof aura.assets?.[name] === 'function'))()",
319
+ },
320
+ {
321
+ id: 'assets.streaming.lifecycle.preload.deterministic',
322
+ expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const normalizePreload = (entry) => ({ ok: !!entry && entry.ok === true, reasonCode: reasonOf(entry), requested: Number(entry?.requested || 0), loaded: Number(entry?.loaded || 0), failed: Number(entry?.failed || 0), failureReasonCodes: Array.isArray(entry?.failures) ? entry.failures.map((row) => reasonOf(row) || '').join('|') : '' }); const normalizeState = (entry) => entry ? { state: entry.state || null, reasonCode: reasonOf(entry) } : null; const normalizeHistory = (rows) => Array.isArray(rows) ? rows.map((row) => `${row.state || ''}:${reasonOf(row) || ''}`).join('|') : ''; const runSample = (tag) => { const missing2d = `__missing__/stream-2d-${tag}.png`; const missing3d = `__missing__/stream-3d-${tag}.glb`; const preload2d = aura.assets.preload2d([missing2d]); const preload3d = aura.assets.preload3d([missing3d]); const state2d = aura.assets.getState(missing2d); const state3d = aura.assets.getState(missing3d); const history2d = aura.assets.getStateHistory(missing2d); const history3d = aura.assets.getStateHistory(missing3d); return { preload2d: normalizePreload(preload2d), preload3d: normalizePreload(preload3d), state2d: normalizeState(state2d), state3d: normalizeState(state3d), history2d: normalizeHistory(history2d), history3d: normalizeHistory(history3d) }; }; const first = runSample('a'); const second = runSample('b'); return JSON.stringify(first) === JSON.stringify(second) && first.preload2d.reasonCode === 'assets_preload_partial_failure' && first.preload2d.requested === 1 && first.preload2d.failed === 1 && first.preload2d.failureReasonCodes === 'missing_asset' && first.preload3d.reasonCode === 'assets_preload_partial_failure' && first.preload3d.requested === 1 && first.preload3d.failed === 1 && first.preload3d.failureReasonCodes === 'missing_asset' && first.state2d?.state === 'failed' && first.state2d?.reasonCode === 'missing_asset' && first.state3d?.state === 'failed' && first.state3d?.reasonCode === 'missing_asset' && first.history2d.startsWith('queued:') && first.history2d.includes('|loading:') && first.history2d.includes('|failed:') && first.history3d.startsWith('queued:') && first.history3d.includes('|loading:') && first.history3d.includes('|failed:missing_asset'); })()",
323
+ },
324
+ {
325
+ id: 'assets.deferred-format.reason-codes.deterministic',
326
+ expression: "(() => { const norm = (entry) => entry ? { ok: entry.ok === true, status: entry.status || null, reasonCode: entry.reasonCode || null, extension: entry.extension || null, kindHint: entry.kindHint || null, supported: entry.supported === true, deferred: entry.deferred === true } : null; const runSample = () => { const supported = norm(aura.assets.getFormatSupport('atlas.png')); const promotedImage = norm(aura.assets.getFormatSupport('future.webp')); const deferredImage = norm(aura.assets.getFormatSupport('future.bmp')); const supportedWoff2 = norm(aura.assets.getFormatSupport('future.woff2')); const deferredFont = norm(aura.assets.getFormatSupport('future.woff')); const promotedModel = norm(aura.assets.getFormatSupport('future.glb')); const nativeVideo = norm(aura.assets.getFormatSupport('future.mp4')); const unsupported = norm(aura.assets.getFormatSupport('blob.xyz')); const invalidRaw = aura.assets.getFormatSupport(42); const invalid = invalidRaw ? { ok: invalidRaw.ok === true, status: invalidRaw.status || null, reasonCode: invalidRaw.reasonCode || null } : null; const loadReason = (() => { try { aura.assets.load('future.bmp'); } catch (err) { return String(err); } return ''; })(); const audioReason = (() => { try { aura.audio.play('future.aac'); } catch (err) { return String(err); } return ''; })(); const fontResult = aura.assets.loadFont('future.woff2'); return { supported, promotedImage, deferredImage, supportedWoff2, deferredFont, promotedModel, nativeVideo, unsupported, invalid, loadReason, audioReason, fontReason: fontResult?.reason || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.supported?.ok === true && first.supported?.status === 'supported' && first.supported?.reasonCode === 'asset_format_supported' && first.supported?.supported === true && first.supported?.deferred === false && first.promotedImage?.ok === true && first.promotedImage?.status === 'supported' && first.promotedImage?.reasonCode === 'asset_format_supported' && first.promotedImage?.deferred === false && first.deferredImage?.ok === true && first.deferredImage?.status === 'unsupported' && first.deferredImage?.reasonCode === 'asset_format_unsupported' && first.deferredImage?.deferred === false && first.supportedWoff2?.status === 'supported' && first.supportedWoff2?.reasonCode === 'asset_format_supported' && first.supportedWoff2?.deferred === false && first.deferredFont?.status === 'unsupported' && first.deferredFont?.reasonCode === 'asset_format_unsupported' && first.deferredFont?.deferred === false && first.promotedModel?.status === 'supported' && first.promotedModel?.reasonCode === 'asset_format_supported' && first.nativeVideo?.status === 'supported' && first.nativeVideo?.reasonCode === 'asset_format_supported' && first.nativeVideo?.extension === 'mp4' && first.unsupported?.status === 'unsupported' && first.unsupported?.reasonCode === 'asset_format_unsupported' && first.invalid?.ok === false && first.invalid?.status === 'unsupported' && first.invalid?.reasonCode === 'invalid_format_probe' && first.loadReason.includes('[reason:asset_format_unsupported]') && first.audioReason.includes('[reason:asset_format_unsupported]') && first.fontReason === 'asset_not_found'; })()",
327
+ },
328
+ {
329
+ id: 'assets.streaming.cache-policy.reason-codes.stable',
330
+ expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const sample = () => { const invalidPaths = aura.assets.preload2d('bad'); const invalidPath = aura.assets.preload3d(['../bad.glb']); const invalidMaxEntries = aura.assets.setCachePolicy({ maxEntries: 0 }); const invalidMaxBytes = aura.assets.setCachePolicy({ maxBytes: 0 }); const invalidMode = aura.assets.setCachePolicy({ evictionMode: 'fifo' }); const updated = aura.assets.setCachePolicy({ maxEntries: 4, maxBytes: null, evictionMode: 'oldest' }); const policy = aura.assets.getCachePolicy(); const missingEvict = aura.assets.evict('__missing__/ghost.png'); const invalidEvict = aura.assets.evict('../ghost.png'); return { invalidPaths: reasonOf(invalidPaths), invalidPath: reasonOf(invalidPath), invalidMaxEntries: reasonOf(invalidMaxEntries), invalidMaxBytes: reasonOf(invalidMaxBytes), invalidMode: reasonOf(invalidMode), updatedOk: !!updated && updated.ok === true, updatedReason: reasonOf(updated), policyMaxEntries: policy?.maxEntries, policyMaxBytes: policy?.maxBytes ?? null, policyEvictionMode: policy?.evictionMode, missingEvict: reasonOf(missingEvict), invalidEvict: reasonOf(invalidEvict) }; }; const first = sample(); const second = sample(); return JSON.stringify(first) === JSON.stringify(second) && first.invalidPaths === 'invalid_preload_paths' && first.invalidPath === 'invalid_preload_path' && first.invalidMaxEntries === 'invalid_max_entries' && first.invalidMaxBytes === 'invalid_max_bytes' && first.invalidMode === 'invalid_eviction_mode' && first.updatedOk === true && first.updatedReason === 'cache_policy_updated' && first.policyMaxEntries === 4 && first.policyMaxBytes === null && first.policyEvictionMode === 'oldest' && first.missingEvict === 'missing_asset' && first.invalidEvict === 'invalid_path'; })()",
331
+ },
332
+ {
333
+ id: 'audio.invalid-input.no-throw.reason-codes',
334
+ expression: "(() => { const runSample = () => { let threw = false; try { aura.audio.stop(-1); aura.audio.pause('x'); aura.audio.resume(99999); aura.audio.setVolume('x', 0.5); } catch (_) { threw = true; } const invalidBus = aura.audio.setBusVolume('', 0.5); const invalidTrackHandle = aura.audio.fadeTrack('oops', { to: 0.2, duration: 0.5 }); const missingBus = aura.audio.fadeBus('missing', { to: 0.2, duration: 0.5 }); const invalidCrossfade = aura.audio.crossfade('oops', 'bad', { duration: 0.5 }); return { threw, invalidBus: invalidBus?.reasonCode || null, invalidTrackHandle: invalidTrackHandle?.reasonCode || null, missingBus: missingBus?.reasonCode || null, invalidCrossfade: invalidCrossfade?.reasonCode || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.threw === false && first.invalidBus === 'invalid_bus' && first.invalidTrackHandle === 'invalid_track_handle' && first.missingBus === 'missing_bus' && typeof first.invalidCrossfade === 'string' && first.invalidCrossfade.length > 0; })()",
335
+ },
336
+ { id: 'storage.save', expression: "typeof aura.storage?.save === 'function'" },
337
+ { id: 'storage.load', expression: "typeof aura.storage?.load === 'function'" },
338
+ { id: 'storage.delete', expression: "typeof aura.storage?.delete === 'function'" },
339
+ { id: 'storage.keys', expression: "typeof aura.storage?.keys === 'function'" },
340
+ ],
341
+ frames: 2,
342
+ source: `
343
+ let img;
344
+ let snd;
345
+
346
+ aura.setup = async function () {
347
+ await aura.assets.load(['player.png', 'coin.wav', 'level.json']);
348
+ img = aura.assets.image('player.png');
349
+ snd = aura.assets.sound('coin.wav');
350
+
351
+ aura.test.equal(aura.assets.mesh('cube.glb').kind, 'mesh');
352
+ aura.test.assert(typeof aura.assets.text('dialogue.txt') === 'string');
353
+ const bytes = aura.assets.bytes('blob.bin');
354
+ aura.test.assert(typeof bytes.byteLength === 'number', 'bytes should expose byteLength');
355
+ aura.test.assert(typeof aura.assets.json('level.json') === 'object');
356
+
357
+ aura.storage.set('highscore', 42);
358
+ aura.test.equal(aura.storage.get('highscore'), 42, 'storage get should return stored value');
359
+ aura.storage.delete('highscore');
360
+ aura.test.equal(aura.storage.get('highscore', 0), 0, 'storage fallback should apply after delete');
361
+
362
+ const handle = aura.audio.play(snd, { loop: true });
363
+ aura.audio.pause(handle);
364
+ aura.audio.resume(handle);
365
+ aura.audio.setVolume(handle, 0.5);
366
+ aura.audio.stop(handle);
367
+ aura.audio.stopAll();
368
+ };
369
+
370
+ aura.draw = function () {
371
+ aura.draw2d.clear(0, 0, 0);
372
+ aura.draw2d.rect(10, 10, 40, 20, aura.colors.red);
373
+ aura.draw2d.rectOutline(10, 40, 40, 20, aura.colors.white, 1);
374
+ aura.draw2d.circle(80, 20, 10, aura.colors.white);
375
+ aura.draw2d.circleOutline(80, 50, 10, aura.colors.white, 1);
376
+ aura.draw2d.line(0, 0, 100, 100, aura.colors.white, 1);
377
+ aura.draw2d.triangle(0, 0, 20, 0, 10, 20, aura.colors.white);
378
+ aura.draw2d.image(img, 30, 30);
379
+ aura.draw2d.sprite(img, 30, 30, { frameX: 0, frameY: 0, frameW: 16, frameH: 16 });
380
+ aura.draw2d.text('ok', 8, 8, { size: 12, color: aura.colors.white });
381
+ aura.draw2d.push();
382
+ aura.draw2d.translate(4, 5);
383
+ aura.draw2d.rotate(0.2);
384
+ aura.draw2d.scale(1.2, 1.2);
385
+ aura.draw2d.pop();
386
+ };
387
+ `,
388
+ },
389
+ {
390
+ id: 'compute-runtime-native',
391
+ modes: ['native'],
392
+ namespaces: ['compute'],
393
+ functions: [
394
+ 'aura.compute.createPipeline',
395
+ 'aura.compute.createBuffer',
396
+ 'aura.compute.writeBuffer',
397
+ 'aura.compute.createBindGroup',
398
+ 'aura.compute.dispatch',
399
+ 'aura.compute.readBuffer',
400
+ 'aura.compute.destroyPipeline',
401
+ 'aura.compute.destroyBuffer',
402
+ 'aura.compute.destroyBindGroup',
403
+ 'aura.compute.getError',
404
+ ],
405
+ nativeChecks: [
406
+ {
407
+ id: 'compute.surface.methods',
408
+ expression: "(() => ['createPipeline','createBuffer','writeBuffer','createBindGroup','dispatch','readBuffer','destroyPipeline','destroyBuffer','destroyBindGroup','getError'].every((name) => typeof aura.compute?.[name] === 'function'))()",
409
+ },
410
+ {
411
+ id: 'compute.dispatch.readback.queued',
412
+ expression: "(() => { const shader = '@group(0) @binding(0) var<storage, read_write> values: array<f32>;\\n@compute @workgroup_size(1)\\nfn main() {\\n values[0] = values[0] + 1.0;\\n}'; const pipeline = aura.compute.createPipeline(shader); const buffer = aura.compute.createBuffer(4, 'storage-read'); aura.compute.writeBuffer(buffer, new Float32Array([2])); const bindGroup = aura.compute.createBindGroup(pipeline, [{ binding: 0, buffer }]); aura.compute.dispatch(pipeline, bindGroup, 1, 1, 1); const queued = aura.compute.readBuffer(buffer, 0, 4); globalThis.__computeNative = { pipeline, buffer, bindGroup, queuedWasNull: queued === null }; return Number.isInteger(pipeline) && pipeline > 0 && Number.isInteger(buffer) && buffer > 0 && Number.isInteger(bindGroup) && bindGroup > 0 && queued === null && aura.compute.getError(pipeline) === null && aura.compute.getError(bindGroup) === null && aura.compute.getError(buffer) === null; })()",
413
+ },
414
+ ],
415
+ nativePostChecks: [
416
+ {
417
+ id: 'compute.dispatch.readback.deterministic',
418
+ expression: "(() => { const state = globalThis.__computeNative || {}; const result = aura.compute.readBuffer(state.buffer); const second = aura.compute.readBuffer(state.buffer); const values = ArrayBuffer.isView(result) ? Array.from(result).map((value) => Number(value.toFixed(6))) : []; const pipelineError = aura.compute.getError(state.pipeline); const bindGroupError = aura.compute.getError(state.bindGroup); const bufferError = aura.compute.getError(state.buffer); aura.compute.destroyBindGroup(state.bindGroup); aura.compute.destroyPipeline(state.pipeline); aura.compute.destroyBuffer(state.buffer); return state.queuedWasNull === true && result instanceof Float32Array && result.length === 1 && values.length === 1 && values[0] === 3 && second === null && pipelineError === null && bindGroupError === null && bufferError === null; })()",
419
+ },
420
+ ],
421
+ nativeFrames: 2,
422
+ frames: 1,
423
+ source: `
424
+ globalThis.__computeNative = {
425
+ pipeline: 0,
426
+ buffer: 0,
427
+ bindGroup: 0,
428
+ queuedWasNull: false,
429
+ };
430
+ `,
431
+ },
432
+ {
433
+ id: 'camera-2d-runtime-surface',
434
+ modes: ['native'],
435
+ namespaces: ['camera', 'draw2d', 'debug'],
436
+ functions: [
437
+ 'aura.camera.x',
438
+ 'aura.camera.y',
439
+ 'aura.camera.zoom',
440
+ 'aura.camera.rotation',
441
+ 'aura.camera.follow',
442
+ 'aura.camera.setDeadzone',
443
+ 'aura.camera.setBounds',
444
+ 'aura.camera.pan',
445
+ 'aura.camera.zoomTo',
446
+ 'aura.camera.shake',
447
+ 'aura.camera.fade',
448
+ 'aura.camera.flash',
449
+ 'aura.camera.onEffectComplete',
450
+ 'aura.camera.update',
451
+ 'aura.camera.getState',
452
+ 'aura.draw2d.rectFill',
453
+ 'aura.draw2d.line',
454
+ 'aura.draw2d.circleFill',
455
+ 'aura.debug.inspectorStats',
456
+ ],
457
+ nativeChecks: [
458
+ {
459
+ id: 'camera.surface.exists',
460
+ expression: "typeof aura.camera === 'object' && ['x', 'y', 'zoom', 'rotation'].every((key) => typeof aura.camera[key] === 'number')",
461
+ },
462
+ {
463
+ id: 'camera.surface.validation',
464
+ expression: "(() => { const original = aura.camera; aura.camera.x = 12.5; aura.camera.y = -3.25; aura.camera.zoom = 2; aura.camera.rotation = 0.5; aura.camera.x = Number.NaN; aura.camera.y = 'oops'; aura.camera.zoom = 0; aura.camera.zoom = -1; aura.camera.rotation = Number.POSITIVE_INFINITY; try { aura.camera = { x: 999 }; } catch (_) {} return aura.camera === original && aura.camera.x === 12.5 && aura.camera.y === -3.25 && aura.camera.zoom === 2 && aura.camera.rotation === 0.5; })()",
465
+ },
466
+ {
467
+ id: 'camera.surface.methods',
468
+ expression: "(() => ['follow','stopFollow','setDeadzone','clearDeadzone','setBounds','clearBounds','pan','panTo','zoomTo','rotateTo','shake','fade','flash','clearEffects','onEffectComplete','offEffectComplete','update','getState'].every((name) => typeof aura.camera?.[name] === 'function'))()",
469
+ },
470
+ ],
471
+ nativePostChecks: [
472
+ {
473
+ id: 'camera.draw2d.pan-zoom-rotation.deterministic',
474
+ expression: "(() => { const sample = () => { const before = aura.debug.inspectorStats(); const start = before && before.queues ? before.queues.draw2dPending : NaN; aura.camera.x = 10; aura.camera.y = 20; aura.camera.zoom = 2; aura.camera.rotation = 0.25; aura.draw2d.rectFill(1, 2, 3, 4, aura.Color.WHITE); aura.camera.zoom = 1; aura.draw2d.line(0, 0, 4, 4, aura.Color.WHITE, 1); aura.camera.x = 0; aura.camera.y = 0; aura.camera.rotation = 0; aura.draw2d.circleFill(8, 8, 2, aura.Color.WHITE); const after = aura.debug.inspectorStats(); const end = after && after.queues ? after.queues.draw2dPending : NaN; return Number.isFinite(start) && Number.isFinite(end) ? (end - start) : NaN; }; const first = sample(); const second = sample(); return first === 12 && second === 12; })()",
475
+ },
476
+ {
477
+ id: 'camera.follow.deadzone.bounds.deterministic',
478
+ expression: "(() => { const target = { x: 0, y: 0 }; const bounded = aura.camera.setBounds({ x: -100, y: -100, width: 200, height: 200 }); const dz = aura.camera.setDeadzone({ x: -5, y: -5, width: 10, height: 10 }); const follow = aura.camera.follow(target, { lerpX: 1, lerpY: 1 }); target.x = 20; target.y = 0; const stepA = aura.camera.update(1 / 60); const stateA = aura.camera.getState(); target.x = 150; target.y = 150; const stepB = aura.camera.update(1 / 60); const stateB = aura.camera.getState(); const stop = aura.camera.stopFollow(); aura.camera.clearDeadzone(); aura.camera.clearBounds(); return bounded && bounded.ok === true && dz && dz.ok === true && follow && follow.ok === true && stepA && stepA.ok === true && stepB && stepB.ok === true && stateA && stateA.x === 15 && stateA.y === 0 && stateB && stateB.x === 100 && stateB.y === 100 && stop && stop.ok === true && stop.stopped === true; })()",
479
+ },
480
+ {
481
+ id: 'camera.effects.callback-ordering.deterministic',
482
+ expression: "(() => { aura.camera.x = 0; aura.camera.y = 0; aura.camera.zoom = 1; aura.camera.rotation = 0; aura.camera.clearEffects(); const calls = []; const late = aura.camera.onEffectComplete((evt) => calls.push(`late:${evt.effectType}:${evt.effectId}`), 10); const early = aura.camera.onEffectComplete((evt) => calls.push(`early:${evt.effectType}:${evt.effectId}`), 1); const pan = aura.camera.pan(20, 10, { duration: 0.5 }); const zoom = aura.camera.zoomTo(2, { duration: 0.5 }); const shake = aura.camera.shake({ duration: 0.5, intensity: 4, frequency: 8 }); const step = aura.camera.update(0.5); const invalidDt = aura.camera.update(0); const state = aura.camera.getState(); const removed = aura.camera.offEffectComplete(late.listenerId) === true && aura.camera.offEffectComplete(early.listenerId) === true; const expected = `early:pan:${pan.effectId}|late:pan:${pan.effectId}|early:zoom:${zoom.effectId}|late:zoom:${zoom.effectId}|early:shake:${shake.effectId}|late:shake:${shake.effectId}`; return pan && pan.ok === true && zoom && zoom.ok === true && shake && shake.ok === true && step && step.ok === true && state && Number(state.x.toFixed(6)) === 20 && Number(state.y.toFixed(6)) === 10 && Number(state.zoom.toFixed(6)) === 2 && state.activeEffects === 0 && invalidDt && invalidDt.ok === false && invalidDt.reasonCode === 'invalid_dt' && calls.join('|') === expected && removed; })()",
483
+ },
484
+ {
485
+ id: 'camera.effects.fade-flash.overlay-deterministic',
486
+ expression: "(() => { aura.camera.clearEffects(); const badFade = aura.camera.fade('bad'); const badDuration = aura.camera.fade({ duration: 0 }); const badColor = aura.camera.fade({ color: { r: 'x', g: 0, b: 0 } }); const badFlash = aura.camera.flash({ alpha: 2 }); const fade = aura.camera.fade({ duration: 0.5, fromAlpha: 0.2, toAlpha: 0.8, color: aura.rgba(0.2, 0.3, 0.4, 1) }); const start = aura.camera.getState(); const midStep = aura.camera.update(0.25); const mid = aura.camera.getState(); const flash = aura.camera.flash({ duration: 0.25, alpha: 0.6, color: aura.rgba(1, 1, 1, 1) }); const flashStart = aura.camera.getState(); const endStep = aura.camera.update(0.25); const end = aura.camera.getState(); aura.camera.clearEffects(); const cleared = aura.camera.getState(); return badFade && badFade.ok === false && badFade.reasonCode === 'invalid_fade_options' && badDuration && badDuration.ok === false && badDuration.reasonCode === 'invalid_fade_duration' && badColor && badColor.ok === false && badColor.reasonCode === 'invalid_fade_color' && badFlash && badFlash.ok === false && badFlash.reasonCode === 'invalid_flash_alpha' && fade && fade.ok === true && start && start.overlay && start.overlay.effectType === 'fade' && Number(start.overlay.alpha.toFixed(6)) === 0.2 && midStep && midStep.ok === true && mid && mid.overlay && mid.overlay.effectType === 'fade' && Number(mid.overlay.alpha.toFixed(6)) === 0.5 && flash && flash.ok === true && flash.reasonCode === 'camera_flash_started' && flashStart && flashStart.overlay && flashStart.overlay.effectType === 'flash' && Number(flashStart.overlay.alpha.toFixed(6)) === 0.6 && endStep && endStep.ok === true && end && end.overlay && end.overlay.effectType === 'fade' && Number(end.overlay.alpha.toFixed(6)) === 0.8 && cleared && cleared.overlay === null; })()",
487
+ },
488
+ ],
489
+ nativeFrames: 2,
490
+ frames: 1,
491
+ source: `
492
+ aura.setup = function () {};
493
+ `,
494
+ },
495
+ {
496
+ id: 'draw2d-product-ui-composition-native',
497
+ modes: ['native'],
498
+ namespaces: ['draw2d', 'camera', 'debug'],
499
+ functions: [
500
+ 'aura.draw2d.createRenderTarget',
501
+ 'aura.draw2d.resizeRenderTarget',
502
+ 'aura.draw2d.destroyRenderTarget',
503
+ 'aura.draw2d.withRenderTarget',
504
+ 'aura.draw2d.pushClipRect',
505
+ 'aura.draw2d.popClipRect',
506
+ 'aura.draw2d.sprite',
507
+ 'aura.draw2d.tileSprite',
508
+ 'aura.draw2d.nineSlice',
509
+ 'aura.draw2d.text',
510
+ 'aura.camera.fade',
511
+ 'aura.camera.flash',
512
+ 'aura.camera.clearEffects',
513
+ 'aura.camera.update',
514
+ 'aura.camera.getState',
515
+ 'aura.debug.inspectorStats',
516
+ ],
517
+ nativeChecks: [
518
+ {
519
+ id: 'draw2d.product-ui.surface.methods',
520
+ expression: "(() => ['createRenderTarget','resizeRenderTarget','destroyRenderTarget','withRenderTarget','pushClipRect','popClipRect','sprite','tileSprite','nineSlice','text'].every((name) => typeof aura.draw2d?.[name] === 'function') && ['fade','flash','clearEffects','update','getState'].every((name) => typeof aura.camera?.[name] === 'function') && typeof aura.debug?.inspectorStats === 'function')()",
521
+ },
522
+ {
523
+ id: 'draw2d.product-ui.runtime.setup',
524
+ expression: `(() => { try { const white = aura.Color?.WHITE || aura.colors?.white || aura.rgba(1, 1, 1, 1); const target = aura.draw2d.createRenderTarget(96, 48); const resize = aura.draw2d.resizeRenderTarget(target, 128, 64); aura.camera.clearEffects(); const fade = aura.camera.fade({ duration: 0.5, fromAlpha: 0.1, toAlpha: 0.7, color: aura.rgba(0.05, 0.08, 0.12, 1) }); globalThis.__draw2dProductUiProbe = { white, target, resize, fadeReason: fade?.reasonCode || null, frames: [], updates: [] }; aura.update = function () { const probe = globalThis.__draw2dProductUiProbe || {}; const updateIndex = Array.isArray(probe.updates) ? probe.updates.length : 0; let flash = null; let before = aura.camera.getState(); if (updateIndex === 1) { flash = aura.camera.flash({ duration: 0.25, alpha: 0.6, color: aura.rgba(1, 1, 1, 1) }); before = aura.camera.getState(); } const step = aura.camera.update(0.25); const after = aura.camera.getState(); probe.updates.push({ updateIndex, flashReason: flash?.reasonCode || null, beforeType: before?.overlay?.effectType || null, beforeAlpha: before?.overlay ? Number(before.overlay.alpha.toFixed(6)) : null, afterType: after?.overlay?.effectType || null, afterAlpha: after?.overlay ? Number(after.overlay.alpha.toFixed(6)) : null, stepReason: step?.reasonCode || null, activeEffects: Number(after?.activeEffects || 0) }); }; aura.draw = function () { const probe = globalThis.__draw2dProductUiProbe || {}; const frameIndex = Array.isArray(probe.frames) ? probe.frames.length : 0; aura.draw2d.clear(4, 6, 10); const invalidClip = aura.draw2d.pushClipRect(0, 0, 0, 10); let offscreenPush = null; let offscreenPop = null; let nestedCapture = null; const capture = aura.draw2d.withRenderTarget(probe.target, () => { aura.draw2d.clear(0, 0, 0, 0); offscreenPush = aura.draw2d.pushClipRect(4, 4, 120, 56); nestedCapture = aura.draw2d.withRenderTarget(probe.target, () => {}); aura.draw2d.rect(6, 6, 116, 52, aura.rgba(0.14, 0.18, 0.25, 0.95)); aura.draw2d.rect(10, 38, 96, 10, aura.rgba(0.4, 0.85, 1, 0.7)); aura.draw2d.text('HP 72 SCORE 1800', 14, 28, { size: 12, color: white }); offscreenPop = aura.draw2d.popClipRect(); }); const bgClip = aura.draw2d.pushClipRect(0, 0, 160, 96); aura.draw2d.tileSprite(probe.target, 0, 0, 160, 96, { tileScaleX: 1.5, tileScaleY: 1.5, tileOffsetX: frameIndex * 6, tileOffsetY: frameIndex * 3, tint: aura.rgba(0.16, 0.2, 0.26, 0.65) }); const bgPop = aura.draw2d.popClipRect(); aura.draw2d.sprite(probe.target, 12, 14, { width: 192, height: 96 }); const hudPush = aura.draw2d.pushClipRect(24, 22, 168, 72); aura.draw2d.nineSlice(probe.target, 24, 22, 168, 72, { slice: 4, tint: aura.rgba(0.18, 0.22, 0.3, 0.92) }); aura.draw2d.text('ENERGY', 36, 40, { size: 12, color: white }); aura.draw2d.tileSprite(probe.target, 36, 52, 120, 10, { tileScaleX: 0.3, tileScaleY: 0.6, tileOffsetX: frameIndex * 2, tileOffsetY: 0, tint: aura.rgba(0.4, 0.85, 1, 0.7) }); const hudPop = aura.draw2d.popClipRect(); const state = aura.camera.getState(); const inspector = aura.debug.inspectorStats(); probe.frames.push({ frameIndex, bgClip: bgClip?.reasonCode || null, bgPop: bgPop?.reasonCode || null, invalidClip: invalidClip?.reasonCode || null, captureReason: capture?.reasonCode || null, captureCommandCount: Number(capture?.commandCount || 0), offscreenPush: offscreenPush?.reasonCode || null, offscreenPop: offscreenPop?.reasonCode || null, nestedCapture: nestedCapture?.reasonCode || null, hudPush: hudPush?.reasonCode || null, hudPop: hudPop?.reasonCode || null, overlayType: state?.overlay?.effectType || null, overlayAlpha: state?.overlay ? Number(state.overlay.alpha.toFixed(6)) : null, draw2dPendingNumeric: Number.isFinite(Number(inspector?.queues?.draw2dPending)), debugOverlayPendingNumeric: Number.isFinite(Number(inspector?.queues?.debugOverlayPending)) }); }; return target?.ok === true && target?.type === 'renderTarget' && target?.__renderTarget === true && Number.isInteger(target?.handle) && target.handle > 0 && resize?.ok === true && resize?.reasonCode === 'draw2d_render_target_resized' && resize?.width === 128 && resize?.height === 64 && fade?.ok === true && fade?.reasonCode === 'camera_fade_started'; } catch (_) { return false; } })()`,
525
+ },
526
+ ],
527
+ nativePostChecks: [
528
+ {
529
+ id: 'draw2d.product-ui.runtime.evidence',
530
+ expression: `(() => { try { const probe = globalThis.__draw2dProductUiProbe || {}; const frames = Array.isArray(probe.frames) ? probe.frames : []; const updates = Array.isArray(probe.updates) ? probe.updates : []; const frame0 = frames[0] || {}; const frame1 = frames[1] || {}; const frame2 = frames[2] || {}; const update0 = updates[0] || {}; const update1 = updates[1] || {}; const update2 = updates[2] || {}; const target = probe.target; const destroyed = aura.draw2d.destroyRenderTarget(target); const missingDestroy = aura.draw2d.destroyRenderTarget(target); aura.camera.clearEffects(); const cleared = aura.camera.getState(); return probe.fadeReason === 'camera_fade_started' && target?.ok === true && Number.isInteger(target?.handle) && target.handle > 0 && probe.resize?.ok === true && probe.resize?.width === 128 && probe.resize?.height === 64 && frames.length === 3 && updates.length === 3 && frame0.bgClip === 'draw2d_clip_pushed' && frame0.bgPop === 'draw2d_clip_popped' && frame0.invalidClip === 'invalid_clip_rect' && frame0.captureReason === 'draw2d_render_target_captured' && frame0.captureCommandCount === 6 && frame0.offscreenPush === 'draw2d_clip_pushed' && frame0.offscreenPop === 'draw2d_clip_popped' && frame0.nestedCapture === 'nested_render_target_capture' && frame0.hudPush === 'draw2d_clip_pushed' && frame0.hudPop === 'draw2d_clip_popped' && frame0.overlayType === 'fade' && frame0.overlayAlpha === 0.4 && frame0.draw2dPendingNumeric === true && frame0.debugOverlayPendingNumeric === true && frame1.captureReason === 'draw2d_render_target_captured' && frame1.captureCommandCount === 6 && frame1.overlayType === 'fade' && frame1.overlayAlpha === 0.7 && frame2.captureReason === 'draw2d_render_target_captured' && frame2.captureCommandCount === 6 && frame2.overlayType === 'fade' && frame2.overlayAlpha === 0.7 && update0.flashReason === null && update0.beforeType === 'fade' && update0.beforeAlpha === 0.1 && update0.afterType === 'fade' && update0.afterAlpha === 0.4 && update0.stepReason === 'camera_updated' && update1.flashReason === 'camera_flash_started' && update1.beforeType === 'flash' && update1.beforeAlpha === 0.6 && update1.afterType === 'fade' && update1.afterAlpha === 0.7 && update1.stepReason === 'camera_updated' && update2.flashReason === null && update2.beforeType === 'fade' && update2.beforeAlpha === 0.7 && update2.afterType === 'fade' && update2.afterAlpha === 0.7 && update2.stepReason === 'camera_updated' && destroyed?.ok === true && destroyed?.reasonCode === 'draw2d_render_target_destroyed' && missingDestroy?.ok === false && missingDestroy?.reasonCode === 'missing_render_target' && cleared?.overlay === null; } catch (_) { return false; } })()`,
531
+ },
532
+ ],
533
+ nativeFrames: 3,
534
+ source: `
535
+ globalThis.__draw2dProductUiProbe = {
536
+ target: null,
537
+ resize: null,
538
+ fadeReason: null,
539
+ frames: [],
540
+ updates: [],
541
+ };
542
+ `,
543
+ nativeSource: `
544
+ const __checks = [];
545
+ let __reported = false;
546
+
547
+ function __toErrorString(value) {
548
+ return String(value && value.message ? value.message : value);
549
+ }
550
+
551
+ function __record(id, pass, detail, trace = null) {
552
+ const row = { id, pass: !!pass, detail: detail || null };
553
+ if (!row.pass && trace !== null && trace !== undefined) {
554
+ row.trace = trace;
555
+ }
556
+ __checks.push(row);
557
+ }
558
+
559
+ function __finish() {
560
+ if (__reported) {
561
+ return;
562
+ }
563
+ const __failedChecks = __checks.filter((check) => !check.pass);
564
+ console.log('__AURA_CONFORMANCE_NATIVE__' + JSON.stringify({
565
+ caseId: 'draw2d-product-ui-composition-native',
566
+ ok: __failedChecks.length === 0,
567
+ checks: __checks,
568
+ failed: __failedChecks.length,
569
+ passed: __checks.length - __failedChecks.length,
570
+ runtimeCacheDiagnostics: null,
571
+ runtimeInspectorSnapshot: null,
572
+ }));
573
+ __reported = true;
574
+ if (aura.window && typeof aura.window.close === 'function') {
575
+ aura.window.close();
576
+ }
577
+ }
578
+
579
+ globalThis.__draw2dProductUiProbe = {
580
+ target: null,
581
+ resize: null,
582
+ fadeReason: null,
583
+ frames: [],
584
+ updates: [],
585
+ };
586
+
587
+ aura.setup = function () {
588
+ try {
589
+ const methodsOk = ['createRenderTarget','resizeRenderTarget','destroyRenderTarget','withRenderTarget','pushClipRect','popClipRect','sprite','tileSprite','nineSlice','text'].every((name) => typeof aura.draw2d?.[name] === 'function')
590
+ && ['fade','flash','clearEffects','update','getState'].every((name) => typeof aura.camera?.[name] === 'function')
591
+ && typeof aura.debug?.inspectorStats === 'function';
592
+ __record('draw2d.product-ui.surface.methods', methodsOk, methodsOk ? null : 'required_methods_missing');
593
+
594
+ const white = aura.Color?.WHITE || aura.colors?.white || aura.rgba(1, 1, 1, 1);
595
+ const target = aura.draw2d.createRenderTarget(96, 48);
596
+ const resize = aura.draw2d.resizeRenderTarget(target, 128, 64);
597
+ aura.camera.clearEffects();
598
+ const fade = aura.camera.fade({
599
+ duration: 0.5,
600
+ fromAlpha: 0.1,
601
+ toAlpha: 0.7,
602
+ color: aura.rgba(0.05, 0.08, 0.12, 1),
603
+ });
604
+ globalThis.__draw2dProductUiProbe = {
605
+ white,
606
+ target,
607
+ resize,
608
+ fadeReason: fade?.reasonCode || null,
609
+ frames: [],
610
+ updates: [],
611
+ };
612
+ const setupOk = target?.ok === true
613
+ && target?.type === 'renderTarget'
614
+ && target?.__renderTarget === true
615
+ && Number.isInteger(target?.handle)
616
+ && target.handle > 0
617
+ && resize?.ok === true
618
+ && resize?.reasonCode === 'draw2d_render_target_resized'
619
+ && resize?.width === 128
620
+ && resize?.height === 64
621
+ && fade?.ok === true
622
+ && fade?.reasonCode === 'camera_fade_started';
623
+ __record('draw2d.product-ui.runtime.setup', setupOk, setupOk ? null : 'draw2d_product_ui_setup_failed');
624
+ } catch (__error) {
625
+ __record('draw2d.product-ui.runtime.setup', false, __toErrorString(__error));
626
+ }
627
+ };
628
+
629
+ aura.update = function () {
630
+ if (__reported) {
631
+ return;
632
+ }
633
+ try {
634
+ const probe = globalThis.__draw2dProductUiProbe || {};
635
+ const updateIndex = Array.isArray(probe.updates) ? probe.updates.length : 0;
636
+ let flash = null;
637
+ let before = aura.camera.getState();
638
+ if (updateIndex === 1) {
639
+ flash = aura.camera.flash({ duration: 0.25, alpha: 0.6, color: aura.rgba(1, 1, 1, 1) });
640
+ before = aura.camera.getState();
641
+ }
642
+ const step = aura.camera.update(0.25);
643
+ const after = aura.camera.getState();
644
+ probe.updates.push({
645
+ updateIndex,
646
+ flashReason: flash?.reasonCode || null,
647
+ beforeType: before?.overlay?.effectType || null,
648
+ beforeAlpha: before?.overlay ? Number(before.overlay.alpha.toFixed(6)) : null,
649
+ afterType: after?.overlay?.effectType || null,
650
+ afterAlpha: after?.overlay ? Number(after.overlay.alpha.toFixed(6)) : null,
651
+ stepReason: step?.reasonCode || null,
652
+ activeEffects: Number(after?.activeEffects || 0),
653
+ });
654
+ } catch (__error) {
655
+ __record('draw2d.product-ui.runtime.evidence', false, __toErrorString(__error));
656
+ __finish();
657
+ }
658
+ };
659
+
660
+ aura.draw = function () {
661
+ if (__reported) {
662
+ return;
663
+ }
664
+ try {
665
+ const probe = globalThis.__draw2dProductUiProbe || {};
666
+ const frameIndex = Array.isArray(probe.frames) ? probe.frames.length : 0;
667
+ aura.draw2d.clear(4, 6, 10);
668
+ const invalidClip = aura.draw2d.pushClipRect(0, 0, 0, 10);
669
+ let offscreenPush = null;
670
+ let offscreenPop = null;
671
+ let nestedCapture = null;
672
+ const capture = aura.draw2d.withRenderTarget(probe.target, () => {
673
+ aura.draw2d.clear(0, 0, 0, 0);
674
+ offscreenPush = aura.draw2d.pushClipRect(4, 4, 120, 56);
675
+ nestedCapture = aura.draw2d.withRenderTarget(probe.target, () => {});
676
+ aura.draw2d.rect(6, 6, 116, 52, aura.rgba(0.14, 0.18, 0.25, 0.95));
677
+ aura.draw2d.rect(10, 38, 96, 10, aura.rgba(0.4, 0.85, 1, 0.7));
678
+ aura.draw2d.text('HP 72 SCORE 1800', 14, 28, { size: 12, color: probe.white });
679
+ offscreenPop = aura.draw2d.popClipRect();
680
+ });
681
+ const bgClip = aura.draw2d.pushClipRect(0, 0, 160, 96);
682
+ aura.draw2d.tileSprite(probe.target, 0, 0, 160, 96, {
683
+ tileScaleX: 1.5,
684
+ tileScaleY: 1.5,
685
+ tileOffsetX: frameIndex * 6,
686
+ tileOffsetY: frameIndex * 3,
687
+ tint: aura.rgba(0.16, 0.2, 0.26, 0.65),
688
+ });
689
+ const bgPop = aura.draw2d.popClipRect();
690
+ aura.draw2d.sprite(probe.target, 12, 14, { width: 192, height: 96 });
691
+ const hudPush = aura.draw2d.pushClipRect(24, 22, 168, 72);
692
+ aura.draw2d.nineSlice(probe.target, 24, 22, 168, 72, {
693
+ slice: 4,
694
+ tint: aura.rgba(0.18, 0.22, 0.3, 0.92),
695
+ });
696
+ aura.draw2d.text('ENERGY', 36, 40, { size: 12, color: probe.white });
697
+ aura.draw2d.tileSprite(probe.target, 36, 52, 120, 10, {
698
+ tileScaleX: 0.3,
699
+ tileScaleY: 0.6,
700
+ tileOffsetX: frameIndex * 2,
701
+ tileOffsetY: 0,
702
+ tint: aura.rgba(0.4, 0.85, 1, 0.7),
703
+ });
704
+ const hudPop = aura.draw2d.popClipRect();
705
+ const state = aura.camera.getState();
706
+ const inspector = aura.debug.inspectorStats();
707
+ probe.frames.push({
708
+ frameIndex,
709
+ bgClip: bgClip?.reasonCode || null,
710
+ bgPop: bgPop?.reasonCode || null,
711
+ invalidClip: invalidClip?.reasonCode || null,
712
+ captureReason: capture?.reasonCode || null,
713
+ captureCommandCount: Number(capture?.commandCount || 0),
714
+ offscreenPush: offscreenPush?.reasonCode || null,
715
+ offscreenPop: offscreenPop?.reasonCode || null,
716
+ nestedCapture: nestedCapture?.reasonCode || null,
717
+ hudPush: hudPush?.reasonCode || null,
718
+ hudPop: hudPop?.reasonCode || null,
719
+ overlayType: state?.overlay?.effectType || null,
720
+ overlayAlpha: state?.overlay ? Number(state.overlay.alpha.toFixed(6)) : null,
721
+ draw2dPendingNumeric: Number.isFinite(Number(inspector?.queues?.draw2dPending)),
722
+ debugOverlayPendingNumeric: Number.isFinite(Number(inspector?.queues?.debugOverlayPending)),
723
+ });
724
+
725
+ if (probe.frames.length < 3) {
726
+ return;
727
+ }
728
+
729
+ const frames = Array.isArray(probe.frames) ? probe.frames : [];
730
+ const updates = Array.isArray(probe.updates) ? probe.updates : [];
731
+ const frame0 = frames[0] || {};
732
+ const frame1 = frames[1] || {};
733
+ const frame2 = frames[2] || {};
734
+ const update0 = updates[0] || {};
735
+ const update1 = updates[1] || {};
736
+ const update2 = updates[2] || {};
737
+ const target = probe.target;
738
+ const destroyed = aura.draw2d.destroyRenderTarget(target);
739
+ const missingDestroy = aura.draw2d.destroyRenderTarget(target);
740
+ aura.camera.clearEffects();
741
+ const cleared = aura.camera.getState();
742
+ const evidenceOk = probe.fadeReason === 'camera_fade_started'
743
+ && target?.ok === true
744
+ && Number.isInteger(target?.handle)
745
+ && target.handle > 0
746
+ && probe.resize?.ok === true
747
+ && probe.resize?.width === 128
748
+ && probe.resize?.height === 64
749
+ && frames.length === 3
750
+ && updates.length === 3
751
+ && frame0.bgClip === 'draw2d_clip_pushed'
752
+ && frame0.bgPop === 'draw2d_clip_popped'
753
+ && frame0.invalidClip === 'invalid_clip_rect'
754
+ && frame0.captureReason === 'draw2d_render_target_captured'
755
+ && frame0.captureCommandCount === 6
756
+ && frame0.offscreenPush === 'draw2d_clip_pushed'
757
+ && frame0.offscreenPop === 'draw2d_clip_popped'
758
+ && frame0.nestedCapture === 'nested_render_target_capture'
759
+ && frame0.hudPush === 'draw2d_clip_pushed'
760
+ && frame0.hudPop === 'draw2d_clip_popped'
761
+ && frame0.overlayType === 'fade'
762
+ && frame0.overlayAlpha === 0.4
763
+ && frame0.draw2dPendingNumeric === true
764
+ && frame0.debugOverlayPendingNumeric === true
765
+ && frame1.captureReason === 'draw2d_render_target_captured'
766
+ && frame1.captureCommandCount === 6
767
+ && frame1.overlayType === 'fade'
768
+ && frame1.overlayAlpha === 0.7
769
+ && frame2.captureReason === 'draw2d_render_target_captured'
770
+ && frame2.captureCommandCount === 6
771
+ && frame2.overlayType === 'fade'
772
+ && frame2.overlayAlpha === 0.7
773
+ && update0.flashReason === null
774
+ && update0.beforeType === 'fade'
775
+ && update0.beforeAlpha === 0.1
776
+ && update0.afterType === 'fade'
777
+ && update0.afterAlpha === 0.4
778
+ && update0.stepReason === 'camera_updated'
779
+ && update1.flashReason === 'camera_flash_started'
780
+ && update1.beforeType === 'flash'
781
+ && update1.beforeAlpha === 0.6
782
+ && update1.afterType === 'fade'
783
+ && update1.afterAlpha === 0.7
784
+ && update1.stepReason === 'camera_updated'
785
+ && update2.flashReason === null
786
+ && update2.beforeType === 'fade'
787
+ && update2.beforeAlpha === 0.7
788
+ && update2.afterType === 'fade'
789
+ && update2.afterAlpha === 0.7
790
+ && update2.stepReason === 'camera_updated'
791
+ && destroyed?.ok === true
792
+ && destroyed?.reasonCode === 'draw2d_render_target_destroyed'
793
+ && missingDestroy?.ok === false
794
+ && missingDestroy?.reasonCode === 'missing_render_target'
795
+ && cleared?.overlay === null;
796
+ __record(
797
+ 'draw2d.product-ui.runtime.evidence',
798
+ evidenceOk,
799
+ evidenceOk ? null : 'draw2d_product_ui_evidence_failed',
800
+ evidenceOk ? null : { frames, updates, destroyed, missingDestroy, cleared },
801
+ );
802
+ } catch (__error) {
803
+ __record('draw2d.product-ui.runtime.evidence', false, __toErrorString(__error));
804
+ }
805
+ __finish();
806
+ };
807
+ `,
808
+ },
809
+ {
810
+ id: 'draw2d-mask-runtime-native',
811
+ modes: ['native'],
812
+ namespaces: ['draw2d', 'debug'],
813
+ functions: [
814
+ 'aura.draw2d.createRenderTarget',
815
+ 'aura.draw2d.destroyRenderTarget',
816
+ 'aura.draw2d.withRenderTarget',
817
+ 'aura.draw2d.withMask',
818
+ 'aura.draw2d.circleFill',
819
+ 'aura.draw2d.rectFill',
820
+ 'aura.draw2d.line',
821
+ 'aura.debug.inspectorStats',
822
+ ],
823
+ nativeChecks: [
824
+ {
825
+ id: 'draw2d.mask-runtime.surface.methods',
826
+ expression: "(() => ['createRenderTarget','destroyRenderTarget','withRenderTarget','withMask','circleFill','rectFill','line'].every((name) => typeof aura.draw2d?.[name] === 'function') && typeof aura.debug?.inspectorStats === 'function')()",
827
+ },
828
+ {
829
+ id: 'draw2d.mask-runtime.setup',
830
+ expression: `(() => { try { const white = aura.Color?.WHITE || aura.colors?.white || aura.rgba(1, 1, 1, 1); const maskTarget = aura.draw2d.createRenderTarget(64, 64); globalThis.__draw2dMaskProbe = { maskTarget, frames: [] }; aura.update = function () {}; aura.draw = function () { const probe = globalThis.__draw2dMaskProbe || {}; const frameIndex = Array.isArray(probe.frames) ? probe.frames.length : 0; aura.draw2d.clear(2, 3, 5); let nestedUnsupported = null; const renderTargetCapture = aura.draw2d.withRenderTarget(maskTarget, () => { aura.draw2d.clear(0, 0, 0, 0); aura.draw2d.circleFill(32, 32, 20, white); nestedUnsupported = aura.draw2d.withMask(maskTarget, 0, 0, 8, 8, () => {}); }); const invalidHandle = aura.draw2d.withMask({ type: 'renderTarget', handle: 9999 }, 0, 0, 8, 8, () => {}); let masked = null; if (frameIndex === 0) { masked = aura.draw2d.withMask(maskTarget, 16, 12, 48, 48, () => { aura.draw2d.rectFill(12, 10, 72, 56, aura.rgba(0.18, 0.32, 0.55, 1)); aura.draw2d.line(12, 10, 84, 66, white, 2); }); } const stats = aura.debug.inspectorStats() || {}; const masking = stats.draw2dRuntime?.masking || {}; const queues = stats.queues || {}; probe.frames.push({ frameIndex, renderTargetCapture: renderTargetCapture?.reasonCode || null, nestedUnsupported: nestedUnsupported?.reasonCode || null, invalidHandle: invalidHandle?.reasonCode || null, maskedReason: masked?.reasonCode || null, maskedCommandCount: Number(masked?.commandCount || 0), pendingPassCount: Number(masking.pendingPassCount || 0), lastSubmittedPassCount: Number(masking.lastSubmittedPassCount || 0), lastExecutedPassCount: Number(masking.lastExecutedPassCount || 0), lastSubmittedCommandCount: Number(masking.lastSubmittedCommandCount || 0), lastReasonCode: masking.lastReasonCode || null, lastOk: masking.lastOk === true, queuePending: Number(queues.draw2dMaskPending || 0) }); }; return maskTarget?.ok === true && maskTarget?.type === 'renderTarget' && maskTarget?.__renderTarget === true && Number.isInteger(maskTarget?.handle) && maskTarget.handle > 0; } catch (_) { return false; } })()`,
831
+ },
832
+ ],
833
+ nativePostChecks: [
834
+ {
835
+ id: 'draw2d.mask-runtime.evidence',
836
+ expression: `(() => { try { const probe = globalThis.__draw2dMaskProbe || {}; const frames = Array.isArray(probe.frames) ? probe.frames : []; const frame0 = frames[0] || {}; const frame1 = frames[1] || {}; const frame2 = frames[2] || {}; const target = probe.maskTarget; const destroyed = aura.draw2d.destroyRenderTarget(target); const missingDestroy = aura.draw2d.destroyRenderTarget(target); return target?.ok === true && Number.isInteger(target?.handle) && target.handle > 0 && frames.length === 3 && frame0.renderTargetCapture === 'draw2d_render_target_captured' && frame0.nestedUnsupported === 'mask_capture_inside_render_target_unsupported' && frame0.invalidHandle === 'invalid_mask_source_handle' && frame0.maskedReason === 'draw2d_mask_captured' && frame0.maskedCommandCount === 2 && frame0.pendingPassCount === 1 && frame0.queuePending === 1 && frame0.lastSubmittedPassCount === 0 && frame0.lastExecutedPassCount === 0 && frame0.lastSubmittedCommandCount === 0 && frame0.lastReasonCode === 'draw2d_mask_idle' && frame0.lastOk === true && frame1.renderTargetCapture === 'draw2d_render_target_captured' && frame1.maskedReason === null && frame1.pendingPassCount === 0 && frame1.queuePending === 0 && frame1.lastSubmittedPassCount === 1 && frame1.lastExecutedPassCount === 1 && frame1.lastSubmittedCommandCount === 2 && frame1.lastReasonCode === 'draw2d_mask_executed' && frame1.lastOk === true && frame2.renderTargetCapture === 'draw2d_render_target_captured' && frame2.maskedReason === null && frame2.pendingPassCount === 0 && frame2.queuePending === 0 && frame2.lastSubmittedPassCount === 0 && frame2.lastExecutedPassCount === 0 && frame2.lastSubmittedCommandCount === 0 && frame2.lastReasonCode === 'draw2d_mask_idle' && frame2.lastOk === true && destroyed?.ok === true && destroyed?.reasonCode === 'draw2d_render_target_destroyed' && missingDestroy?.ok === false && missingDestroy?.reasonCode === 'missing_render_target'; } catch (_) { return false; } })()`,
837
+ debugExpression: "(() => { try { const stats = aura.debug.inspectorStats() || {}; return { probe: globalThis.__draw2dMaskProbe || null, draw2dRuntime: stats.draw2dRuntime || null, queues: stats.queues || null }; } catch (error) { return { debugError: String(error && error.message ? error.message : error) }; } })()",
838
+ },
839
+ ],
840
+ nativeFrames: 3,
841
+ source: `
842
+ globalThis.__draw2dMaskProbe = {
843
+ maskTarget: null,
844
+ frames: [],
845
+ };
846
+ `,
847
+ nativeSource: `
848
+ const __checks = [];
849
+ let __reported = false;
850
+
851
+ function __toErrorString(value) {
852
+ return String(value && value.message ? value.message : value);
853
+ }
854
+
855
+ function __record(id, pass, detail, trace = null) {
856
+ const row = { id, pass: !!pass, detail: detail || null };
857
+ if (!row.pass && trace !== null && trace !== undefined) {
858
+ row.trace = trace;
859
+ }
860
+ __checks.push(row);
861
+ }
862
+
863
+ function __finish() {
864
+ if (__reported) {
865
+ return;
866
+ }
867
+ const __failedChecks = __checks.filter((check) => !check.pass);
868
+ console.log('__AURA_CONFORMANCE_NATIVE__' + JSON.stringify({
869
+ caseId: 'draw2d-mask-runtime-native',
870
+ ok: __failedChecks.length === 0,
871
+ checks: __checks,
872
+ failed: __failedChecks.length,
873
+ passed: __checks.length - __failedChecks.length,
874
+ runtimeCacheDiagnostics: null,
875
+ runtimeInspectorSnapshot: null,
876
+ }));
877
+ __reported = true;
878
+ if (aura.window && typeof aura.window.close === 'function') {
879
+ aura.window.close();
880
+ }
881
+ }
882
+
883
+ globalThis.__draw2dMaskProbe = {
884
+ maskTarget: null,
885
+ frames: [],
886
+ };
887
+
888
+ aura.setup = function () {
889
+ try {
890
+ const methodsOk = ['createRenderTarget','destroyRenderTarget','withRenderTarget','withMask','circleFill','rectFill','line']
891
+ .every((name) => typeof aura.draw2d?.[name] === 'function')
892
+ && typeof aura.debug?.inspectorStats === 'function';
893
+ __record('draw2d.mask-runtime.surface.methods', methodsOk, methodsOk ? null : 'required_methods_missing');
894
+
895
+ const maskTarget = aura.draw2d.createRenderTarget(64, 64);
896
+ globalThis.__draw2dMaskProbe = {
897
+ maskTarget,
898
+ frames: [],
899
+ };
900
+
901
+ const setupOk = maskTarget?.ok === true
902
+ && maskTarget?.type === 'renderTarget'
903
+ && maskTarget?.__renderTarget === true
904
+ && Number.isInteger(maskTarget?.handle)
905
+ && maskTarget.handle > 0;
906
+ __record('draw2d.mask-runtime.setup', setupOk, setupOk ? null : 'draw2d_mask_setup_failed');
907
+ } catch (__error) {
908
+ __record('draw2d.mask-runtime.setup', false, __toErrorString(__error));
909
+ __finish();
910
+ }
911
+ };
912
+
913
+ aura.update = function () {};
914
+
915
+ aura.draw = function () {
916
+ if (__reported) {
917
+ return;
918
+ }
919
+ try {
920
+ const probe = globalThis.__draw2dMaskProbe || {};
921
+ const frameIndex = Array.isArray(probe.frames) ? probe.frames.length : 0;
922
+ const white = aura.Color?.WHITE || aura.colors?.white || aura.rgba(1, 1, 1, 1);
923
+
924
+ aura.draw2d.clear(2, 3, 5);
925
+ let nestedUnsupported = null;
926
+ const renderTargetCapture = aura.draw2d.withRenderTarget(probe.maskTarget, () => {
927
+ aura.draw2d.clear(0, 0, 0, 0);
928
+ aura.draw2d.circleFill(32, 32, 20, white);
929
+ nestedUnsupported = aura.draw2d.withMask(probe.maskTarget, 0, 0, 8, 8, () => {});
930
+ });
931
+ const invalidHandle = aura.draw2d.withMask({ type: 'renderTarget', handle: 9999 }, 0, 0, 8, 8, () => {});
932
+ let masked = null;
933
+ if (frameIndex === 0) {
934
+ masked = aura.draw2d.withMask(probe.maskTarget, 16, 12, 48, 48, () => {
935
+ aura.draw2d.rectFill(12, 10, 72, 56, aura.rgba(0.18, 0.32, 0.55, 1));
936
+ aura.draw2d.line(12, 10, 84, 66, white, 2);
937
+ });
938
+ }
939
+
940
+ const stats = aura.debug.inspectorStats() || {};
941
+ const masking = stats.draw2dRuntime?.masking || {};
942
+ const queues = stats.queues || {};
943
+ probe.frames.push({
944
+ frameIndex,
945
+ renderTargetCapture: renderTargetCapture?.reasonCode || null,
946
+ nestedUnsupported: nestedUnsupported?.reasonCode || null,
947
+ invalidHandle: invalidHandle?.reasonCode || null,
948
+ maskedReason: masked?.reasonCode || null,
949
+ maskedCommandCount: Number(masked?.commandCount || 0),
950
+ pendingPassCount: Number(masking.pendingPassCount || 0),
951
+ lastSubmittedPassCount: Number(masking.lastSubmittedPassCount || 0),
952
+ lastExecutedPassCount: Number(masking.lastExecutedPassCount || 0),
953
+ lastSubmittedCommandCount: Number(masking.lastSubmittedCommandCount || 0),
954
+ lastReasonCode: masking.lastReasonCode || null,
955
+ lastOk: masking.lastOk === true,
956
+ queuePending: Number(queues.draw2dMaskPending || 0),
957
+ });
958
+
959
+ if (probe.frames.length < 3) {
960
+ return;
961
+ }
962
+
963
+ const frames = Array.isArray(probe.frames) ? probe.frames : [];
964
+ const frame0 = frames[0] || {};
965
+ const frame1 = frames[1] || {};
966
+ const frame2 = frames[2] || {};
967
+ const target = probe.maskTarget;
968
+ const destroyed = aura.draw2d.destroyRenderTarget(target);
969
+ const missingDestroy = aura.draw2d.destroyRenderTarget(target);
970
+ const evidenceOk = target?.ok === true
971
+ && Number.isInteger(target?.handle)
972
+ && target.handle > 0
973
+ && frames.length === 3
974
+ && frame0.renderTargetCapture === 'draw2d_render_target_captured'
975
+ && frame0.nestedUnsupported === 'mask_capture_inside_render_target_unsupported'
976
+ && frame0.invalidHandle === 'invalid_mask_source_handle'
977
+ && frame0.maskedReason === 'draw2d_mask_captured'
978
+ && frame0.maskedCommandCount === 2
979
+ && frame0.pendingPassCount === 1
980
+ && frame0.queuePending === 1
981
+ && frame0.lastSubmittedPassCount === 0
982
+ && frame0.lastExecutedPassCount === 0
983
+ && frame0.lastSubmittedCommandCount === 0
984
+ && frame0.lastReasonCode === 'draw2d_mask_idle'
985
+ && frame0.lastOk === true
986
+ && frame1.renderTargetCapture === 'draw2d_render_target_captured'
987
+ && frame1.maskedReason === null
988
+ && frame1.pendingPassCount === 0
989
+ && frame1.queuePending === 0
990
+ && frame1.lastSubmittedPassCount === 1
991
+ && frame1.lastExecutedPassCount === 1
992
+ && frame1.lastSubmittedCommandCount === 2
993
+ && frame1.lastReasonCode === 'draw2d_mask_executed'
994
+ && frame1.lastOk === true
995
+ && frame2.renderTargetCapture === 'draw2d_render_target_captured'
996
+ && frame2.maskedReason === null
997
+ && frame2.pendingPassCount === 0
998
+ && frame2.queuePending === 0
999
+ && frame2.lastSubmittedPassCount === 0
1000
+ && frame2.lastExecutedPassCount === 0
1001
+ && frame2.lastSubmittedCommandCount === 0
1002
+ && frame2.lastReasonCode === 'draw2d_mask_idle'
1003
+ && frame2.lastOk === true
1004
+ && destroyed?.ok === true
1005
+ && destroyed?.reasonCode === 'draw2d_render_target_destroyed'
1006
+ && missingDestroy?.ok === false
1007
+ && missingDestroy?.reasonCode === 'missing_render_target';
1008
+ __record(
1009
+ 'draw2d.mask-runtime.evidence',
1010
+ evidenceOk,
1011
+ evidenceOk ? null : 'draw2d_mask_runtime_evidence_failed',
1012
+ evidenceOk ? null : { frames, destroyed, missingDestroy, draw2dRuntime: stats.draw2dRuntime || null, queues },
1013
+ );
1014
+ } catch (__error) {
1015
+ __record('draw2d.mask-runtime.evidence', false, __toErrorString(__error));
1016
+ }
1017
+ __finish();
1018
+ };
1019
+ `,
1020
+ },
1021
+ {
1022
+ id: 'runtime-behavior-native',
1023
+ modes: ['native'],
1024
+ namespaces: ['draw2d', 'audio', 'assets', 'debug', 'storage'],
1025
+ functions: [
1026
+ 'aura.draw2d.sprite',
1027
+ 'aura.draw2d.text',
1028
+ 'aura.draw2d.measureText',
1029
+ 'aura.audio.play',
1030
+ 'aura.audio.stop',
1031
+ 'aura.audio.pause',
1032
+ 'aura.audio.resume',
1033
+ 'aura.audio.setVolume',
1034
+ 'aura.assets.load',
1035
+ 'aura.assets.exists',
1036
+ 'aura.storage.save',
1037
+ 'aura.storage.load',
1038
+ 'aura.storage.delete',
1039
+ 'aura.debug.log',
1040
+ 'aura.debug.drawRect',
1041
+ 'aura.debug.drawCircle',
1042
+ 'aura.debug.drawText',
1043
+ 'aura.debug.enableInspector',
1044
+ 'aura.debug.inspectorStats',
1045
+ ],
1046
+ nativeChecks: [
1047
+ {
1048
+ id: 'draw2d.measureText.scalesWithSize',
1049
+ expression: "(() => { const small = aura.draw2d.measureText('Hello', { size: 8 }); const large = aura.draw2d.measureText('Hello', { size: 24 }); return Number.isFinite(small.width) && Number.isFinite(large.width) && large.width > small.width && large.height >= small.height; })()",
1050
+ },
1051
+ {
1052
+ id: 'draw2d.measureText.emptyStringZeroWidth',
1053
+ expression: "(() => { const metrics = aura.draw2d.measureText('', { size: 24 }); return metrics.width === 0 && metrics.height === 0; })()",
1054
+ },
1055
+ {
1056
+ id: 'draw2d.spriteAndText.acceptCalls',
1057
+ expression: "(() => { try { aura.draw2d.sprite({ path: 'player.png' }, 10, 20, { width: 16 }); aura.draw2d.text('ok', 5, 5, { size: 12, align: 'center', color: aura.rgba(1, 1, 1, 1) }); return true; } catch (_) { return false; } })()",
1058
+ },
1059
+ {
1060
+ id: 'assets.exists.missingIsFalse',
1061
+ expression: "aura.assets.exists('__missing__/ghost.png') === false",
1062
+ },
1063
+ {
1064
+ id: 'assets.load.missingThrowsDeterministicError',
1065
+ expression: "(() => { try { aura.assets.load('__missing__/ghost.png'); return false; } catch (e) { return String(e).includes('Asset not found: __missing__/ghost.png'); } })()",
1066
+ },
1067
+ {
1068
+ id: 'audio.play.missingAssetThrows',
1069
+ expression: "(() => { try { aura.audio.play('__missing__/ghost.wav'); return false; } catch (e) { return String(e).includes('Asset not found: __missing__/ghost.wav'); } })()",
1070
+ },
1071
+ {
1072
+ id: 'audio.invalidHandle.noThrow',
1073
+ expression: "(() => { try { aura.audio.stop(-1); aura.audio.pause('x'); aura.audio.resume(99999); aura.audio.setVolume('x', 0.5); return true; } catch (_) { return false; } })()",
1074
+ },
1075
+ {
1076
+ id: 'debug.devHelpers.callable',
1077
+ expression: "(() => { try { aura.debug.log('conformance'); aura.debug.drawRect(1, 2, 3, 4); aura.debug.drawCircle(5, 6, 7); aura.debug.drawText('dbg', 8, 9); return true; } catch (_) { return false; } })()",
1078
+ },
1079
+ {
1080
+ id: 'debug.inspector.stats-shape',
1081
+ expression: "(() => { try { const enabled = aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats(); const disabled = aura.debug.enableInspector(false); const phase2 = stats.phase2 || {}; const rows = [phase2.animation, phase2.tilemap, phase2.scene3d, phase2.particles]; const phase2Ok = rows.every((row) => row && Number.isFinite(row.active) && Number.isFinite(row.total) && Number.isFinite(row.callbackQueueDepth) && row.active >= 0 && row.total >= 0 && row.callbackQueueDepth >= 0); const scene3dRuntime = stats.scene3dRuntime || {}; const submission = scene3dRuntime.submission || {}; const camera = scene3dRuntime.camera || {}; const resources = scene3dRuntime.resources || {}; const cameraVectorsOk = Number.isFinite(camera.position?.x) && Number.isFinite(camera.position?.y) && Number.isFinite(camera.position?.z) && Number.isFinite(camera.target?.x) && Number.isFinite(camera.target?.y) && Number.isFinite(camera.target?.z); const billboardOk = Number.isFinite(submission.billboardCommandsPending) && Number.isFinite(submission.billboardUniqueTextureHandles) && Number.isFinite(submission.billboardLastCommandCount) && Number.isFinite(submission.billboardLastRenderedCount) && Number.isFinite(submission.billboardLastBatchCount) && typeof submission.billboardLastReasonCode === 'string'; const morphOk = Number.isFinite(submission.morphCandidateDrawCount) && Number.isFinite(submission.morphCustomPipelineDrawCount) && Number.isFinite(submission.morphMissingBufferDrawCount) && Number.isFinite(submission.morphPreparedBindGroupCount) && Number.isFinite(submission.morphExecutedBindGroupCount) && Number.isFinite(submission.morphExecutedMeshCount) && Number.isFinite(submission.morphExecutedNonZeroWeightDrawCount) && Number.isFinite(submission.morphExecutedNonZeroWeightMeshCount) && typeof submission.morphLastReasonCode === 'string' && Number.isFinite(resources.morphTrackedMeshCount) && Number.isFinite(resources.morphPendingUploadCount); const scene3dRuntimeOk = Number.isFinite(submission.drawMeshPending) && Number.isFinite(submission.uniqueMeshHandles) && Number.isFinite(submission.uniqueMaterialHandles) && Number.isFinite(submission.nonDefaultTransforms) && billboardOk && morphOk && typeof submission.skyboxRequested === 'boolean' && typeof submission.clearRequested === 'boolean' && typeof camera.mode === 'string' && camera.mode.length > 0 && Number.isFinite(camera.aspect) && Number.isFinite(camera.fovDegrees) && Number.isFinite(camera.near) && Number.isFinite(camera.far) && cameraVectorsOk && Number.isFinite(resources.meshHandlesRegistered) && Number.isFinite(resources.meshUploadsPending) && Number.isFinite(resources.materialHandlesKnown) && Number.isFinite(resources.materialCommandsPending) && Number.isFinite(resources.lightPointCount) && typeof resources.lightDirectionalActive === 'boolean'; return enabled === true && disabled === false && Number.isFinite(stats.frameCount) && Number.isFinite(stats.frame.fps) && Number.isFinite(stats.frame.deltaSeconds) && Number.isFinite(stats.frame.elapsedSeconds) && Number.isFinite(stats.window.width) && Number.isFinite(stats.window.height) && Number.isFinite(stats.window.pixelRatio) && Number.isFinite(stats.queues.draw2dPending) && Number.isFinite(stats.queues.draw3dPending) && Number.isFinite(stats.queues.debugOverlayPending) && phase2Ok && scene3dRuntimeOk; } catch (_) { return false; } })()",
1082
+ debugExpression: "(() => { try { const enabled = aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats(); const disabled = aura.debug.enableInspector(false); const phase2 = stats.phase2 || {}; const rows = [phase2.animation, phase2.tilemap, phase2.scene3d, phase2.particles]; const phase2Rows = rows.map((row) => ({ present: !!row, active: row?.active, total: row?.total, callbackQueueDepth: row?.callbackQueueDepth })); const phase2Ok = rows.every((row) => row && Number.isFinite(row.active) && Number.isFinite(row.total) && Number.isFinite(row.callbackQueueDepth) && row.active >= 0 && row.total >= 0 && row.callbackQueueDepth >= 0); const scene3dRuntime = stats.scene3dRuntime || {}; const submission = scene3dRuntime.submission || {}; const camera = scene3dRuntime.camera || {}; const resources = scene3dRuntime.resources || {}; const cameraVectorsOk = Number.isFinite(camera.position?.x) && Number.isFinite(camera.position?.y) && Number.isFinite(camera.position?.z) && Number.isFinite(camera.target?.x) && Number.isFinite(camera.target?.y) && Number.isFinite(camera.target?.z); const billboardOk = Number.isFinite(submission.billboardCommandsPending) && Number.isFinite(submission.billboardUniqueTextureHandles) && Number.isFinite(submission.billboardLastCommandCount) && Number.isFinite(submission.billboardLastRenderedCount) && Number.isFinite(submission.billboardLastBatchCount) && typeof submission.billboardLastReasonCode === 'string'; const morphOk = Number.isFinite(submission.morphCandidateDrawCount) && Number.isFinite(submission.morphCustomPipelineDrawCount) && Number.isFinite(submission.morphMissingBufferDrawCount) && Number.isFinite(submission.morphPreparedBindGroupCount) && Number.isFinite(submission.morphExecutedBindGroupCount) && Number.isFinite(submission.morphExecutedMeshCount) && Number.isFinite(submission.morphExecutedNonZeroWeightDrawCount) && Number.isFinite(submission.morphExecutedNonZeroWeightMeshCount) && typeof submission.morphLastReasonCode === 'string' && Number.isFinite(resources.morphTrackedMeshCount) && Number.isFinite(resources.morphPendingUploadCount); const scene3dRuntimeOk = Number.isFinite(submission.drawMeshPending) && Number.isFinite(submission.uniqueMeshHandles) && Number.isFinite(submission.uniqueMaterialHandles) && Number.isFinite(submission.nonDefaultTransforms) && billboardOk && morphOk && typeof submission.skyboxRequested === 'boolean' && typeof submission.clearRequested === 'boolean' && typeof submission.environmentMapConfigured === 'boolean' && Number.isFinite(submission.pointShadowLightCount) && Number.isFinite(submission.spotShadowLightCount) && Number.isFinite(submission.pointShadowPassCount) && Number.isFinite(submission.spotShadowPassCount) && typeof camera.mode === 'string' && camera.mode.length > 0 && Number.isFinite(camera.aspect) && Number.isFinite(camera.fovDegrees) && Number.isFinite(camera.near) && Number.isFinite(camera.far) && cameraVectorsOk && Number.isFinite(resources.meshHandlesRegistered) && Number.isFinite(resources.meshUploadsPending) && Number.isFinite(resources.materialHandlesKnown) && Number.isFinite(resources.materialCommandsPending) && Number.isFinite(resources.lightPointCount) && Number.isFinite(resources.lightSpotCount) && typeof resources.lightDirectionalActive === 'boolean'; return { enabled, disabled, frameCount: stats?.frameCount, frame: stats?.frame || null, window: stats?.window || null, queues: stats?.queues || null, phase2Ok, phase2Rows, scene3dRuntimeOk, cameraMode: camera?.mode, cameraAspect: camera?.aspect, cameraVectorsOk, submission, resources }; } catch (error) { return { traceError: String(error && error.message ? error.message : error) }; } })()",
1083
+ },
1084
+ {
1085
+ id: 'debug.inspector.scene3d-animation-shape',
1086
+ expression: "(() => { try { aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats(); aura.debug.enableInspector(false); const animation = stats?.scene3dRuntime?.animation || {}; return Number.isFinite(animation.clipCount) && Number.isFinite(animation.playingClipCount) && Number.isFinite(animation.transitionCount) && Number.isFinite(animation.activeWeightSum) && Number.isFinite(animation.loopEventsDispatched) && Number.isFinite(animation.completeEventsDispatched) && Number.isFinite(animation.blendEventsDispatched) && Number.isFinite(animation.skinnedClipCount) && Number.isFinite(animation.totalBoneCount) && Number.isFinite(animation.skinningInfluenceSum) && Number.isFinite(animation.skinningUpdates); } catch (_) { return false; } })()",
1087
+ },
1088
+ {
1089
+ id: 'storage.saveLoadDelete.roundTrip',
1090
+ expression: "(() => { const key = '__conformance_roundtrip__'; aura.storage.save(key, { score: 99 }); const loaded = aura.storage.load(key); const listed = aura.storage.keys(); aura.storage.delete(key); return !!loaded && loaded.score === 99 && Array.isArray(listed) && listed.includes(key) && aura.storage.load(key) === null; })()",
1091
+ },
1092
+ {
1093
+ id: 'optional.modules.steam.not-exposed',
1094
+ expression: "typeof aura.steam === 'undefined' && !Object.prototype.hasOwnProperty.call(aura, 'steam')",
1095
+ },
1096
+ ],
1097
+ nativeFrames: 2,
1098
+ source: `
1099
+ aura.setup = function () {};
1100
+ `,
1101
+ },
1102
+ {
1103
+ id: 'audio-mixer-fade-bus-controls',
1104
+ modes: ['shim', 'native'],
1105
+ namespaces: ['audio', 'assets'],
1106
+ functions: [
1107
+ 'aura.audio.setBusVolume',
1108
+ 'aura.audio.assignBus',
1109
+ 'aura.audio.fadeTrack',
1110
+ 'aura.audio.fadeBus',
1111
+ 'aura.audio.crossfade',
1112
+ 'aura.audio.update',
1113
+ 'aura.audio.clearEnvelopes',
1114
+ 'aura.audio.getMixerState',
1115
+ ],
1116
+ nativeChecks: [
1117
+ {
1118
+ id: 'audio.mixer.surface.methods',
1119
+ expression: "(() => ['setBusVolume','assignBus','fadeTrack','fadeBus','crossfade','update','clearEnvelopes','getMixerState'].every((name) => typeof aura.audio?.[name] === 'function'))()",
1120
+ },
1121
+ {
1122
+ id: 'audio.mixer.reason-codes.deterministic',
1123
+ expression: "(() => { const runSample = () => { const invalidBus = aura.audio.setBusVolume('', 0.5); const invalidTrackHandle = aura.audio.fadeTrack('oops', { to: 0.2, duration: 0.5 }); const missingTrack = aura.audio.fadeTrack(999999, { to: 0.2, duration: 0.5 }); const missingBus = aura.audio.fadeBus('missing', { to: 0.2, duration: 0.5 }); const invalidDt = aura.audio.update(0); return { invalidBus: invalidBus?.reasonCode || null, invalidTrackHandle: invalidTrackHandle?.reasonCode || null, missingTrack: missingTrack?.reasonCode || null, missingBus: missingBus?.reasonCode || null, invalidDt: invalidDt?.reasonCode || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.invalidBus === 'invalid_bus' && first.invalidTrackHandle === 'invalid_track_handle' && first.missingTrack === 'missing_track' && first.missingBus === 'missing_bus' && first.invalidDt === 'invalid_dt'; })()",
1124
+ },
1125
+ ],
1126
+ frames: 1,
1127
+ nativeFrames: 1,
1128
+ source: `
1129
+ aura.setup = async function () {
1130
+ if (aura.assets && typeof aura.assets.load === 'function') {
1131
+ await aura.assets.load(['coin.wav']);
1132
+ }
1133
+
1134
+ const runSample = () => {
1135
+ const h1 = aura.audio.play('coin.wav', { volume: 1, bus: 'music' });
1136
+ const h2 = aura.audio.play('coin.wav', { volume: 1, bus: 'sfx' });
1137
+ const h3 = aura.audio.play('coin.wav', { volume: 0.5, bus: 'music' });
1138
+
1139
+ const invalidBus = aura.audio.setBusVolume('', 0.5);
1140
+ const invalidTrackHandle = aura.audio.fadeTrack('oops', { to: 0.2, duration: 0.5 });
1141
+ const missingTrack = aura.audio.fadeTrack(999999, { to: 0.2, duration: 0.5 });
1142
+ const missingBus = aura.audio.fadeBus('missing', { to: 0.2, duration: 0.5 });
1143
+
1144
+ const setMusic = aura.audio.setBusVolume('music', 0.6);
1145
+ const setSfx = aura.audio.setBusVolume('sfx', 0.8);
1146
+ const moveTrack = aura.audio.assignBus(h2, 'music');
1147
+ const fadeTrack = aura.audio.fadeTrack(h3, { to: 0.1, duration: 0.5 });
1148
+ const fadeBus = aura.audio.fadeBus('music', { to: 0.4, duration: 0.5 });
1149
+ const crossfade = aura.audio.crossfade(h1, h2, { duration: 0.5, toStartVolume: 0, toVolume: 0.9, stopFrom: true });
1150
+
1151
+ const stepA = aura.audio.update(0.25);
1152
+ const stateA = aura.audio.getMixerState();
1153
+ const invalidDt = aura.audio.update(0);
1154
+ const stepB = aura.audio.update(0.25);
1155
+ const stateB = aura.audio.getMixerState();
1156
+ const cleared = aura.audio.clearEnvelopes();
1157
+
1158
+ const musicBus = stateB && Array.isArray(stateB.buses)
1159
+ ? stateB.buses.find((entry) => entry.bus === 'music')
1160
+ : null;
1161
+ const trackTwo = stateB && Array.isArray(stateB.tracks)
1162
+ ? stateB.tracks.find((entry) => entry.handle === h2)
1163
+ : null;
1164
+ const trackThree = stateB && Array.isArray(stateB.tracks)
1165
+ ? stateB.tracks.find((entry) => entry.handle === h3)
1166
+ : null;
1167
+
1168
+ const snapshot = {
1169
+ invalidBus: invalidBus && invalidBus.reasonCode,
1170
+ invalidTrackHandle: invalidTrackHandle && invalidTrackHandle.reasonCode,
1171
+ missingTrack: missingTrack && missingTrack.reasonCode,
1172
+ missingBus: missingBus && missingBus.reasonCode,
1173
+ setMusicOk: !!setMusic && setMusic.ok === true,
1174
+ setSfxOk: !!setSfx && setSfx.ok === true,
1175
+ moveTrackOk: !!moveTrack && moveTrack.ok === true,
1176
+ fadeTrackOk: !!fadeTrack && fadeTrack.ok === true,
1177
+ fadeBusOk: !!fadeBus && fadeBus.ok === true,
1178
+ crossfadeOk: !!crossfade && crossfade.ok === true,
1179
+ stepAOk: !!stepA && stepA.ok === true,
1180
+ stepBOk: !!stepB && stepB.ok === true,
1181
+ invalidDt: invalidDt && invalidDt.reasonCode,
1182
+ stateAEnvelopes: stateA && Array.isArray(stateA.envelopes) ? stateA.envelopes.length : -1,
1183
+ stateBEnvelopes: stateB && Array.isArray(stateB.envelopes) ? stateB.envelopes.length : -1,
1184
+ stateBTracks: stateB && Array.isArray(stateB.tracks) ? stateB.tracks.length : -1,
1185
+ musicBusVolume: musicBus ? Number(musicBus.volume.toFixed(6)) : null,
1186
+ trackTwoVolume: trackTwo ? Number(trackTwo.effectiveVolume.toFixed(6)) : null,
1187
+ trackThreeVolume: trackThree ? Number(trackThree.effectiveVolume.toFixed(6)) : null,
1188
+ cleared: cleared && cleared.cleared,
1189
+ };
1190
+
1191
+ aura.audio.stopAll();
1192
+ return JSON.stringify(snapshot);
1193
+ };
1194
+
1195
+ const first = runSample();
1196
+ const second = runSample();
1197
+ aura.test.equal(first, second, 'audio mixer sample must be deterministic across reruns');
1198
+
1199
+ const parsed = JSON.parse(first);
1200
+ aura.test.equal(parsed.invalidBus, 'invalid_bus', 'invalid bus should be reason-coded');
1201
+ aura.test.equal(parsed.invalidTrackHandle, 'invalid_track_handle', 'invalid track handle should be reason-coded');
1202
+ aura.test.equal(parsed.missingTrack, 'missing_track', 'missing track should be reason-coded');
1203
+ aura.test.equal(parsed.missingBus, 'missing_bus', 'missing bus should be reason-coded');
1204
+ aura.test.equal(parsed.invalidDt, 'invalid_dt', 'invalid dt should be reason-coded');
1205
+ aura.test.assert(parsed.setMusicOk && parsed.setSfxOk && parsed.moveTrackOk, 'bus transitions should succeed');
1206
+ aura.test.assert(parsed.fadeTrackOk && parsed.fadeBusOk && parsed.crossfadeOk, 'fade helpers should succeed');
1207
+ aura.test.assert(parsed.stepAOk && parsed.stepBOk, 'audio update steps should succeed');
1208
+ aura.test.equal(parsed.stateAEnvelopes, 4, 'mid-step should retain all envelopes');
1209
+ aura.test.equal(parsed.stateBEnvelopes, 0, 'final step should complete envelopes');
1210
+ aura.test.equal(parsed.stateBTracks, 2, 'crossfade should stop source track after completion');
1211
+ aura.test.equal(parsed.musicBusVolume, 0.4, 'music bus should settle at deterministic target');
1212
+ aura.test.equal(parsed.trackTwoVolume, 0.36, 'crossfade target track effective volume should be deterministic');
1213
+ aura.test.equal(parsed.trackThreeVolume, 0.04, 'faded track effective volume should be deterministic');
1214
+ aura.test.equal(parsed.cleared, 0, 'clearEnvelopes should report zero after completion');
1215
+ };
1216
+ `,
1217
+ },
1218
+ {
1219
+ id: 'tilemap-query-bridge',
1220
+ namespaces: ['tilemap'],
1221
+ functions: [
1222
+ 'aura.tilemap.queryPoint',
1223
+ 'aura.tilemap.queryAABB',
1224
+ 'aura.tilemap.queryRay',
1225
+ 'aura.tilemap.queryRaycast',
1226
+ ],
1227
+ nativeChecks: [
1228
+ { id: 'tilemap.queryPoint', expression: "typeof aura.tilemap?.queryPoint === 'function'" },
1229
+ { id: 'tilemap.queryAABB', expression: "typeof aura.tilemap?.queryAABB === 'function'" },
1230
+ { id: 'tilemap.queryRay', expression: "typeof aura.tilemap?.queryRay === 'function'" },
1231
+ { id: 'tilemap.queryRaycast', expression: "typeof aura.tilemap?.queryRaycast === 'function'" },
1232
+ { id: 'tilemap.query.invalid-handle.reason-code', expression: "(() => { const result = aura.tilemap.queryPoint(999999, { x: 0, y: 0 }); return result && result.ok === false && result.reasonCode === 'invalid_map_handle'; })()" },
1233
+ {
1234
+ id: 'tilemap.query.reason-codes.expanded',
1235
+ expression: "(() => { const runSample = () => { const fixture = { width: 2, height: 2, tilewidth: 16, tileheight: 16, layers: [ { name: 'ground', type: 'tilelayer', width: 2, height: 2, data: [1, 0, 0, 0], properties: { solid: true } } ], tilesets: [ { firstgid: 1, image: 'tiles.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const invalidPoint = aura.tilemap.queryPoint(mapId, { x: 'bad', y: 0 }); const invalidAabb = aura.tilemap.queryAABB(mapId, { x: 0, y: 0, w: 0, h: 1 }); const invalidRay = aura.tilemap.queryRay(mapId, { x: 0, y: 0, dx: 0, dy: 0, maxDistance: 10 }); const invalidModel = aura.tilemap.queryPoint({ width: 4 }, 1, 1); const invalidHandle = aura.tilemap.queryPoint(999999, { x: 0, y: 0 }); let threw = false; try { aura.tilemap.queryRay(mapId, { x: 0, y: 0, dx: 'bad', dy: 0, maxDistance: 10 }); } catch (_) { threw = true; } aura.tilemap.unload(mapId); return { invalidPoint: invalidPoint?.reasonCode || null, invalidAabb: invalidAabb?.reasonCode || null, invalidRay: invalidRay?.reasonCode || null, invalidModel: invalidModel?.reasonCode || null, invalidHandle: invalidHandle?.reasonCode || null, threw }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.invalidPoint === 'invalid_point_args' && first.invalidAabb === 'invalid_aabb_args' && first.invalidRay === 'invalid_ray_args' && first.invalidModel === 'invalid_model' && first.invalidHandle === 'invalid_map_handle' && first.threw === false; })()",
1236
+ },
1237
+ {
1238
+ id: 'tilemap.query.model-schema.deterministic',
1239
+ expression: "(() => { const runSample = () => { const model = { width: 2, height: 2, tilewidth: 16, tileheight: 16, solidLayerNames: ['solid'], layers: [ { name: 'solid', width: 2, height: 2, data: [1, 0, 0, 0], properties: { solid: true } }, { name: 'dynamic', width: 2, height: 2, data: [0, 1, 0, 0], queryEnabled: true } ], solidCells: [ { x: 1, y: 0, layerIndex: 5, layerName: 'manual' }, { x: 1, y: 0, layerIndex: 5, layerName: 'manual' } ] }; const point = aura.tilemap.queryPoint(model, { x: 16.5, y: 0.5 }); const aabb = aura.tilemap.queryAABB(model, { x: 0, y: 0, w: 32, h: 16 }); const ray = aura.tilemap.queryRay(model, { x: 0.5, y: 0.5, dx: 1, dy: 0, maxDistance: 64 }); return { pointLayers: Array.isArray(point?.hits) ? point.hits.map((hit) => hit.layerIndex).join(',') : '', aabbLayers: Array.isArray(aabb?.hits) ? aabb.hits.map((hit) => hit.layerIndex).join(',') : '', rayLayer: Number(ray?.hitCell?.layerIndex ?? -1), pointOk: point?.ok === true, aabbOk: aabb?.ok === true, rayOk: ray?.ok === true, pointHit: point?.hit === true, aabbHit: aabb?.hit === true, rayHit: ray?.hit === true, invalidModelReason: aura.tilemap.queryPoint({ width: 2 }, { x: 0, y: 0 })?.reasonCode, invalidAabbReason: aura.tilemap.queryAABB(model, { x: 0, y: 0, w: 0, h: 1 })?.reasonCode }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.pointOk === true && first.aabbOk === true && first.rayOk === true && first.pointHit === true && first.aabbHit === true && first.rayHit === true && first.pointLayers === '1,5' && first.aabbLayers === '0,1,5' && first.rayLayer === 0 && first.invalidModelReason === 'invalid_model' && first.invalidAabbReason === 'invalid_aabb_args'; })()",
1240
+ },
1241
+ ],
1242
+ frames: 1,
1243
+ source: `
1244
+ aura.setup = function () {
1245
+ const mkLayer = (name, hits) => {
1246
+ const data = new Array(16).fill(0);
1247
+ for (const [x, y] of hits) data[(y * 4) + x] = 1;
1248
+ return {
1249
+ name,
1250
+ type: 'tilelayer',
1251
+ width: 4,
1252
+ height: 4,
1253
+ data,
1254
+ properties: { solid: name.startsWith('walls-') },
1255
+ };
1256
+ };
1257
+ const fixture = {
1258
+ width: 4,
1259
+ height: 4,
1260
+ tilewidth: 16,
1261
+ tileheight: 16,
1262
+ solidLayerNames: ['walls-a', 'walls-b'],
1263
+ layers: [
1264
+ mkLayer('ground', [[0, 0], [3, 3]]),
1265
+ mkLayer('walls-a', [[1, 1], [2, 1]]),
1266
+ mkLayer('walls-b', [[1, 1]]),
1267
+ ],
1268
+ tilesets: [
1269
+ { firstgid: 1, image: 'tiles.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 },
1270
+ ],
1271
+ };
1272
+ const mapHandle = aura.tilemap.import(fixture);
1273
+
1274
+ const point = aura.tilemap.queryPoint(mapHandle, { x: 16.5, y: 16.5 });
1275
+ const pointRepeat = aura.tilemap.queryPoint(mapHandle, { x: 16.5, y: 16.5 });
1276
+ const aabb = aura.tilemap.queryAABB(mapHandle, { x: 0, y: 0, w: 48, h: 32 });
1277
+ const ray = aura.tilemap.queryRay(mapHandle, { x: 0.5, y: 16.5, dx: 1, dy: 0, maxDistance: 96 });
1278
+ const rayAlias = aura.tilemap.queryRaycast(mapHandle, { x: 0.5, y: 16.5, dx: 1, dy: 0, maxDistance: 96 });
1279
+
1280
+ aura.test.assert(point && point.ok === true && point.hit === true, 'tilemap point query should hit');
1281
+ aura.test.assert(Array.isArray(point.hits) && point.hits.length === 2, 'tilemap point should return deterministic layered hits');
1282
+ aura.test.assert(point.hits[0].layerIndex === 1 && point.hits[1].layerIndex === 2, 'tilemap point ordering should be deterministic');
1283
+ aura.test.assert(JSON.stringify(point.hits) === JSON.stringify(pointRepeat.hits), 'tilemap point ordering should be stable across reruns');
1284
+
1285
+ aura.test.assert(aabb && aabb.ok === true && aabb.hit === true, 'tilemap aabb query should hit');
1286
+ aura.test.assert(Array.isArray(aabb.hits) && aabb.hits.length === 3, 'tilemap aabb should include deterministic hit set');
1287
+ aura.test.assert(aabb.hits[0].layerIndex === 1 && aabb.hits[0].x === 1 && aabb.hits[0].y === 1, 'tilemap aabb hit ordering start');
1288
+ aura.test.assert(aabb.hits[1].layerIndex === 1 && aabb.hits[1].x === 2 && aabb.hits[1].y === 1, 'tilemap aabb hit ordering middle');
1289
+ aura.test.assert(aabb.hits[2].layerIndex === 2 && aabb.hits[2].x === 1 && aabb.hits[2].y === 1, 'tilemap aabb hit ordering end');
1290
+
1291
+ aura.test.assert(ray && ray.ok === true && ray.hit === true && ray.hitCell && ray.hitCell.x === 1 && ray.hitCell.y === 1, 'tilemap ray query should return first deterministic hit');
1292
+ aura.test.assert(rayAlias && rayAlias.ok === true && rayAlias.hit === true && rayAlias.hitCell && rayAlias.hitCell.x === ray.hitCell.x && rayAlias.hitCell.y === ray.hitCell.y, 'tilemap ray alias should match queryRay');
1293
+
1294
+ const invalidPoint = aura.tilemap.queryPoint(mapHandle, { x: 'bad', y: 0 });
1295
+ const invalidAabb = aura.tilemap.queryAABB(mapHandle, { x: 0, y: 0, w: 0, h: 1 });
1296
+ const invalidRay = aura.tilemap.queryRay(mapHandle, { x: 0, y: 0, dx: 0, dy: 0, maxDistance: 10 });
1297
+ const invalidHandle = aura.tilemap.queryPoint(999999, { x: 0, y: 0 });
1298
+ const invalidModel = aura.tilemap.queryPoint({ width: 4 }, 1, 1);
1299
+ const unloadResult = aura.tilemap.unload(mapHandle);
1300
+
1301
+ aura.test.assert(invalidPoint && invalidPoint.ok === false && invalidPoint.reasonCode === 'invalid_point_args', 'tilemap invalid point input should be reason-coded');
1302
+ aura.test.assert(invalidAabb && invalidAabb.ok === false && invalidAabb.reasonCode === 'invalid_aabb_args', 'tilemap invalid aabb input should be reason-coded');
1303
+ aura.test.assert(invalidRay && invalidRay.ok === false && invalidRay.reasonCode === 'invalid_ray_args', 'tilemap invalid ray input should be reason-coded');
1304
+ aura.test.assert(invalidHandle && invalidHandle.ok === false && invalidHandle.reasonCode === 'invalid_map_handle', 'tilemap invalid handle should be reason-coded');
1305
+ aura.test.assert(invalidModel && invalidModel.ok === false && invalidModel.reasonCode === 'invalid_model', 'tilemap invalid model should be reason-coded');
1306
+ aura.test.assert(unloadResult === true, 'tilemap handle should unload cleanly after query checks');
1307
+ };
1308
+ `,
1309
+ },
1310
+ {
1311
+ id: 'tilemap-runtime-mutation-layer-controls',
1312
+ modes: ['shim', 'native'],
1313
+ namespaces: ['tilemap'],
1314
+ functions: [
1315
+ 'aura.tilemap.setTile',
1316
+ 'aura.tilemap.setRegion',
1317
+ 'aura.tilemap.removeRegion',
1318
+ 'aura.tilemap.replaceRegion',
1319
+ 'aura.tilemap.setTileCollision',
1320
+ 'aura.tilemap.setLayerFlags',
1321
+ 'aura.tilemap.setLayerVisibility',
1322
+ 'aura.tilemap.setLayerCollision',
1323
+ 'aura.tilemap.queryObjects',
1324
+ 'aura.tilemap.queryObjectsAtPoint',
1325
+ 'aura.tilemap.queryObjectsInAABB',
1326
+ ],
1327
+ nativeChecks: [
1328
+ { id: 'tilemap.mutation.setTile', expression: "typeof aura.tilemap?.setTile === 'function'" },
1329
+ { id: 'tilemap.mutation.setRegion', expression: "typeof aura.tilemap?.setRegion === 'function'" },
1330
+ { id: 'tilemap.mutation.removeRegion', expression: "typeof aura.tilemap?.removeRegion === 'function'" },
1331
+ { id: 'tilemap.mutation.replaceRegion', expression: "typeof aura.tilemap?.replaceRegion === 'function'" },
1332
+ { id: 'tilemap.mutation.setTileCollision', expression: "typeof aura.tilemap?.setTileCollision === 'function'" },
1333
+ { id: 'tilemap.layer.setLayerFlags', expression: "typeof aura.tilemap?.setLayerFlags === 'function'" },
1334
+ { id: 'tilemap.layer.setLayerVisibility', expression: "typeof aura.tilemap?.setLayerVisibility === 'function'" },
1335
+ { id: 'tilemap.layer.setLayerCollision', expression: "typeof aura.tilemap?.setLayerCollision === 'function'" },
1336
+ { id: 'tilemap.query.queryObjects', expression: "typeof aura.tilemap?.queryObjects === 'function'" },
1337
+ { id: 'tilemap.query.queryObjectsAtPoint', expression: "typeof aura.tilemap?.queryObjectsAtPoint === 'function'" },
1338
+ { id: 'tilemap.query.queryObjectsInAABB', expression: "typeof aura.tilemap?.queryObjectsInAABB === 'function'" },
1339
+ {
1340
+ id: 'tilemap.mutation.reason-codes.expanded',
1341
+ expression: "(() => { const runSample = () => { const fixture = { width: 2, height: 2, tilewidth: 16, tileheight: 16, layers: [ { name: 'ground', type: 'tilelayer', width: 2, height: 2, data: [1, 1, 1, 1] } ], tilesets: [ { firstgid: 1, image: 'tiles-a.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 }, { firstgid: 5, image: 'tiles-b.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const invalidGidArgs = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 'bad'); const unmappedBoundary = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 2); const validBoundary = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 5); const invalidReplaceArgs = aura.tilemap.replaceRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 1, 'bad'); const invalidLayerFlags = aura.tilemap.setLayerFlags(mapId, 'ground', {}); const invalidVisibility = aura.tilemap.setLayerVisibility(mapId, 'ground', 'yes'); const invalidCollision = aura.tilemap.setLayerCollision(mapId, 'ground', 'yes'); const invalidLayer = aura.tilemap.removeRegion(mapId, 'missing', { x: 0, y: 0, w: 1, h: 1 }); const invalidHandle = aura.tilemap.setRegion(999999, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 1); aura.tilemap.unload(mapId); return { invalidGidArgs: invalidGidArgs?.reasonCode, unmappedBoundary: unmappedBoundary?.reasonCode, validBoundaryOk: validBoundary?.ok === true, validBoundaryChanged: Number(validBoundary?.changedTiles || 0), invalidReplaceArgs: invalidReplaceArgs?.reasonCode, invalidLayerFlags: invalidLayerFlags?.reasonCode, invalidVisibility: invalidVisibility?.reasonCode, invalidCollision: invalidCollision?.reasonCode, invalidLayer: invalidLayer?.reasonCode, invalidHandle: invalidHandle?.reasonCode }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.invalidGidArgs === 'invalid_gid_args' && first.unmappedBoundary === 'unmapped_layer_gid' && first.validBoundaryOk === true && first.validBoundaryChanged === 1 && first.invalidReplaceArgs === 'invalid_replace_args' && first.invalidLayerFlags === 'invalid_layer_flags' && first.invalidVisibility === 'invalid_visibility_flag' && first.invalidCollision === 'invalid_collision_flag' && first.invalidLayer === 'invalid_layer_ref' && first.invalidHandle === 'invalid_map_handle'; })()",
1342
+ },
1343
+ {
1344
+ id: 'tilemap.mutation.layer-controls.deterministic',
1345
+ expression: "(() => { const runSample = () => { const fixture = { width: 4, height: 4, tilewidth: 16, tileheight: 16, solidLayerNames: ['ground', 'hazards'], layers: [ { name: 'ground', type: 'tilelayer', width: 4, height: 4, data: Array.from({ length: 16 }, () => 1) }, { name: 'hazards', type: 'tilelayer', width: 4, height: 4, data: [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] } ], tilesets: [ { firstgid: 1, image: 'tiles.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const initial = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const setRegion = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 2, h: 1 }, 0); const topRow = aura.tilemap.queryAABB(mapId, { x: 0, y: 0, w: 32, h: 16 }); const removeRegion = aura.tilemap.removeRegion(mapId, 'hazards', { x: 1, y: 1, w: 1, h: 1 }); const afterRemove = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const replaceRegion = aura.tilemap.replaceRegion(mapId, 'ground', { x: 1, y: 1, w: 1, h: 1 }, 1, 0); const afterReplace = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const collisionOff = aura.tilemap.setLayerCollision(mapId, 'ground', false); const hazardsPoint = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 }); const visibilityOff = aura.tilemap.setLayerVisibility(mapId, 'hazards', false); const hiddenDraw = aura.tilemap.draw(mapId, { cull: false }); const flags = aura.tilemap.setLayerFlags(mapId, 'hazards', { visible: true, collision: false }); const shownDraw = aura.tilemap.draw(mapId, { cull: false }); const queryMuted = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 }); const groundCollisionOn = aura.tilemap.setLayerFlags(mapId, 'ground', { collision: true }); const hazardsCollisionOn = aura.tilemap.setLayerCollision(mapId, 'hazards', true); const queryRestored = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 }); const invalidRegion = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 0, h: 1 }, 1); const invalidGid = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 999); const invalidFlags = aura.tilemap.setLayerFlags(mapId, 'ground', { collision: 'yes' }); const invalidLayer = aura.tilemap.setLayerVisibility(mapId, 'missing', true); aura.tilemap.unload(mapId); return { initialHits: Array.isArray(initial?.hits) ? initial.hits.length : -1, topRowHits: Array.isArray(topRow?.hits) ? topRow.hits.length : -1, afterRemoveHits: Array.isArray(afterRemove?.hits) ? afterRemove.hits.length : -1, afterReplaceHits: Array.isArray(afterReplace?.hits) ? afterReplace.hits.length : -1, hazardsHits: Array.isArray(hazardsPoint?.hits) ? hazardsPoint.hits.length : -1, queryMutedHits: Array.isArray(queryMuted?.hits) ? queryMuted.hits.length : -1, queryRestoredHits: Array.isArray(queryRestored?.hits) ? queryRestored.hits.length : -1, setChanged: setRegion?.changedTiles, removeChanged: removeRegion?.changedTiles, replaceChanged: replaceRegion?.changedTiles, collisionOff: collisionOff?.collision, visibilityOff: visibilityOff?.visible, hiddenOrder: hiddenDraw?.layerOrder?.join(','), shownOrder: shownDraw?.layerOrder?.join(','), flagsVisible: flags?.visible, flagsCollision: flags?.collision, groundCollisionOn: groundCollisionOn?.collision, hazardsCollisionOn: hazardsCollisionOn?.collision, invalidRegion: invalidRegion?.reasonCode, invalidGid: invalidGid?.reasonCode, invalidFlags: invalidFlags?.reasonCode, invalidLayer: invalidLayer?.reasonCode }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.initialHits === 2 && first.topRowHits === 0 && first.afterRemoveHits === 1 && first.afterReplaceHits === 0 && first.hazardsHits === 1 && first.queryMutedHits === 0 && first.queryRestoredHits === 2 && first.setChanged === 2 && first.removeChanged === 1 && first.replaceChanged === 1 && first.collisionOff === false && first.visibilityOff === false && first.hiddenOrder === 'ground' && first.shownOrder === 'ground,hazards' && first.flagsVisible === true && first.flagsCollision === false && first.groundCollisionOn === true && first.hazardsCollisionOn === true && first.invalidRegion === 'invalid_region_args' && first.invalidGid === 'unmapped_layer_gid' && first.invalidFlags === 'invalid_collision_flag' && first.invalidLayer === 'invalid_layer_ref'; })()",
1346
+ },
1347
+ {
1348
+ id: 'tilemap.object-query-and-tile-mutation.expanded.deterministic',
1349
+ expression: "(() => { const runSample = () => { const fixture = { width: 4, height: 4, tilewidth: 16, tileheight: 16, layers: [ { name: 'ground', type: 'tilelayer', width: 4, height: 4, data: [1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], properties: { solid: true } }, { name: 'objects-main', type: 'objectgroup', objects: [ { id: 1, name: 'spawn', type: 'spawn', class: 'player', x: 16, y: 16, point: true, properties: { team: 'blue' } }, { id: 2, name: 'coin', type: 'pickup', x: 32, y: 16, width: 16, height: 16, properties: { kind: 'coin', value: 5 } }, { id: 3, name: 'secret', type: 'trigger', x: 48, y: 16, width: 16, height: 16, visible: false, properties: { kind: 'secret' } } ] }, { name: 'objects-props', type: 'objectgroup', objects: [ { id: 5, name: 'barrel', type: 'prop', class: 'breakable', x: 16, y: 32, width: 16, height: 16, properties: { kind: 'destructible', hp: 3 } } ] } ], tilesets: [ { firstgid: 1, image: 'tiles.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const all = aura.tilemap.queryObjects(mapId); const allRepeat = aura.tilemap.queryObjects(mapId); const withHidden = aura.tilemap.queryObjects(mapId, { includeHidden: true }); const byLayer = aura.tilemap.queryObjects(mapId, { layer: 'objects-main' }); const byProperty = aura.tilemap.queryObjects(mapId, { property: { name: 'kind', value: 'coin' } }); const byProperties = aura.tilemap.queryObjects(mapId, { properties: { kind: 'destructible', hp: 3 } }); const pointObjects = aura.tilemap.queryObjectsAtPoint(mapId, { x: 32.5, y: 16.5 }); const pointRepeat = aura.tilemap.queryObjectsAtPoint(mapId, { x: 32.5, y: 16.5 }); const aabbObjects = aura.tilemap.queryObjectsInAABB(mapId, { x: 0, y: 0, w: 64, h: 32 }); const invalidLayer = aura.tilemap.queryObjects(mapId, { layer: 'missing' }); const invalidProperty = aura.tilemap.queryObjects(mapId, { property: { name: '' } }); const invalidPoint = aura.tilemap.queryObjectsAtPoint(mapId, { x: 'bad', y: 0 }); const invalidAabb = aura.tilemap.queryObjectsInAABB(mapId, { x: 0, y: 0, w: 0, h: 1 }); const beforePoint = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const setTileClear = aura.tilemap.setTile(mapId, 'ground', { x: 1, y: 1 }, 0); const afterClearPoint = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const setTileRestore = aura.tilemap.setTile(mapId, 'ground', 1, 1, 1); const afterRestorePoint = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const collisionOff = aura.tilemap.setTileCollision(mapId, 'ground', { x: 1, y: 1 }, false); const afterCollisionOff = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const collisionOn = aura.tilemap.setTileCollision(mapId, 'ground', 1, 1, true); const afterCollisionOn = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const collisionRegion = aura.tilemap.setTileCollision(mapId, 'ground', 0, 0, 2, 1, false); const topRow = aura.tilemap.queryAABB(mapId, { x: 0, y: 0, w: 32, h: 16 }); const invalidSetTile = aura.tilemap.setTile(mapId, 'ground', { x: 1, y: 1 }, 'bad'); const outOfBoundsSetTile = aura.tilemap.setTile(mapId, 'ground', { x: 99, y: 99 }, 1); const invalidSetTileCollision = aura.tilemap.setTileCollision(mapId, 'ground', { x: 1, y: 1 }, 'yes'); aura.tilemap.unload(mapId); return { allNames: Array.isArray(all?.hits) ? all.hits.map((entry) => entry.name).join(',') : '', allRepeatNames: Array.isArray(allRepeat?.hits) ? allRepeat.hits.map((entry) => entry.name).join(',') : '', hiddenNames: Array.isArray(withHidden?.hits) ? withHidden.hits.map((entry) => entry.name).join(',') : '', layerNames: Array.isArray(byLayer?.hits) ? byLayer.hits.map((entry) => entry.name).join(',') : '', propertyNames: Array.isArray(byProperty?.hits) ? byProperty.hits.map((entry) => entry.name).join(',') : '', propertiesNames: Array.isArray(byProperties?.hits) ? byProperties.hits.map((entry) => entry.name).join(',') : '', pointNames: Array.isArray(pointObjects?.hits) ? pointObjects.hits.map((entry) => entry.name).join(',') : '', pointRepeatNames: Array.isArray(pointRepeat?.hits) ? pointRepeat.hits.map((entry) => entry.name).join(',') : '', aabbCount: Number(aabbObjects?.count || 0), invalidLayer: invalidLayer?.reasonCode || null, invalidProperty: invalidProperty?.reasonCode || null, invalidPoint: invalidPoint?.reasonCode || null, invalidAabb: invalidAabb?.reasonCode || null, beforeHits: Array.isArray(beforePoint?.hits) ? beforePoint.hits.length : -1, afterClearHits: Array.isArray(afterClearPoint?.hits) ? afterClearPoint.hits.length : -1, afterRestoreHits: Array.isArray(afterRestorePoint?.hits) ? afterRestorePoint.hits.length : -1, afterCollisionOffHits: Array.isArray(afterCollisionOff?.hits) ? afterCollisionOff.hits.length : -1, afterCollisionOnHits: Array.isArray(afterCollisionOn?.hits) ? afterCollisionOn.hits.length : -1, topRowHits: Array.isArray(topRow?.hits) ? topRow.hits.length : -1, setTileClearChanged: Number(setTileClear?.changedTiles || 0), setTileRestoreChanged: Number(setTileRestore?.changedTiles || 0), collisionOffChanged: Number(collisionOff?.changedTiles || 0), collisionOnChanged: Number(collisionOn?.changedTiles || 0), collisionRegionChanged: Number(collisionRegion?.changedTiles || 0), invalidSetTile: invalidSetTile?.reasonCode || null, outOfBoundsSetTile: outOfBoundsSetTile?.reasonCode || null, invalidSetTileCollision: invalidSetTileCollision?.reasonCode || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.allNames === 'spawn,coin,barrel' && first.allNames === first.allRepeatNames && first.hiddenNames === 'spawn,coin,secret,barrel' && first.layerNames === 'spawn,coin' && first.propertyNames === 'coin' && first.propertiesNames === 'barrel' && first.pointNames === 'coin' && first.pointNames === first.pointRepeatNames && first.aabbCount === 2 && first.invalidLayer === 'invalid_object_layer_ref' && first.invalidProperty === 'invalid_object_property_filter' && first.invalidPoint === 'invalid_object_point_args' && first.invalidAabb === 'invalid_object_aabb_args' && first.beforeHits === 1 && first.afterClearHits === 0 && first.afterRestoreHits === 1 && first.afterCollisionOffHits === 0 && first.afterCollisionOnHits === 1 && first.topRowHits === 0 && first.setTileClearChanged === 1 && first.setTileRestoreChanged === 1 && first.collisionOffChanged === 1 && first.collisionOnChanged === 1 && first.collisionRegionChanged === 2 && first.invalidSetTile === 'invalid_gid_args' && first.outOfBoundsSetTile === 'tile_out_of_bounds' && first.invalidSetTileCollision === 'invalid_collision_flag'; })()",
1350
+ },
1351
+ ],
1352
+ frames: 1,
1353
+ source: `
1354
+ aura.setup = function () {
1355
+ const fixture = {
1356
+ width: 4,
1357
+ height: 4,
1358
+ tilewidth: 16,
1359
+ tileheight: 16,
1360
+ solidLayerNames: ['ground', 'hazards'],
1361
+ layers: [
1362
+ {
1363
+ name: 'ground',
1364
+ type: 'tilelayer',
1365
+ width: 4,
1366
+ height: 4,
1367
+ data: Array.from({ length: 16 }, () => 1),
1368
+ },
1369
+ {
1370
+ name: 'hazards',
1371
+ type: 'tilelayer',
1372
+ width: 4,
1373
+ height: 4,
1374
+ data: [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1375
+ },
1376
+ ],
1377
+ tilesets: [
1378
+ { firstgid: 1, image: 'tiles.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 },
1379
+ ],
1380
+ };
1381
+
1382
+ const mapId = aura.tilemap.import(fixture);
1383
+ const initial = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 });
1384
+ const setRegion = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 2, h: 1 }, 0);
1385
+ const topRow = aura.tilemap.queryAABB(mapId, { x: 0, y: 0, w: 32, h: 16 });
1386
+ const removeRegion = aura.tilemap.removeRegion(mapId, 'hazards', { x: 1, y: 1, w: 1, h: 1 });
1387
+ const afterRemove = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 });
1388
+ const replaceRegion = aura.tilemap.replaceRegion(mapId, 'ground', { x: 1, y: 1, w: 1, h: 1 }, 1, 0);
1389
+ const afterReplace = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 });
1390
+ const collisionOff = aura.tilemap.setLayerCollision(mapId, 'ground', false);
1391
+ const hazardsPoint = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 });
1392
+ const visibilityOff = aura.tilemap.setLayerVisibility(mapId, 'hazards', false);
1393
+ const hiddenDraw = aura.tilemap.draw(mapId, { cull: false });
1394
+ const flags = aura.tilemap.setLayerFlags(mapId, 'hazards', { visible: true, collision: false });
1395
+ const shownDraw = aura.tilemap.draw(mapId, { cull: false });
1396
+ const queryMuted = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 });
1397
+ const groundCollisionOn = aura.tilemap.setLayerFlags(mapId, 'ground', { collision: true });
1398
+ const hazardsCollisionOn = aura.tilemap.setLayerCollision(mapId, 'hazards', true);
1399
+ const queryRestored = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 });
1400
+ const invalidRegion = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 0, h: 1 }, 1);
1401
+ const invalidGid = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 999);
1402
+ const invalidFlags = aura.tilemap.setLayerFlags(mapId, 'ground', { collision: 'yes' });
1403
+ const invalidLayer = aura.tilemap.setLayerVisibility(mapId, 'missing', true);
1404
+ const unload = aura.tilemap.unload(mapId);
1405
+
1406
+ aura.test.assert(Array.isArray(initial.hits) && initial.hits.length === 2, 'tilemap initial layered query should hit both layers');
1407
+ aura.test.assert(setRegion && setRegion.ok === true && setRegion.changedTiles === 2, 'tilemap setRegion should mutate deterministic tile count');
1408
+ aura.test.assert(Array.isArray(topRow.hits) && topRow.hits.length === 0, 'tilemap setRegion should affect deterministic top-row query hits');
1409
+ aura.test.assert(removeRegion && removeRegion.ok === true && removeRegion.changedTiles === 1, 'tilemap removeRegion should clear deterministic tile count');
1410
+ aura.test.assert(Array.isArray(afterRemove.hits) && afterRemove.hits.length === 1, 'tilemap removeRegion should reduce layered hits');
1411
+ aura.test.assert(replaceRegion && replaceRegion.ok === true && replaceRegion.changedTiles === 1, 'tilemap replaceRegion should deterministically swap target gid');
1412
+ aura.test.assert(Array.isArray(afterReplace.hits) && afterReplace.hits.length === 0, 'tilemap replaceRegion should affect point query deterministically');
1413
+ aura.test.assert(collisionOff && collisionOff.ok === true && collisionOff.collision === false, 'tilemap setLayerCollision should disable query collision');
1414
+ aura.test.assert(Array.isArray(hazardsPoint.hits) && hazardsPoint.hits.length === 1, 'tilemap collision toggle should isolate hazard layer hits');
1415
+ aura.test.assert(visibilityOff && visibilityOff.ok === true && visibilityOff.visible === false, 'tilemap setLayerVisibility should update visibility deterministically');
1416
+ aura.test.assert(hiddenDraw && hiddenDraw.layerOrder.join(',') === 'ground', 'tilemap hidden layer should be omitted from draw ordering');
1417
+ aura.test.assert(flags && flags.ok === true && flags.visible === true && flags.collision === false, 'tilemap setLayerFlags should update visible/collision together');
1418
+ aura.test.assert(shownDraw && shownDraw.layerOrder.join(',') === 'ground,hazards', 'tilemap setLayerFlags should restore draw ordering deterministically');
1419
+ aura.test.assert(Array.isArray(queryMuted.hits) && queryMuted.hits.length === 0, 'tilemap collision-off flags should mute query hits');
1420
+ aura.test.assert(groundCollisionOn && groundCollisionOn.collision === true, 'tilemap ground collision flag should restore deterministically');
1421
+ aura.test.assert(hazardsCollisionOn && hazardsCollisionOn.collision === true, 'tilemap hazard collision toggle should restore deterministically');
1422
+ aura.test.assert(Array.isArray(queryRestored.hits) && queryRestored.hits.length === 2, 'tilemap collision flags should restore layered query hits');
1423
+ aura.test.assert(invalidRegion && invalidRegion.ok === false && invalidRegion.reasonCode === 'invalid_region_args', 'tilemap invalid region args should be reason-coded');
1424
+ aura.test.assert(invalidGid && invalidGid.ok === false && invalidGid.reasonCode === 'unmapped_layer_gid', 'tilemap unmapped gid mutation should be reason-coded');
1425
+ aura.test.assert(invalidFlags && invalidFlags.ok === false && invalidFlags.reasonCode === 'invalid_collision_flag', 'tilemap invalid collision flag should be reason-coded');
1426
+ aura.test.assert(invalidLayer && invalidLayer.ok === false && invalidLayer.reasonCode === 'invalid_layer_ref', 'tilemap invalid layer ref should be reason-coded');
1427
+ aura.test.equal(unload, true, 'tilemap handle should unload after mutation checks');
1428
+ };
1429
+ `,
1430
+ }
1431
+ ];