hbengine 0.2.3

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 (561) hide show
  1. package/README.md +20 -0
  2. package/engine-cli/connect.mjs +73 -0
  3. package/engine-cli/hbengine.mjs +565 -0
  4. package/engine-cli/package.json +1 -0
  5. package/engine-cli/templates.mjs +14 -0
  6. package/engine-conformance/analyse-worker.mjs +12 -0
  7. package/engine-conformance/analyse.mjs +4458 -0
  8. package/engine-conformance/bundle.mjs +208 -0
  9. package/engine-conformance/clock.mjs +294 -0
  10. package/engine-conformance/csp.go +56 -0
  11. package/engine-conformance/csp.json +6 -0
  12. package/engine-conformance/csp.mjs +20 -0
  13. package/engine-conformance/gzip-sizes.mjs +9 -0
  14. package/engine-conformance/package.json +1 -0
  15. package/engine-conformance/picture.mjs +55 -0
  16. package/engine-conformance/report.go +349 -0
  17. package/engine-conformance/run.mjs +1217 -0
  18. package/engine-conformance/scan.mjs +476 -0
  19. package/engine-conformance/testdata/clock-overlay/bundle/assets.json +1 -0
  20. package/engine-conformance/testdata/clock-overlay/bundle/game.css +17 -0
  21. package/engine-conformance/testdata/clock-overlay/bundle/game.js +83 -0
  22. package/engine-conformance/testdata/clock-overlay/bundle/hbtest.json +4 -0
  23. package/engine-conformance/testdata/clock-overlay/bundle/index.html +34 -0
  24. package/engine-conformance/testdata/clock-probe/bundle/assets.json +1 -0
  25. package/engine-conformance/testdata/clock-probe/bundle/game.css +17 -0
  26. package/engine-conformance/testdata/clock-probe/bundle/game.js +65 -0
  27. package/engine-conformance/testdata/clock-probe/bundle/hbtest.json +4 -0
  28. package/engine-conformance/testdata/clock-probe/bundle/index.html +35 -0
  29. package/engine-conformance/testdata/clock-probe/bundle/probe-worker.js +46 -0
  30. package/engine-conformance/testdata/clock-probe/bundle/probe.js +210 -0
  31. package/engine-conformance/testdata/clock-probe/math/dice.tsv +100 -0
  32. package/engine-conformance/testdata/clock-probe/math/manifest.json +2925 -0
  33. package/engine-conformance/testdata/clock-worker/bundle/assets.json +1 -0
  34. package/engine-conformance/testdata/clock-worker/bundle/clock-worker.js +14 -0
  35. package/engine-conformance/testdata/clock-worker/bundle/game.css +17 -0
  36. package/engine-conformance/testdata/clock-worker/bundle/game.js +61 -0
  37. package/engine-conformance/testdata/clock-worker/bundle/hbtest.json +4 -0
  38. package/engine-conformance/testdata/clock-worker/bundle/index.html +35 -0
  39. package/engine-conformance/testdata/clock-worker/math/dice.tsv +100 -0
  40. package/engine-conformance/testdata/clock-worker/math/manifest.json +2925 -0
  41. package/engine-conformance/travel.mjs +1209 -0
  42. package/engine-conformance/vendor/acorn/LICENSE +21 -0
  43. package/engine-conformance/vendor/acorn/acorn.mjs +6266 -0
  44. package/engine-conformance/wasm-releases.json +168 -0
  45. package/engine-examples/README.md +23 -0
  46. package/engine-examples/coin/AGENTS.md +3 -0
  47. package/engine-examples/coin/README.md +7 -0
  48. package/engine-examples/coin/bundle/assets.json +1 -0
  49. package/engine-examples/coin/bundle/game.css +5 -0
  50. package/engine-examples/coin/bundle/game.js +19 -0
  51. package/engine-examples/coin/bundle/hbtest.json +5 -0
  52. package/engine-examples/coin/bundle/index.html +16 -0
  53. package/engine-examples/coin/math/coin.tsv +2 -0
  54. package/engine-examples/coin/math/manifest.json +17 -0
  55. package/engine-examples/crash/AGENTS.md +3 -0
  56. package/engine-examples/crash/README.md +14 -0
  57. package/engine-examples/crash/bundle/assets.json +1 -0
  58. package/engine-examples/crash/bundle/game.css +8 -0
  59. package/engine-examples/crash/bundle/game.js +90 -0
  60. package/engine-examples/crash/bundle/hbtest.json +5 -0
  61. package/engine-examples/crash/bundle/index.html +20 -0
  62. package/engine-examples/crash/math/crash-01.tsv +2 -0
  63. package/engine-examples/crash/math/crash-02.tsv +2 -0
  64. package/engine-examples/crash/math/crash-03.tsv +2 -0
  65. package/engine-examples/crash/math/crash-04.tsv +2 -0
  66. package/engine-examples/crash/math/crash-05.tsv +2 -0
  67. package/engine-examples/crash/math/crash-06.tsv +2 -0
  68. package/engine-examples/crash/math/crash-07.tsv +2 -0
  69. package/engine-examples/crash/math/crash-08.tsv +2 -0
  70. package/engine-examples/crash/math/crash-09.tsv +2 -0
  71. package/engine-examples/crash/math/crash-10.tsv +2 -0
  72. package/engine-examples/crash/math/crash-11.tsv +2 -0
  73. package/engine-examples/crash/math/crash-12.tsv +2 -0
  74. package/engine-examples/crash/math/crash-13.tsv +2 -0
  75. package/engine-examples/crash/math/crash-14.tsv +2 -0
  76. package/engine-examples/crash/math/crash-15.tsv +2 -0
  77. package/engine-examples/crash/math/crash-16.tsv +2 -0
  78. package/engine-examples/crash/math/crash-17.tsv +2 -0
  79. package/engine-examples/crash/math/crash-18.tsv +2 -0
  80. package/engine-examples/crash/math/crash-19.tsv +2 -0
  81. package/engine-examples/crash/math/crash-20.tsv +2 -0
  82. package/engine-examples/crash/math/crash-21.tsv +2 -0
  83. package/engine-examples/crash/math/crash-22.tsv +2 -0
  84. package/engine-examples/crash/math/crash-23.tsv +2 -0
  85. package/engine-examples/crash/math/crash-24.tsv +2 -0
  86. package/engine-examples/crash/math/crash-25.tsv +2 -0
  87. package/engine-examples/crash/math/crash-26.tsv +2 -0
  88. package/engine-examples/crash/math/crash-27.tsv +2 -0
  89. package/engine-examples/crash/math/crash-28.tsv +2 -0
  90. package/engine-examples/crash/math/crash-29.tsv +2 -0
  91. package/engine-examples/crash/math/crash-30.tsv +2 -0
  92. package/engine-examples/crash/math/crash-31.tsv +2 -0
  93. package/engine-examples/crash/math/crash-32.tsv +2 -0
  94. package/engine-examples/crash/math/crash-33.tsv +2 -0
  95. package/engine-examples/crash/math/crash-34.tsv +2 -0
  96. package/engine-examples/crash/math/crash-35.tsv +2 -0
  97. package/engine-examples/crash/math/crash-36.tsv +2 -0
  98. package/engine-examples/crash/math/crash-37.tsv +2 -0
  99. package/engine-examples/crash/math/crash-38.tsv +2 -0
  100. package/engine-examples/crash/math/crash-39.tsv +2 -0
  101. package/engine-examples/crash/math/crash-40.tsv +2 -0
  102. package/engine-examples/crash/math/crash-41.tsv +2 -0
  103. package/engine-examples/crash/math/crash-42.tsv +2 -0
  104. package/engine-examples/crash/math/crash-43.tsv +2 -0
  105. package/engine-examples/crash/math/crash-44.tsv +2 -0
  106. package/engine-examples/crash/math/crash-45.tsv +2 -0
  107. package/engine-examples/crash/math/crash-46.tsv +2 -0
  108. package/engine-examples/crash/math/crash-47.tsv +2 -0
  109. package/engine-examples/crash/math/crash-48.tsv +2 -0
  110. package/engine-examples/crash/math/crash-49.tsv +2 -0
  111. package/engine-examples/crash/math/crash-50.tsv +2 -0
  112. package/engine-examples/crash/math/crash-51.tsv +2 -0
  113. package/engine-examples/crash/math/crash-52.tsv +2 -0
  114. package/engine-examples/crash/math/crash-53.tsv +2 -0
  115. package/engine-examples/crash/math/crash-54.tsv +2 -0
  116. package/engine-examples/crash/math/crash-55.tsv +2 -0
  117. package/engine-examples/crash/math/crash-56.tsv +2 -0
  118. package/engine-examples/crash/math/crash-57.tsv +2 -0
  119. package/engine-examples/crash/math/crash-58.tsv +2 -0
  120. package/engine-examples/crash/math/crash-59.tsv +2 -0
  121. package/engine-examples/crash/math/crash-60.tsv +2 -0
  122. package/engine-examples/crash/math/crash-61.tsv +2 -0
  123. package/engine-examples/crash/math/crash-62.tsv +2 -0
  124. package/engine-examples/crash/math/crash-63.tsv +2 -0
  125. package/engine-examples/crash/math/crash-64.tsv +2 -0
  126. package/engine-examples/crash/math/gen.mjs +31 -0
  127. package/engine-examples/crash/math/manifest.json +1 -0
  128. package/engine-examples/dice/AGENTS.md +3 -0
  129. package/engine-examples/dice/README.md +25 -0
  130. package/engine-examples/dice/bundle/assets.json +1 -0
  131. package/engine-examples/dice/bundle/game.css +17 -0
  132. package/engine-examples/dice/bundle/game.js +61 -0
  133. package/engine-examples/dice/bundle/hbtest.json +4 -0
  134. package/engine-examples/dice/bundle/index.html +34 -0
  135. package/engine-examples/dice/math/dice.tsv +100 -0
  136. package/engine-examples/dice/math/manifest.json +2925 -0
  137. package/engine-examples/keno/AGENTS.md +3 -0
  138. package/engine-examples/keno/README.md +11 -0
  139. package/engine-examples/keno/bundle/assets.json +1 -0
  140. package/engine-examples/keno/bundle/game.css +14 -0
  141. package/engine-examples/keno/bundle/game.js +86 -0
  142. package/engine-examples/keno/bundle/hbtest.json +4 -0
  143. package/engine-examples/keno/bundle/i18n.json +342 -0
  144. package/engine-examples/keno/bundle/index.html +20 -0
  145. package/engine-examples/keno/bundle/model.json +1 -0
  146. package/engine-examples/keno/math/gen.mjs +35 -0
  147. package/engine-examples/keno/math/keno-picks-1.tsv +2 -0
  148. package/engine-examples/keno/math/keno-picks-10.tsv +11 -0
  149. package/engine-examples/keno/math/keno-picks-2.tsv +3 -0
  150. package/engine-examples/keno/math/keno-picks-3.tsv +4 -0
  151. package/engine-examples/keno/math/keno-picks-4.tsv +5 -0
  152. package/engine-examples/keno/math/keno-picks-5.tsv +6 -0
  153. package/engine-examples/keno/math/keno-picks-6.tsv +7 -0
  154. package/engine-examples/keno/math/keno-picks-7.tsv +8 -0
  155. package/engine-examples/keno/math/keno-picks-8.tsv +9 -0
  156. package/engine-examples/keno/math/keno-picks-9.tsv +10 -0
  157. package/engine-examples/keno/math/manifest.json +1 -0
  158. package/engine-examples/keno/math-kit/pack.mjs +165 -0
  159. package/engine-examples/keno/math-kit/package.json +8 -0
  160. package/engine-examples/keno/math-kit/slot.mjs +94 -0
  161. package/engine-examples/keno/math-kit/tumble.mjs +94 -0
  162. package/engine-examples/lines/AGENTS.md +3 -0
  163. package/engine-examples/lines/README.md +20 -0
  164. package/engine-examples/lines/bundle/assets.json +1 -0
  165. package/engine-examples/lines/bundle/game.css +16 -0
  166. package/engine-examples/lines/bundle/game.js +125 -0
  167. package/engine-examples/lines/bundle/hbtest.json +4 -0
  168. package/engine-examples/lines/bundle/i18n.json +242 -0
  169. package/engine-examples/lines/bundle/index.html +23 -0
  170. package/engine-examples/lines/bundle/model.json +1 -0
  171. package/engine-examples/lines/math/gen.mjs +54 -0
  172. package/engine-examples/lines/math-kit/pack.mjs +165 -0
  173. package/engine-examples/lines/math-kit/package.json +8 -0
  174. package/engine-examples/lines/math-kit/slot.mjs +94 -0
  175. package/engine-examples/lines/math-kit/tumble.mjs +94 -0
  176. package/engine-examples/mines/AGENTS.md +3 -0
  177. package/engine-examples/mines/README.md +13 -0
  178. package/engine-examples/mines/bundle/assets.json +1 -0
  179. package/engine-examples/mines/bundle/game.css +12 -0
  180. package/engine-examples/mines/bundle/game.js +58 -0
  181. package/engine-examples/mines/bundle/hbtest.json +8 -0
  182. package/engine-examples/mines/bundle/index.html +16 -0
  183. package/engine-examples/mines/math/gen.mjs +40 -0
  184. package/engine-examples/mines/math/manifest.json +1 -0
  185. package/engine-examples/mines/math/mines-1-1.tsv +2 -0
  186. package/engine-examples/mines/math/mines-1-2.tsv +2 -0
  187. package/engine-examples/mines/math/mines-1-22.tsv +2 -0
  188. package/engine-examples/mines/math/mines-1-23.tsv +2 -0
  189. package/engine-examples/mines/math/mines-1-24.tsv +2 -0
  190. package/engine-examples/mines/math/mines-1-3.tsv +2 -0
  191. package/engine-examples/mines/math/mines-10-1.tsv +2 -0
  192. package/engine-examples/mines/math/mines-10-10.tsv +2 -0
  193. package/engine-examples/mines/math/mines-10-11.tsv +2 -0
  194. package/engine-examples/mines/math/mines-10-12.tsv +2 -0
  195. package/engine-examples/mines/math/mines-10-13.tsv +2 -0
  196. package/engine-examples/mines/math/mines-10-14.tsv +2 -0
  197. package/engine-examples/mines/math/mines-10-15.tsv +2 -0
  198. package/engine-examples/mines/math/mines-10-2.tsv +2 -0
  199. package/engine-examples/mines/math/mines-10-3.tsv +2 -0
  200. package/engine-examples/mines/math/mines-10-4.tsv +2 -0
  201. package/engine-examples/mines/math/mines-10-5.tsv +2 -0
  202. package/engine-examples/mines/math/mines-10-6.tsv +2 -0
  203. package/engine-examples/mines/math/mines-10-7.tsv +2 -0
  204. package/engine-examples/mines/math/mines-10-8.tsv +2 -0
  205. package/engine-examples/mines/math/mines-10-9.tsv +2 -0
  206. package/engine-examples/mines/math/mines-11-1.tsv +2 -0
  207. package/engine-examples/mines/math/mines-11-10.tsv +2 -0
  208. package/engine-examples/mines/math/mines-11-11.tsv +2 -0
  209. package/engine-examples/mines/math/mines-11-12.tsv +2 -0
  210. package/engine-examples/mines/math/mines-11-13.tsv +2 -0
  211. package/engine-examples/mines/math/mines-11-14.tsv +2 -0
  212. package/engine-examples/mines/math/mines-11-2.tsv +2 -0
  213. package/engine-examples/mines/math/mines-11-3.tsv +2 -0
  214. package/engine-examples/mines/math/mines-11-4.tsv +2 -0
  215. package/engine-examples/mines/math/mines-11-5.tsv +2 -0
  216. package/engine-examples/mines/math/mines-11-6.tsv +2 -0
  217. package/engine-examples/mines/math/mines-11-7.tsv +2 -0
  218. package/engine-examples/mines/math/mines-11-8.tsv +2 -0
  219. package/engine-examples/mines/math/mines-11-9.tsv +2 -0
  220. package/engine-examples/mines/math/mines-12-1.tsv +2 -0
  221. package/engine-examples/mines/math/mines-12-10.tsv +2 -0
  222. package/engine-examples/mines/math/mines-12-11.tsv +2 -0
  223. package/engine-examples/mines/math/mines-12-12.tsv +2 -0
  224. package/engine-examples/mines/math/mines-12-13.tsv +2 -0
  225. package/engine-examples/mines/math/mines-12-2.tsv +2 -0
  226. package/engine-examples/mines/math/mines-12-3.tsv +2 -0
  227. package/engine-examples/mines/math/mines-12-4.tsv +2 -0
  228. package/engine-examples/mines/math/mines-12-5.tsv +2 -0
  229. package/engine-examples/mines/math/mines-12-6.tsv +2 -0
  230. package/engine-examples/mines/math/mines-12-7.tsv +2 -0
  231. package/engine-examples/mines/math/mines-12-8.tsv +2 -0
  232. package/engine-examples/mines/math/mines-12-9.tsv +2 -0
  233. package/engine-examples/mines/math/mines-13-1.tsv +2 -0
  234. package/engine-examples/mines/math/mines-13-10.tsv +2 -0
  235. package/engine-examples/mines/math/mines-13-11.tsv +2 -0
  236. package/engine-examples/mines/math/mines-13-12.tsv +2 -0
  237. package/engine-examples/mines/math/mines-13-2.tsv +2 -0
  238. package/engine-examples/mines/math/mines-13-3.tsv +2 -0
  239. package/engine-examples/mines/math/mines-13-4.tsv +2 -0
  240. package/engine-examples/mines/math/mines-13-5.tsv +2 -0
  241. package/engine-examples/mines/math/mines-13-6.tsv +2 -0
  242. package/engine-examples/mines/math/mines-13-7.tsv +2 -0
  243. package/engine-examples/mines/math/mines-13-8.tsv +2 -0
  244. package/engine-examples/mines/math/mines-13-9.tsv +2 -0
  245. package/engine-examples/mines/math/mines-14-1.tsv +2 -0
  246. package/engine-examples/mines/math/mines-14-10.tsv +2 -0
  247. package/engine-examples/mines/math/mines-14-11.tsv +2 -0
  248. package/engine-examples/mines/math/mines-14-2.tsv +2 -0
  249. package/engine-examples/mines/math/mines-14-3.tsv +2 -0
  250. package/engine-examples/mines/math/mines-14-4.tsv +2 -0
  251. package/engine-examples/mines/math/mines-14-5.tsv +2 -0
  252. package/engine-examples/mines/math/mines-14-6.tsv +2 -0
  253. package/engine-examples/mines/math/mines-14-7.tsv +2 -0
  254. package/engine-examples/mines/math/mines-14-8.tsv +2 -0
  255. package/engine-examples/mines/math/mines-14-9.tsv +2 -0
  256. package/engine-examples/mines/math/mines-15-1.tsv +2 -0
  257. package/engine-examples/mines/math/mines-15-10.tsv +2 -0
  258. package/engine-examples/mines/math/mines-15-2.tsv +2 -0
  259. package/engine-examples/mines/math/mines-15-3.tsv +2 -0
  260. package/engine-examples/mines/math/mines-15-4.tsv +2 -0
  261. package/engine-examples/mines/math/mines-15-5.tsv +2 -0
  262. package/engine-examples/mines/math/mines-15-6.tsv +2 -0
  263. package/engine-examples/mines/math/mines-15-7.tsv +2 -0
  264. package/engine-examples/mines/math/mines-15-8.tsv +2 -0
  265. package/engine-examples/mines/math/mines-15-9.tsv +2 -0
  266. package/engine-examples/mines/math/mines-16-1.tsv +2 -0
  267. package/engine-examples/mines/math/mines-16-2.tsv +2 -0
  268. package/engine-examples/mines/math/mines-16-3.tsv +2 -0
  269. package/engine-examples/mines/math/mines-16-4.tsv +2 -0
  270. package/engine-examples/mines/math/mines-16-5.tsv +2 -0
  271. package/engine-examples/mines/math/mines-16-6.tsv +2 -0
  272. package/engine-examples/mines/math/mines-16-7.tsv +2 -0
  273. package/engine-examples/mines/math/mines-16-8.tsv +2 -0
  274. package/engine-examples/mines/math/mines-16-9.tsv +2 -0
  275. package/engine-examples/mines/math/mines-17-1.tsv +2 -0
  276. package/engine-examples/mines/math/mines-17-2.tsv +2 -0
  277. package/engine-examples/mines/math/mines-17-3.tsv +2 -0
  278. package/engine-examples/mines/math/mines-17-4.tsv +2 -0
  279. package/engine-examples/mines/math/mines-17-5.tsv +2 -0
  280. package/engine-examples/mines/math/mines-17-6.tsv +2 -0
  281. package/engine-examples/mines/math/mines-17-7.tsv +2 -0
  282. package/engine-examples/mines/math/mines-17-8.tsv +2 -0
  283. package/engine-examples/mines/math/mines-18-1.tsv +2 -0
  284. package/engine-examples/mines/math/mines-18-2.tsv +2 -0
  285. package/engine-examples/mines/math/mines-18-3.tsv +2 -0
  286. package/engine-examples/mines/math/mines-18-4.tsv +2 -0
  287. package/engine-examples/mines/math/mines-18-5.tsv +2 -0
  288. package/engine-examples/mines/math/mines-18-6.tsv +2 -0
  289. package/engine-examples/mines/math/mines-18-7.tsv +2 -0
  290. package/engine-examples/mines/math/mines-19-1.tsv +2 -0
  291. package/engine-examples/mines/math/mines-19-2.tsv +2 -0
  292. package/engine-examples/mines/math/mines-19-3.tsv +2 -0
  293. package/engine-examples/mines/math/mines-19-4.tsv +2 -0
  294. package/engine-examples/mines/math/mines-19-5.tsv +2 -0
  295. package/engine-examples/mines/math/mines-19-6.tsv +2 -0
  296. package/engine-examples/mines/math/mines-2-1.tsv +2 -0
  297. package/engine-examples/mines/math/mines-2-19.tsv +2 -0
  298. package/engine-examples/mines/math/mines-2-2.tsv +2 -0
  299. package/engine-examples/mines/math/mines-2-20.tsv +2 -0
  300. package/engine-examples/mines/math/mines-2-21.tsv +2 -0
  301. package/engine-examples/mines/math/mines-2-22.tsv +2 -0
  302. package/engine-examples/mines/math/mines-2-23.tsv +2 -0
  303. package/engine-examples/mines/math/mines-2-3.tsv +2 -0
  304. package/engine-examples/mines/math/mines-2-4.tsv +2 -0
  305. package/engine-examples/mines/math/mines-2-5.tsv +2 -0
  306. package/engine-examples/mines/math/mines-20-1.tsv +2 -0
  307. package/engine-examples/mines/math/mines-20-2.tsv +2 -0
  308. package/engine-examples/mines/math/mines-20-3.tsv +2 -0
  309. package/engine-examples/mines/math/mines-20-4.tsv +2 -0
  310. package/engine-examples/mines/math/mines-20-5.tsv +2 -0
  311. package/engine-examples/mines/math/mines-21-1.tsv +2 -0
  312. package/engine-examples/mines/math/mines-21-2.tsv +2 -0
  313. package/engine-examples/mines/math/mines-21-3.tsv +2 -0
  314. package/engine-examples/mines/math/mines-21-4.tsv +2 -0
  315. package/engine-examples/mines/math/mines-22-1.tsv +2 -0
  316. package/engine-examples/mines/math/mines-22-2.tsv +2 -0
  317. package/engine-examples/mines/math/mines-22-3.tsv +2 -0
  318. package/engine-examples/mines/math/mines-23-1.tsv +2 -0
  319. package/engine-examples/mines/math/mines-23-2.tsv +2 -0
  320. package/engine-examples/mines/math/mines-24-1.tsv +2 -0
  321. package/engine-examples/mines/math/mines-3-1.tsv +2 -0
  322. package/engine-examples/mines/math/mines-3-17.tsv +2 -0
  323. package/engine-examples/mines/math/mines-3-18.tsv +2 -0
  324. package/engine-examples/mines/math/mines-3-19.tsv +2 -0
  325. package/engine-examples/mines/math/mines-3-2.tsv +2 -0
  326. package/engine-examples/mines/math/mines-3-20.tsv +2 -0
  327. package/engine-examples/mines/math/mines-3-21.tsv +2 -0
  328. package/engine-examples/mines/math/mines-3-22.tsv +2 -0
  329. package/engine-examples/mines/math/mines-3-3.tsv +2 -0
  330. package/engine-examples/mines/math/mines-3-4.tsv +2 -0
  331. package/engine-examples/mines/math/mines-3-5.tsv +2 -0
  332. package/engine-examples/mines/math/mines-3-6.tsv +2 -0
  333. package/engine-examples/mines/math/mines-4-1.tsv +2 -0
  334. package/engine-examples/mines/math/mines-4-13.tsv +2 -0
  335. package/engine-examples/mines/math/mines-4-14.tsv +2 -0
  336. package/engine-examples/mines/math/mines-4-15.tsv +2 -0
  337. package/engine-examples/mines/math/mines-4-16.tsv +2 -0
  338. package/engine-examples/mines/math/mines-4-17.tsv +2 -0
  339. package/engine-examples/mines/math/mines-4-18.tsv +2 -0
  340. package/engine-examples/mines/math/mines-4-19.tsv +2 -0
  341. package/engine-examples/mines/math/mines-4-2.tsv +2 -0
  342. package/engine-examples/mines/math/mines-4-20.tsv +2 -0
  343. package/engine-examples/mines/math/mines-4-21.tsv +2 -0
  344. package/engine-examples/mines/math/mines-4-3.tsv +2 -0
  345. package/engine-examples/mines/math/mines-4-4.tsv +2 -0
  346. package/engine-examples/mines/math/mines-4-5.tsv +2 -0
  347. package/engine-examples/mines/math/mines-4-6.tsv +2 -0
  348. package/engine-examples/mines/math/mines-4-7.tsv +2 -0
  349. package/engine-examples/mines/math/mines-4-8.tsv +2 -0
  350. package/engine-examples/mines/math/mines-4-9.tsv +2 -0
  351. package/engine-examples/mines/math/mines-5-1.tsv +2 -0
  352. package/engine-examples/mines/math/mines-5-10.tsv +2 -0
  353. package/engine-examples/mines/math/mines-5-11.tsv +2 -0
  354. package/engine-examples/mines/math/mines-5-12.tsv +2 -0
  355. package/engine-examples/mines/math/mines-5-13.tsv +2 -0
  356. package/engine-examples/mines/math/mines-5-14.tsv +2 -0
  357. package/engine-examples/mines/math/mines-5-15.tsv +2 -0
  358. package/engine-examples/mines/math/mines-5-16.tsv +2 -0
  359. package/engine-examples/mines/math/mines-5-17.tsv +2 -0
  360. package/engine-examples/mines/math/mines-5-18.tsv +2 -0
  361. package/engine-examples/mines/math/mines-5-19.tsv +2 -0
  362. package/engine-examples/mines/math/mines-5-2.tsv +2 -0
  363. package/engine-examples/mines/math/mines-5-20.tsv +2 -0
  364. package/engine-examples/mines/math/mines-5-3.tsv +2 -0
  365. package/engine-examples/mines/math/mines-5-4.tsv +2 -0
  366. package/engine-examples/mines/math/mines-5-5.tsv +2 -0
  367. package/engine-examples/mines/math/mines-5-6.tsv +2 -0
  368. package/engine-examples/mines/math/mines-5-7.tsv +2 -0
  369. package/engine-examples/mines/math/mines-5-8.tsv +2 -0
  370. package/engine-examples/mines/math/mines-5-9.tsv +2 -0
  371. package/engine-examples/mines/math/mines-6-1.tsv +2 -0
  372. package/engine-examples/mines/math/mines-6-10.tsv +2 -0
  373. package/engine-examples/mines/math/mines-6-11.tsv +2 -0
  374. package/engine-examples/mines/math/mines-6-12.tsv +2 -0
  375. package/engine-examples/mines/math/mines-6-13.tsv +2 -0
  376. package/engine-examples/mines/math/mines-6-14.tsv +2 -0
  377. package/engine-examples/mines/math/mines-6-15.tsv +2 -0
  378. package/engine-examples/mines/math/mines-6-16.tsv +2 -0
  379. package/engine-examples/mines/math/mines-6-17.tsv +2 -0
  380. package/engine-examples/mines/math/mines-6-18.tsv +2 -0
  381. package/engine-examples/mines/math/mines-6-19.tsv +2 -0
  382. package/engine-examples/mines/math/mines-6-2.tsv +2 -0
  383. package/engine-examples/mines/math/mines-6-3.tsv +2 -0
  384. package/engine-examples/mines/math/mines-6-4.tsv +2 -0
  385. package/engine-examples/mines/math/mines-6-5.tsv +2 -0
  386. package/engine-examples/mines/math/mines-6-6.tsv +2 -0
  387. package/engine-examples/mines/math/mines-6-7.tsv +2 -0
  388. package/engine-examples/mines/math/mines-6-8.tsv +2 -0
  389. package/engine-examples/mines/math/mines-6-9.tsv +2 -0
  390. package/engine-examples/mines/math/mines-7-1.tsv +2 -0
  391. package/engine-examples/mines/math/mines-7-10.tsv +2 -0
  392. package/engine-examples/mines/math/mines-7-11.tsv +2 -0
  393. package/engine-examples/mines/math/mines-7-12.tsv +2 -0
  394. package/engine-examples/mines/math/mines-7-13.tsv +2 -0
  395. package/engine-examples/mines/math/mines-7-14.tsv +2 -0
  396. package/engine-examples/mines/math/mines-7-15.tsv +2 -0
  397. package/engine-examples/mines/math/mines-7-16.tsv +2 -0
  398. package/engine-examples/mines/math/mines-7-17.tsv +2 -0
  399. package/engine-examples/mines/math/mines-7-18.tsv +2 -0
  400. package/engine-examples/mines/math/mines-7-2.tsv +2 -0
  401. package/engine-examples/mines/math/mines-7-3.tsv +2 -0
  402. package/engine-examples/mines/math/mines-7-4.tsv +2 -0
  403. package/engine-examples/mines/math/mines-7-5.tsv +2 -0
  404. package/engine-examples/mines/math/mines-7-6.tsv +2 -0
  405. package/engine-examples/mines/math/mines-7-7.tsv +2 -0
  406. package/engine-examples/mines/math/mines-7-8.tsv +2 -0
  407. package/engine-examples/mines/math/mines-7-9.tsv +2 -0
  408. package/engine-examples/mines/math/mines-8-1.tsv +2 -0
  409. package/engine-examples/mines/math/mines-8-10.tsv +2 -0
  410. package/engine-examples/mines/math/mines-8-11.tsv +2 -0
  411. package/engine-examples/mines/math/mines-8-12.tsv +2 -0
  412. package/engine-examples/mines/math/mines-8-13.tsv +2 -0
  413. package/engine-examples/mines/math/mines-8-14.tsv +2 -0
  414. package/engine-examples/mines/math/mines-8-15.tsv +2 -0
  415. package/engine-examples/mines/math/mines-8-16.tsv +2 -0
  416. package/engine-examples/mines/math/mines-8-17.tsv +2 -0
  417. package/engine-examples/mines/math/mines-8-2.tsv +2 -0
  418. package/engine-examples/mines/math/mines-8-3.tsv +2 -0
  419. package/engine-examples/mines/math/mines-8-4.tsv +2 -0
  420. package/engine-examples/mines/math/mines-8-5.tsv +2 -0
  421. package/engine-examples/mines/math/mines-8-6.tsv +2 -0
  422. package/engine-examples/mines/math/mines-8-7.tsv +2 -0
  423. package/engine-examples/mines/math/mines-8-8.tsv +2 -0
  424. package/engine-examples/mines/math/mines-8-9.tsv +2 -0
  425. package/engine-examples/mines/math/mines-9-1.tsv +2 -0
  426. package/engine-examples/mines/math/mines-9-10.tsv +2 -0
  427. package/engine-examples/mines/math/mines-9-11.tsv +2 -0
  428. package/engine-examples/mines/math/mines-9-12.tsv +2 -0
  429. package/engine-examples/mines/math/mines-9-13.tsv +2 -0
  430. package/engine-examples/mines/math/mines-9-14.tsv +2 -0
  431. package/engine-examples/mines/math/mines-9-15.tsv +2 -0
  432. package/engine-examples/mines/math/mines-9-16.tsv +2 -0
  433. package/engine-examples/mines/math/mines-9-2.tsv +2 -0
  434. package/engine-examples/mines/math/mines-9-3.tsv +2 -0
  435. package/engine-examples/mines/math/mines-9-4.tsv +2 -0
  436. package/engine-examples/mines/math/mines-9-5.tsv +2 -0
  437. package/engine-examples/mines/math/mines-9-6.tsv +2 -0
  438. package/engine-examples/mines/math/mines-9-7.tsv +2 -0
  439. package/engine-examples/mines/math/mines-9-8.tsv +2 -0
  440. package/engine-examples/mines/math/mines-9-9.tsv +2 -0
  441. package/engine-examples/plinko/AGENTS.md +3 -0
  442. package/engine-examples/plinko/README.md +9 -0
  443. package/engine-examples/plinko/bundle/assets.json +1 -0
  444. package/engine-examples/plinko/bundle/game.css +5 -0
  445. package/engine-examples/plinko/bundle/game.js +92 -0
  446. package/engine-examples/plinko/bundle/hbtest.json +4 -0
  447. package/engine-examples/plinko/bundle/index.html +16 -0
  448. package/engine-examples/plinko/bundle/model.json +1 -0
  449. package/engine-examples/plinko/math/gen.mjs +40 -0
  450. package/engine-examples/plinko/math/manifest.json +1 -0
  451. package/engine-examples/plinko/math/plinko-12-high.tsv +13 -0
  452. package/engine-examples/plinko/math/plinko-12-low.tsv +13 -0
  453. package/engine-examples/plinko/math/plinko-12-medium.tsv +13 -0
  454. package/engine-examples/plinko/math/plinko-16-high.tsv +17 -0
  455. package/engine-examples/plinko/math/plinko-16-low.tsv +17 -0
  456. package/engine-examples/plinko/math/plinko-16-medium.tsv +17 -0
  457. package/engine-examples/plinko/math/plinko-8-high.tsv +9 -0
  458. package/engine-examples/plinko/math/plinko-8-low.tsv +9 -0
  459. package/engine-examples/plinko/math/plinko-8-medium.tsv +9 -0
  460. package/engine-examples/plinko/math-kit/pack.mjs +165 -0
  461. package/engine-examples/plinko/math-kit/package.json +8 -0
  462. package/engine-examples/plinko/math-kit/slot.mjs +94 -0
  463. package/engine-examples/plinko/math-kit/tumble.mjs +94 -0
  464. package/engine-examples/slot/AGENTS.md +3 -0
  465. package/engine-examples/slot/README.md +26 -0
  466. package/engine-examples/slot/bundle/assets.json +1 -0
  467. package/engine-examples/slot/bundle/game.css +19 -0
  468. package/engine-examples/slot/bundle/game.js +42 -0
  469. package/engine-examples/slot/bundle/hbtest.json +4 -0
  470. package/engine-examples/slot/bundle/index.html +23 -0
  471. package/engine-examples/slot/math/gen.mjs +79 -0
  472. package/engine-examples/slot/math/manifest.json +1 -0
  473. package/engine-examples/slot/math/slot.tsv +110592 -0
  474. package/engine-examples/tower/AGENTS.md +3 -0
  475. package/engine-examples/tower/README.md +21 -0
  476. package/engine-examples/tower/bundle/assets.json +1 -0
  477. package/engine-examples/tower/bundle/game.css +8 -0
  478. package/engine-examples/tower/bundle/game.js +37 -0
  479. package/engine-examples/tower/bundle/hbtest.json +5 -0
  480. package/engine-examples/tower/bundle/index.html +16 -0
  481. package/engine-examples/tower/math/floor.tsv +4 -0
  482. package/engine-examples/tower/math/manifest.json +61 -0
  483. package/engine-examples/tumble/AGENTS.md +3 -0
  484. package/engine-examples/tumble/README.md +18 -0
  485. package/engine-examples/tumble/bundle/assets.json +1 -0
  486. package/engine-examples/tumble/bundle/game.css +13 -0
  487. package/engine-examples/tumble/bundle/game.js +119 -0
  488. package/engine-examples/tumble/bundle/hbtest.json +4 -0
  489. package/engine-examples/tumble/bundle/i18n.json +122 -0
  490. package/engine-examples/tumble/bundle/index.html +20 -0
  491. package/engine-examples/tumble/bundle/model.json +1 -0
  492. package/engine-examples/tumble/math/gen.mjs +38 -0
  493. package/engine-examples/tumble/math-kit/pack.mjs +165 -0
  494. package/engine-examples/tumble/math-kit/package.json +8 -0
  495. package/engine-examples/tumble/math-kit/slot.mjs +94 -0
  496. package/engine-examples/tumble/math-kit/tumble.mjs +94 -0
  497. package/engine-examples/wheel/README.md +14 -0
  498. package/engine-examples/wheel/bundle/assets.json +1 -0
  499. package/engine-examples/wheel/bundle/game.css +7 -0
  500. package/engine-examples/wheel/bundle/game.js +51 -0
  501. package/engine-examples/wheel/bundle/hbtest.json +4 -0
  502. package/engine-examples/wheel/bundle/index.html +19 -0
  503. package/engine-examples/wheel/math/high.tsv +24 -0
  504. package/engine-examples/wheel/math/low.tsv +24 -0
  505. package/engine-examples/wheel/math/manifest.json +48 -0
  506. package/engine-examples/wheel/math/medium.tsv +24 -0
  507. package/engine-math/pack.mjs +165 -0
  508. package/engine-math/package.json +1 -0
  509. package/engine-math/slot.mjs +94 -0
  510. package/engine-math/tumble.mjs +94 -0
  511. package/engine-mcp/package.json +1 -0
  512. package/engine-mcp/server.mjs +151 -0
  513. package/engine-sdk/GUIDE.md +855 -0
  514. package/engine-sdk/assets.js +92 -0
  515. package/engine-sdk/autoplay.js +82 -0
  516. package/engine-sdk/bet-field.js +391 -0
  517. package/engine-sdk/kit/fiat-coins.js +51 -0
  518. package/engine-sdk/kit/numeric-input.js +116 -0
  519. package/engine-sdk/kit/wallet-format.js +423 -0
  520. package/engine-sdk/marks.js +22 -0
  521. package/engine-sdk/messages.js +289 -0
  522. package/engine-sdk/money.js +89 -0
  523. package/engine-sdk/package.json +1 -0
  524. package/engine-sdk/protocol.js +263 -0
  525. package/engine-sdk/sdk.d.ts +138 -0
  526. package/engine-sdk/sdk.js +330 -0
  527. package/engine-sdk/stake.js +68 -0
  528. package/engine-sdk/ui.d.ts +36 -0
  529. package/engine-sdk/ui.js +340 -0
  530. package/engine-shell/core/faults.js +127 -0
  531. package/engine-shell/core/rgs.js +193 -0
  532. package/engine-shell/core/router.js +917 -0
  533. package/engine-shell/host/boot.js +408 -0
  534. package/engine-shell/host/engine-v1.js +438 -0
  535. package/engine-shell/host/fair.js +285 -0
  536. package/engine-shell/host/fonts/SOURCE.json +11 -0
  537. package/engine-shell/host/fonts/figtree-400.woff2 +0 -0
  538. package/engine-shell/host/fonts/figtree-500.woff2 +0 -0
  539. package/engine-shell/host/fonts/figtree-600.woff2 +0 -0
  540. package/engine-shell/host/fonts/red-hat-display-700.woff2 +0 -0
  541. package/engine-shell/host/index.html +230 -0
  542. package/engine-shell/host/page.js +809 -0
  543. package/engine-shell/host/sandbox-panel.js +681 -0
  544. package/engine-shell/host/sandbox-scenarios.js +554 -0
  545. package/engine-shell/host/site-bridge.js +87 -0
  546. package/engine-shell/host/strings.js +916 -0
  547. package/engine-shell/package.json +1 -0
  548. package/engine-shell/verifiers/badcat/coin.tsv +2 -0
  549. package/engine-shell/verifiers/badcat/dice-2.tsv +11 -0
  550. package/engine-shell/verifiers/badcat/dice-5.tsv +26 -0
  551. package/engine-shell/verifiers/badcat/dice-9.tsv +46 -0
  552. package/engine-shell/verifiers/badcat/manifest.json +3344 -0
  553. package/engine-shell/verifiers/badcat/rps.tsv +3 -0
  554. package/engine-shell/verifiers/badcat/triple.tsv +66 -0
  555. package/engine-shell/verifiers/engine-verify.js +358 -0
  556. package/engine-shell/verifiers/package.json +5 -0
  557. package/engine-shell/verifiers/parity/manifest.json +27 -0
  558. package/engine-shell/verifiers/parity/wide.tsv +5 -0
  559. package/engine-shell/verifiers/parity-vectors.json +244 -0
  560. package/engine-shell/verifiers/vectors.json +100 -0
  561. package/package.json +10 -0
@@ -0,0 +1,408 @@
1
+ /**
2
+ * The shell page: the frame the site launches for an engine game.
3
+ *
4
+ * launch URL <games host>[/housegames]/engine/play/<slug>/?otp=…&currency=…
5
+ * (from House Studio's sandbox, play.housestudio.io: &ticket=… too)
6
+ * 1. the one-time code is taken off the address bar and exchanged at
7
+ * <prefix>/direct/session for a short player token (the House
8
+ * Originals' launch, unchanged);
9
+ * 2. the engine session is minted with it and kept in memory only;
10
+ * 3. the game's bundle is framed sandboxed (no network, no storage, no
11
+ * session) and the router connects the two;
12
+ * 4. the site hears the same bridge messages Coin Flip sends.
13
+ *
14
+ * Every endpoint is this page's own origin, under the path the page itself
15
+ * was served from (`/housegames` on the site, nothing on games.housebets.*):
16
+ * nothing in the query can point the shell anywhere else (the PR #211 rule).
17
+ */
18
+ import { createRgs } from '../core/rgs.js';
19
+ import { createFaults } from '../core/faults.js';
20
+ import { createRouter } from '../core/router.js';
21
+ import { createSiteBridge } from './site-bridge.js';
22
+ import { wordsFor } from './strings.js';
23
+ import { moneyFor } from '../../engine-sdk/money.js';
24
+
25
+ export const API = () => {
26
+ const at = location.pathname.indexOf('/engine/play/');
27
+ return location.origin + (at < 0 ? '/housegames' : location.pathname.slice(0, at));
28
+ };
29
+
30
+ export const shouldLoadSandboxPanel = (env) => env === 'sandbox';
31
+
32
+ async function post(path, body, headers = {}) {
33
+ const ctrl = new AbortController();
34
+ const timer = setTimeout(() => ctrl.abort(), 10_000);
35
+ try {
36
+ const res = await fetch(API() + path, {
37
+ method: 'POST',
38
+ headers: { 'Content-Type': 'application/json', ...headers },
39
+ body: JSON.stringify(body),
40
+ credentials: 'omit',
41
+ cache: 'no-store',
42
+ signal: ctrl.signal,
43
+ });
44
+ const json = await res.json().catch(() => null);
45
+ if (!res.ok || !json) throw new Error((json && json.error) || `HTTP ${res.status}`);
46
+ return json;
47
+ } finally {
48
+ clearTimeout(timer);
49
+ }
50
+ }
51
+
52
+ // The site that frames the shell. In production the shell is served from the
53
+ // site's own origin; anywhere else the framing page must be one of the sites
54
+ // below (or, for a shell running on this machine, a page on this machine).
55
+ // Never taken from the URL.
56
+ const SITES = ['https://housebets.com', 'https://www.housebets.com', 'https://beta.housebets.com',
57
+ 'https://housebets.dev', 'https://www.housebets.dev', 'https://arbitrium.housebets.dev']; // staging
58
+ function siteOrigin() {
59
+ let ref = null;
60
+ try { ref = document.referrer ? new URL(document.referrer).origin : null; } catch { ref = null; }
61
+ const local = /^(localhost|127\.0\.0\.1)$/.test(location.hostname) && ref && /^http:\/\/(localhost|127\.0\.0\.1):\d+$/.test(ref);
62
+ if (ref === location.origin || SITES.includes(ref) || local) return ref;
63
+ return location.origin;
64
+ }
65
+
66
+ // The platform's currency payload (the object our own games receive as
67
+ // currentlycurrency) says how the player sees amounts: in the coin, or in a
68
+ // fiat at the platform's rate. The game gets it as `init.wallet`, the shape
69
+ // the Originals' wallet-format.js reads, cut to the fields that shape uses (no
70
+ // picture URLs: the SDK carries our marks). Money stays the server's: the coin
71
+ // must be the session's, and a stake's precision never exceeds what the server
72
+ // takes for it. A payload that disagrees is dropped for the plain coin.
73
+ const CODE = /^[A-Z0-9]{2,10}$/;
74
+ const cleanCode = (v) => (typeof v === 'string' && CODE.test(v.trim().toUpperCase()) ? v.trim().toUpperCase() : null);
75
+ const whole = (v) => (Number.isSafeInteger(v) && v >= 0 ? v : null);
76
+ export function walletFrom(raw, currency) {
77
+ const plain = { symbol: currency.symbol, name: currency.symbol, isFiat: false, rate: 1, betPrecision: Math.min(8, currency.decimals) };
78
+ let p;
79
+ try { p = raw ? JSON.parse(raw) : null; } catch { return plain; }
80
+ if (!p || typeof p !== 'object' || Array.isArray(p)) return plain;
81
+ // In a fiat display the platform renames `symbol` to the fiat and keeps
82
+ // the coin in `name`; `code` is the coin's id, the session's.
83
+ const coin = cleanCode(p.name) || cleanCode(p.symbol);
84
+ if (coin !== String(currency.symbol).toUpperCase() || (p.code !== undefined && p.code !== currency.code)) return plain;
85
+ const own = whole(p.betPrecision) ?? whole(p.decimals) ?? whole(p.precision) ?? 8;
86
+ const rate = Number(p.rate);
87
+ const fiat = p.isFiat === true && Number.isFinite(rate) && rate > 0 ? cleanCode(p.fiat) || cleanCode(p.currentFiat) || cleanCode(p.symbol) : null;
88
+ return {
89
+ symbol: fiat || coin,
90
+ name: coin,
91
+ isFiat: !!fiat,
92
+ ...(fiat ? { currentFiat: fiat } : {}),
93
+ rate: fiat ? rate : 1,
94
+ betPrecision: Math.min(8, own, currency.decimals),
95
+ };
96
+ }
97
+
98
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
99
+ const NIL_UUID = '00000000-0000-0000-0000-000000000000';
100
+
101
+ /**
102
+ * What the site's `housebets-game-control` envelope asks, read as the
103
+ * Originals read it (limbo host-controls.js): a bare action is the bar's
104
+ * button — `provably-fair` opens the dialog on the player's seeds,
105
+ * `site-input` is the site page reporting a trusted press or key the player
106
+ * made on it, outside the game frame (the click gate shadows the window's
107
+ * activation for it, as for a press on the shell's own controls: SPEC §5),
108
+ * the rest goes to the game as a control; an envelope with any of the bet history's
109
+ * round fields (gameType, roundId, requestId) is the history asking for one
110
+ * round, and only a whole one counts — this game's catalog slug, a round id
111
+ * that is a UUID and not nil, a request id that is a UUID — because a
112
+ * half-filled envelope opened on the seeds would show the player a screen
113
+ * they could take for the verification they asked for. `provably-fair-cancel`
114
+ * names the request to abandon. Returns {fairness: roundId|null, requestId?},
115
+ * {cancel: requestId}, {input: true}, {control: action}, or null for nothing.
116
+ */
117
+ export function siteCommand(d, gameId) {
118
+ if (!d || typeof d !== 'object' || typeof d.action !== 'string') return null;
119
+ const historical = 'gameType' in d || 'roundId' in d || 'requestId' in d;
120
+ const requestId = String(d.requestId || '').toLowerCase();
121
+ if (d.action === 'provably-fair-cancel') {
122
+ return historical && d.gameType === gameId && UUID.test(requestId) ? { cancel: requestId } : null;
123
+ }
124
+ if (!historical) {
125
+ if (d.action === 'provably-fair') return { fairness: null };
126
+ if (d.action === 'site-input') return { input: true };
127
+ return { control: d.action };
128
+ }
129
+ if (d.action !== 'provably-fair' || d.gameType !== gameId) return null;
130
+ const roundId = String(d.roundId || '').toLowerCase();
131
+ if (!UUID.test(roundId) || roundId === NIL_UUID || !UUID.test(requestId)) return null;
132
+ return { fairness: roundId, requestId };
133
+ }
134
+
135
+ /**
136
+ * The dialog's openers. The site's bar, the bet history and the shell's own
137
+ * button are the player's gesture: the dialog opens afresh, on the seeds or
138
+ * on the history's round. The game's `fairness.open` is untrusted code
139
+ * asking: the dialog is told (`fromGame`) and gives up nothing the player
140
+ * has typed or started in it.
141
+ */
142
+ export function fairnessOpeners(ui, rgs, ctx) {
143
+ const open = (roundId, fromGame, requestId) => ui.fairness(rgs, roundId, { ...ctx, fromGame, requestId: requestId || null });
144
+ return {
145
+ player: (roundId = null, requestId = null) => open(roundId, false, requestId),
146
+ game: (roundId) => open(roundId, true, null),
147
+ };
148
+ }
149
+
150
+ /**
151
+ * The site's document, when the shell may listen to it: the shell is served
152
+ * from the site's own origin in production (www.housebets.com/housegames/
153
+ * engine/play/…), so it is a same-origin frame of the site page, and a
154
+ * browser hands a press anywhere in that page — the chat, the header — to the
155
+ * shell's window as activation too (the HTML activation notification reaches
156
+ * every same-origin frame of the clicked page). The shell therefore watches
157
+ * the site's presses and focus moves as it watches its own. A shell on an
158
+ * origin of its own (a stand, the games host of ARCHITECTURE §7) gets no such
159
+ * activation and needs no such listener: the parent refuses its document, and
160
+ * that refusal is the proof. Null when there is no site around the shell, or
161
+ * when it is another origin.
162
+ */
163
+ export function siteDocument(win, parentOrigin) {
164
+ if (!win || win.parent === win || parentOrigin !== win.location.origin) return null;
165
+ try {
166
+ const doc = win.parent.document;
167
+ return doc && typeof doc.addEventListener === 'function' ? doc : null;
168
+ } catch {
169
+ return null; // cross-origin after all: its activation never reaches this window
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Where the page asks for its game's descriptor. A launch from House Studio's
175
+ * sandbox (play.housestudio.io, ADR 0005) carries a ticket, without which the
176
+ * studio names no version that is not live yet: it goes with the question. The
177
+ * casino's launch carries none and asks as it always has.
178
+ */
179
+ export function descriptorURL(ticket) {
180
+ return ticket ? `./descriptor.json?ticket=${encodeURIComponent(ticket)}` : './descriptor.json';
181
+ }
182
+
183
+ export async function boot({ ui }) {
184
+ const params = new URLSearchParams(location.search);
185
+ const otp = params.get('otp');
186
+ const ticket = params.get('ticket');
187
+ const currency = Number(params.get('currency'));
188
+ const payload = params.get('currencyPayload');
189
+ // The player's language, as the site names it on the launch (our own
190
+ // games read the same `lang`), else the browser's. The page's own words
191
+ // (the Provably Fair dialog) are in it from here on.
192
+ const locale = (params.get('lang') || params.get('locale') || navigator.language || 'en').slice(0, 12);
193
+ ui.locale(locale);
194
+ // The code is single-use; it leaves the address bar before anything else.
195
+ history.replaceState(null, '', location.pathname);
196
+ const slug = location.pathname.split('/').filter(Boolean).at(-1);
197
+ if (!otp || !Number.isSafeInteger(currency) || currency <= 0 || !/^[a-z0-9][a-z0-9-]{1,23}$/.test(slug)) {
198
+ return ui.fail('This game was opened without a launch. Open it from the casino.');
199
+ }
200
+
201
+ let descriptor, token, session;
202
+ try {
203
+ // Where the game's bundle lives and how the site names the game. In
204
+ // production this comes with the game's live version (in the sandbox,
205
+ // the newest that passed, for this launch's ticket); without one the
206
+ // studio says why (not live, or no front end kept for it).
207
+ const res = await fetch(descriptorURL(ticket), { cache: 'no-store' });
208
+ descriptor = await res.json().catch(() => null);
209
+ if (!res.ok || !descriptor || !descriptor.bundleUrl) {
210
+ return ui.fail((descriptor && descriptor.error) || 'This game is not available right now. Please try again later.');
211
+ }
212
+ ({ token } = await post('/direct/session', { otp }));
213
+ // No origin binding: the session id never leaves this page's memory
214
+ // (browsers send no Origin on same-origin GETs, which would fail it).
215
+ session = await post('/engine/v1/sessions', { game: slug, currency }, { 'X-Token': token });
216
+ } catch (e) {
217
+ return ui.fail('The game could not start. Please reopen it.', e);
218
+ }
219
+ token = null;
220
+
221
+ const faults = createFaults(fetch);
222
+ const rgs = createRgs({ base: API() + '/engine/v1', sessionId: session.sessionId, fetch: faults.fetch });
223
+ const auth = await rgs.authenticate(locale);
224
+ if (!auth.ok) return ui.fail('The game could not start. Please reopen it.', auth.error);
225
+ // The front end the studio named and the maths the round server plays must be
226
+ // one version: for the seconds a new version takes to reach both, the game waits.
227
+ if (typeof descriptor.mathHash === 'string' && descriptor.mathHash && auth.body.mathHash !== descriptor.mathHash) {
228
+ return ui.fail('This game is being updated. Please reopen it in a moment.');
229
+ }
230
+ // One launch, counted on the engine with the game's errors and abandoned rounds
231
+ // (the router sends those): the studio's Reports show the counts per day.
232
+ rgs.telemetry('boot');
233
+ // The studio dashboard's sandbox launcher can force the first round to a
234
+ // row of the table (?force=12, or 3,0,0 for a ladder's steps) to test a
235
+ // big win or a feature on demand. The server allows it only in the sandbox.
236
+ const force = params.get('force');
237
+ if (force && auth.body.env === 'sandbox' && /^\d{1,7}(,\d{1,7}){0,63}$/.test(force)) {
238
+ const f = await rgs.force(force.split(',').map(Number));
239
+ if (!f.ok) console.warn('force refused', f.error);
240
+ }
241
+ // How amounts are shown (the player's fiat display, as the site shows the
242
+ // header); money itself is in the wallet currency the server confirmed.
243
+ const init = { ...auth.body, wallet: walletFrom(payload, auth.body.currency), locale };
244
+ // How money is shown, the same as the site's header: the autoplay
245
+ // confirmation shows the stake and the totals in the player's coin, and
246
+ // the bet it is for by the labels the maths names (the id when it names none).
247
+ const money = moneyFor(init.wallet);
248
+ const betLine = (modeId, betId) => {
249
+ const modes = Array.isArray(init.modes) ? init.modes : [];
250
+ const label = (x, id) => (x && typeof x.label === 'string' && x.label ? x.label : id);
251
+ const mode = modes.find((m) => m && m.id === modeId);
252
+ const bet = label(mode && Array.isArray(mode.bets) ? mode.bets.find((b) => b && b.id === betId) : null, betId);
253
+ return modes.length > 1 ? `${label(mode, modeId)} · ${bet}` : bet;
254
+ };
255
+ // The page reads the player's way: right to left for Arabic, Hebrew,
256
+ // Persian, Urdu; the game frame inherits it.
257
+ document.documentElement.lang = locale;
258
+ document.documentElement.dir = /^(ar|he|fa|ur)\b/i.test(locale) ? 'rtl' : 'ltr';
259
+
260
+ const parentOrigin = window.parent !== window ? siteOrigin() : null;
261
+ const bridge = createSiteBridge({ parent: window.parent, parentOrigin, gameId: descriptor.catalogSlug, currencyId: currency });
262
+ const frame = ui.frame(descriptor.bundleUrl);
263
+ // The Provably Fair dialog, the shell's own: on the player's seeds (the
264
+ // site's bar, the shell's button) or on a round (the game's fairness.open,
265
+ // the bet history's envelope). It verifies against the maths the shell is
266
+ // running unless the round names another version, shows money as the
267
+ // wallet shows it, and holds the rotation while a round is open; the
268
+ // bets go on behind it, so it is told when a round starts and ends.
269
+ const fairness = fairnessOpeners(ui, rgs, { game: slug, mathHash: init.mathHash, wallet: init.wallet, roundOpen: () => router.roundOpen() });
270
+ // The click gate reads this window's user activation (a click inside the
271
+ // game frame reaches it — and, with the shell on the site's origin, so does
272
+ // a click anywhere in the site page: those are shadowed below) and the
273
+ // document's focus (only a click in the frame moves it there: router.js
274
+ // browserFocusInGame, its default; a focus the game moved there by script
275
+ // is told apart below); its refusals, and the autoplay control, speak the
276
+ // player's language.
277
+ const router = createRouter({
278
+ self: window,
279
+ gameWindow: () => frame.contentWindow,
280
+ gameOrigin: new URL(descriptor.bundleUrl).origin,
281
+ rgs,
282
+ init,
283
+ words: wordsFor(locale),
284
+ site: {
285
+ // The shell's own autoplay control over the game: rounds left and
286
+ // Stop, in this document, where the game cannot cover or press it.
287
+ autoplay: ui.autoplay,
288
+ // The autoplay confirmation: the router hands the capped terms in
289
+ // wallet units; the shell shows them in the player's coin, as the
290
+ // header does, and starts nothing until the player presses Start.
291
+ autoplayConfirm: (t) => {
292
+ const m = (n) => `${money.format(n)} ${money.unit}`;
293
+ ui.autoplayConfirm({ bet: betLine(t.mode, t.bet), rounds: t.rounds, stake: m(t.stake), total: m(t.total), stopProfit: t.stopOnProfit ? m(t.stopOnProfit) : null, stopLoss: m(t.stopOnLoss) });
294
+ },
295
+ // The router took the confirmation down (its deadline, the reality
296
+ // check, the session over): the dialog closes with the game refused.
297
+ autoplayDismiss: ui.autoplayDismiss,
298
+ roundStart: (r) => {
299
+ bridge.roundStart(r);
300
+ ui.fairnessRoundChanged(false);
301
+ },
302
+ roundEnd: (r, round) => {
303
+ bridge.roundEnd(r);
304
+ ui.round({ ...r, round });
305
+ ui.fairnessRoundChanged(true);
306
+ },
307
+ ready: () => {
308
+ ui.ready();
309
+ bridge.ready();
310
+ },
311
+ loading: ui.loading,
312
+ fairness: fairness.game,
313
+ error: (message) => console.warn('[game]', message),
314
+ ended: () => ui.fail('This game session has ended. Please reopen the game.'),
315
+ },
316
+ });
317
+ // The game asks for its config (hello) on every page it loads; the router
318
+ // answers. Nothing waits on the frame's load event, which a heavy page
319
+ // fires late. The site's bar: Provably Fair is the shell's own dialog on
320
+ // the player's seeds, the same for every game as for every House
321
+ // Original, open before the first round too (the hash is what the player
322
+ // checks before playing); the bet history's envelope opens it on that
323
+ // round (siteCommand); the site's report of the player's own press or key
324
+ // on its page (`site-input`) shadows this window's activation exactly as
325
+ // a press on the shell's controls does — the shell shares the site's
326
+ // origin, so that press armed this window too, and a bundle that stole
327
+ // the focus back by script must get no bet out of it; the rest is the
328
+ // game's.
329
+ bridge.listen((d) => {
330
+ const cmd = siteCommand(d, descriptor.catalogSlug);
331
+ if (!cmd) return;
332
+ if (cmd.cancel) ui.fairnessCancel(cmd.cancel);
333
+ else if (cmd.input) router.shellInput();
334
+ else if (cmd.control) router.tell('control', { action: cmd.control });
335
+ else fairness.player(cmd.fairness, cmd.requestId);
336
+ });
337
+ // No site around the shell (opened directly, or a stand): the shell
338
+ // shows the button the site's bar would.
339
+ if (window.parent === window) ui.fairButton(() => fairness.player());
340
+ // The player's Stop on the shell's autoplay control ends the session at once.
341
+ ui.autoplayStop(() => router.autoplayStop());
342
+ // The confirmation's Start/Cancel: a real click on the shell's own button
343
+ // starts the session; anything else refuses it. The game cannot reach these.
344
+ ui.onAutoplayConfirm((ok) => (ok ? router.confirmAutoplay() : router.cancelAutoplay()));
345
+ // A press or a key on the shell's own controls arms this window's activation
346
+ // as a game click does: the router is told, and takes no play on it meanwhile.
347
+ ui.onShellInput(() => router.shellInput());
348
+ // So does a press anywhere in the site's page when the shell is a
349
+ // same-origin frame of it (production): the chat, the header, a button of
350
+ // the site's. Its presses are shadowed the same way, so a chat click or a
351
+ // word typed there buys the game nothing (siteDocument says when the site's
352
+ // document may be listened to; a cross-origin site gives this window no
353
+ // activation and no document).
354
+ const siteDoc = siteDocument(window, parentOrigin);
355
+ if (siteDoc) ui.onShellInput(() => router.shellInput(), siteDoc);
356
+ // The focus must be the player's: a game can move it into its own frame by
357
+ // script, and the player's next keystrokes (meant for the chat) would then
358
+ // land in the game, granting it activation. The page watches the focus
359
+ // entering and leaving the frame (on this document's moves and the site's,
360
+ // the presses on either, and by reading document.activeElement besides);
361
+ // the router says whether the entry was the player's — the activation
362
+ // live, no shell or site press, no scripted focus and no exit from the
363
+ // frame within its lifetime (a game can take the focus back the moment
364
+ // the player tabs out of it, with that Tab's activation still live), or
365
+ // the player's own Tab key just before it (the page saw the key) — and a
366
+ // focus that was not is blurred straight back out of the frame and buys no
367
+ // play until the player focuses the frame themselves.
368
+ ui.watchGameFocus((viaTab, sinceExitMs) => router.frameFocused(viaTab, sinceExitMs), siteDoc ? [document, siteDoc] : [document]);
369
+ // A round left open as the page goes is counted as abandoned (pagehide also
370
+ // fires on a close, which beforeunload may not on a phone), and an autoplay
371
+ // session ends with the page — as it does when the page is merely hidden
372
+ // (another tab, the phone locked): a run is for a player who is looking.
373
+ window.addEventListener('pagehide', router.unload);
374
+ document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') router.hidden(); });
375
+ if (shouldLoadSandboxPanel(auth.body.env)) {
376
+ import('./sandbox-panel.js').then(({ createSandboxPanel }) => createSandboxPanel({
377
+ rgs,
378
+ router,
379
+ faults,
380
+ api: API(),
381
+ frame,
382
+ slug,
383
+ mathHash: init.mathHash,
384
+ money,
385
+ refresh: async () => {
386
+ const refreshed = await rgs.authenticate(locale);
387
+ if (refreshed.ok) {
388
+ router.refreshInit({ ...refreshed.body, wallet: walletFrom(payload, refreshed.body.currency), locale });
389
+ }
390
+ return refreshed;
391
+ },
392
+ })).catch((error) => console.warn('[sandbox panel]', error));
393
+ }
394
+ // The reality check the server asks for: every N seconds of play the
395
+ // game is paused and the player told how long it has been.
396
+ const every = init.rg && init.rg.realityCheckSec > 0 ? init.rg.realityCheckSec * 1000 : 0;
397
+ if (every) {
398
+ const started = Date.now();
399
+ const check = async () => {
400
+ router.tell('pause', { reason: 'reality-check' });
401
+ await ui.realityCheck(Math.round((Date.now() - started) / 60000));
402
+ router.tell('resume', {});
403
+ setTimeout(check, every);
404
+ };
405
+ setTimeout(check, every);
406
+ }
407
+ ui.started({ game: init.game, mathHash: init.mathHash, env: init.env, currency: init.currency });
408
+ }