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,438 @@
1
+ /**
2
+ * `engine-v1` in the browser: the pack hash of SPEC.md §1.3 and the derivation
3
+ * of §2, as the Verify tab of the shell's Provably Fair dialog (page.js) runs
4
+ * them for a player. Web Crypto for HMAC-SHA256 and SHA-256, BigInt for the
5
+ * 64-bit draws, nothing imported — the page's policy is `script-src 'self'`
6
+ * and a verifier that pulls code from elsewhere is not one the player can
7
+ * trust.
8
+ *
9
+ * The reference is the backend's CommonJS verifier, kept byte for byte at
10
+ * ../verifiers/engine-verify.js (housegames-backend
11
+ * internal/engine/verifiers/engine-verify.js, whose sha256 engine-v1.test.js
12
+ * pins). Node's `crypto` is synchronous and the browser's is not, and the
13
+ * reference reads files where this reads the public /math document, so this
14
+ * is a port, not a copy: engine-v1.test.js holds it to the reference on both
15
+ * golden packs (BAD CAT, and the parity pack whose rows carry every escape
16
+ * the canonical form has a rule for), on random seeds, nonces, modes and
17
+ * bets, on a byte order mark, on a document that is not UTF-8 and on every
18
+ * pack the reference refuses, so the two cannot drift apart unnoticed.
19
+ */
20
+
21
+ const subtle = globalThis.crypto.subtle;
22
+ const utf8 = new TextEncoder();
23
+
24
+ // ---- bytes → text, as the Go reference reads them ----------------------------
25
+ // A pack is UTF-8. The /math document's manifest is the studio's submitted
26
+ // bytes as they are — hashed, served and kept on record as they are — so a
27
+ // manifest that is not well-formed UTF-8 is refused at upload
28
+ // (mathpack.Load), by the reference (readManifestText) and by the retention
29
+ // that keeps the document, never admitted as U+FFFD into a document no text
30
+ // column could hold. A table's bytes are canonicalised on the way in (one
31
+ // U+FFFD per byte that is not well-formed, as Go decodes), so the rows the
32
+ // document carries are UTF-8 already. The document is therefore UTF-8 end to
33
+ // end, and one that is not is not the server's: it is refused here as the
34
+ // reference refuses the manifest. `fatal` refuses; `ignoreBOM` keeps a byte
35
+ // order mark as the character it is to Go and to the reference, wherever it
36
+ // stands — a decoder that stripped one at the start would hash a manifest
37
+ // the server never hashed.
38
+ const utf8Decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
39
+
40
+ /** The text of `bytes` (Uint8Array or ArrayBuffer); an Error when they are not well-formed UTF-8. */
41
+ export function textFromBytes(bytes) {
42
+ try {
43
+ return utf8Decoder.decode(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes));
44
+ } catch (e) {
45
+ throw new Error('the document is not UTF-8');
46
+ }
47
+ }
48
+
49
+ // ---- canonical JSON: keys sorted, no whitespace, numbers as written ---------
50
+ // A tiny parser that keeps each number's source text, so `2.50` stays `2.50`
51
+ // exactly as the Go reference does. Strings are escaped the way Go's
52
+ // encoding/json escapes them: `"` `\` and the short escapes \b \f \n \r \t,
53
+ // every other control character as \u00xx, `<` `>` `&` U+2028 U+2029 as
54
+ // \uXXXX, everything else as itself. An escaped surrogate pair is one code
55
+ // point; a lone surrogate is U+FFFD. Keys sort by code point.
56
+
57
+ export function canonicalJSON(text) {
58
+ let i = 0;
59
+ const ws = () => { while (i < text.length && ' \t\r\n'.includes(text[i])) i++; };
60
+ const fail = (m) => { throw new Error(`canonical json: ${m} at ${i}`); };
61
+ function value() {
62
+ ws();
63
+ const c = text[i];
64
+ if (c === '{') return object();
65
+ if (c === '[') return array();
66
+ if (c === '"') return string();
67
+ if (c === 't' && text.startsWith('true', i)) { i += 4; return 'true'; }
68
+ if (c === 'f' && text.startsWith('false', i)) { i += 5; return 'false'; }
69
+ if (c === 'n' && text.startsWith('null', i)) { i += 4; return 'null'; }
70
+ const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text.slice(i));
71
+ if (!m) fail('unexpected token');
72
+ i += m[0].length;
73
+ return m[0];
74
+ }
75
+ function object() {
76
+ i++; ws();
77
+ const entries = [];
78
+ if (text[i] === '}') { i++; return '{}'; }
79
+ for (;;) {
80
+ ws();
81
+ if (text[i] !== '"') fail('expected key');
82
+ const k = rawString();
83
+ ws();
84
+ if (text[i] !== ':') fail('expected :');
85
+ i++;
86
+ entries.push([k, value()]);
87
+ ws();
88
+ if (text[i] === ',') { i++; continue; }
89
+ if (text[i] === '}') { i++; break; }
90
+ fail('expected , or }');
91
+ }
92
+ if (new Set(entries.map(([k]) => k)).size !== entries.length) fail('duplicate key');
93
+ entries.sort((a, b) => compareCodePoints(a[0], b[0]));
94
+ return '{' + entries.map(([k, v]) => escapeString(k) + ':' + v).join(',') + '}';
95
+ }
96
+ function array() {
97
+ i++; ws();
98
+ const items = [];
99
+ if (text[i] === ']') { i++; return '[]'; }
100
+ for (;;) {
101
+ items.push(value());
102
+ ws();
103
+ if (text[i] === ',') { i++; continue; }
104
+ if (text[i] === ']') { i++; break; }
105
+ fail('expected , or ]');
106
+ }
107
+ return '[' + items.join(',') + ']';
108
+ }
109
+ function rawString() {
110
+ // returns the decoded string
111
+ i++;
112
+ let out = '';
113
+ for (;;) {
114
+ if (i >= text.length) fail('unterminated string');
115
+ const c = text[i++];
116
+ if (c === '"') return out;
117
+ // A raw control character inside a string is not JSON; Go's scanner
118
+ // refuses it, so a verifier that escaped it would hash a pack the
119
+ // gate never takes.
120
+ if (c.charCodeAt(0) < 0x20) fail('control character in string');
121
+ if (c !== '\\') { out += c; continue; }
122
+ const e = text[i++];
123
+ if (e === 'u') { out += String.fromCodePoint(unicodeEscape()); continue; }
124
+ const short = { '"': '"', '\\': '\\', '/': '/', b: '\b', f: '\f', n: '\n', r: '\r', t: '\t' }[e];
125
+ if (short === undefined) fail('bad escape');
126
+ out += short;
127
+ }
128
+ }
129
+ // unicodeEscape reads the XXXX of a \uXXXX (i is just past the u). A high
130
+ // surrogate followed by an escaped low surrogate is one code point. A
131
+ // surrogate on its own is U+FFFD, as Go's decoder has it, and whatever
132
+ // escape follows a lone high surrogate is read on its own.
133
+ function unicodeEscape() {
134
+ const cp = hex4(i);
135
+ i += 4;
136
+ if (cp < 0xd800 || cp > 0xdfff) return cp;
137
+ if (cp <= 0xdbff && text[i] === '\\' && text[i + 1] === 'u') {
138
+ const low = hex4(i + 2);
139
+ if (low >= 0xdc00 && low <= 0xdfff) {
140
+ i += 6;
141
+ return 0x10000 + ((cp - 0xd800) << 10) + (low - 0xdc00);
142
+ }
143
+ }
144
+ return 0xfffd;
145
+ }
146
+ function hex4(at) {
147
+ const h = text.slice(at, at + 4);
148
+ if (!/^[0-9a-fA-F]{4}$/.test(h)) fail('bad \\u escape');
149
+ return parseInt(h, 16);
150
+ }
151
+ function string() { return escapeString(rawString()); }
152
+ const out = value();
153
+ ws();
154
+ if (i !== text.length) fail('trailing data');
155
+ return out;
156
+ }
157
+
158
+ // compareCodePoints orders keys as the Go reference does: by code point,
159
+ // which is UTF-8 byte order. JavaScript's own `<` compares UTF-16 code
160
+ // units, which would put an emoji before U+E000..U+FFFF.
161
+ export function compareCodePoints(a, b) {
162
+ let i = 0, j = 0;
163
+ while (i < a.length && j < b.length) {
164
+ const x = a.codePointAt(i), y = b.codePointAt(j);
165
+ if (x !== y) return x - y;
166
+ i += x > 0xffff ? 2 : 1;
167
+ j += y > 0xffff ? 2 : 1;
168
+ }
169
+ return (a.length - i) - (b.length - j);
170
+ }
171
+
172
+ export function escapeString(s) {
173
+ let out = '"';
174
+ for (const ch of s) {
175
+ const cp = ch.codePointAt(0);
176
+ if (ch === '"') out += '\\"';
177
+ else if (ch === '\\') out += '\\\\';
178
+ else if (ch === '\b') out += '\\b';
179
+ else if (ch === '\f') out += '\\f';
180
+ else if (ch === '\n') out += '\\n';
181
+ else if (ch === '\r') out += '\\r';
182
+ else if (ch === '\t') out += '\\t';
183
+ else if (cp < 0x20 || ch === '<' || ch === '>' || ch === '&' || cp === 0x2028 || cp === 0x2029) {
184
+ out += '\\u' + cp.toString(16).padStart(4, '0');
185
+ } else out += ch;
186
+ }
187
+ return out + '"';
188
+ }
189
+
190
+ // ---- pack ------------------------------------------------------------------------
191
+
192
+ // A weight is what Go's strconv.ParseUint(s, 10, 64) takes: ASCII digits and
193
+ // nothing else. BigInt() alone would also take `0x10`, `+5` and ` 5`, and a
194
+ // verifier must not hash a table the gate refuses.
195
+ const WEIGHT = /^[0-9]+$/;
196
+
197
+ export function parseTable(name, text) {
198
+ const rows = [];
199
+ const lines = text.split('\n');
200
+ if (lines[lines.length - 1] === '') lines.pop();
201
+ for (const line of lines) {
202
+ if (line.startsWith('#') && rows.length === 0) continue;
203
+ const tab = line.indexOf('\t');
204
+ const field = tab < 0 ? line : line.slice(0, tab);
205
+ if (!WEIGHT.test(field)) throw new Error(`${name}: weight ${JSON.stringify(field)} is not a decimal integer`);
206
+ const w = BigInt(field);
207
+ if (w <= 0n) throw new Error(`${name}: weight must be > 0`);
208
+ let data = null;
209
+ if (tab >= 0) {
210
+ // The data column is one JSON value: never empty, never holding a
211
+ // second tab or a carriage return (a CRLF file is refused, as in Go).
212
+ const rest = line.slice(tab + 1);
213
+ if (rest === '' || /[\t\r]/.test(rest)) throw new Error(`${name}: malformed data column`);
214
+ data = canonicalJSON(rest);
215
+ }
216
+ rows.push({ weight: w, data });
217
+ }
218
+ if (rows.length < 2) throw new Error(`${name}: needs at least 2 rows`);
219
+ const cum = [];
220
+ let sum = 0n;
221
+ for (const r of rows) { sum += r.weight; cum.push(sum); }
222
+ if (sum > (1n << 63n)) throw new Error(`${name}: weights sum past 2^63`);
223
+ return { name, rows, cum, sum };
224
+ }
225
+
226
+ export function tableBytes(t) {
227
+ return t.rows.map((r) => r.weight.toString() + (r.data === null ? '' : '\t' + r.data) + '\n').join('');
228
+ }
229
+
230
+ /**
231
+ * The raw text of one member of the JSON object `text`, exactly as written:
232
+ * what the hash commits to is the manifest's own bytes, canonicalised, and a
233
+ * JSON.parse → JSON.stringify round trip would rewrite them.
234
+ */
235
+ export function rawMember(text, key) {
236
+ let i = 0;
237
+ const ws = () => { while (i < text.length && ' \t\r\n'.includes(text[i])) i++; };
238
+ const fail = (m) => { throw new Error(`json: ${m} at ${i}`); };
239
+ function skipString() {
240
+ i++;
241
+ for (;;) {
242
+ if (i >= text.length) fail('unterminated string');
243
+ const c = text[i++];
244
+ if (c === '"') return;
245
+ if (c === '\\') i++;
246
+ }
247
+ }
248
+ function skipValue() {
249
+ ws();
250
+ const c = text[i];
251
+ if (c === '"') return skipString();
252
+ if (c === '{' || c === '[') {
253
+ const close = c === '{' ? '}' : ']';
254
+ i++; ws();
255
+ if (text[i] === close) { i++; return; }
256
+ for (;;) {
257
+ if (c === '{') { ws(); if (text[i] !== '"') fail('expected key'); skipString(); ws(); if (text[i] !== ':') fail('expected :'); i++; }
258
+ skipValue();
259
+ ws();
260
+ if (text[i] === ',') { i++; continue; }
261
+ if (text[i] === close) { i++; return; }
262
+ fail('expected , or ' + close);
263
+ }
264
+ }
265
+ const m = /^(true|false|null|-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?)/.exec(text.slice(i));
266
+ if (!m) fail('unexpected token');
267
+ i += m[0].length;
268
+ }
269
+ ws();
270
+ if (text[i] !== '{') fail('expected an object');
271
+ i++; ws();
272
+ if (text[i] === '}') return null;
273
+ for (;;) {
274
+ ws();
275
+ if (text[i] !== '"') fail('expected key');
276
+ const start = i;
277
+ skipString();
278
+ const k = JSON.parse(text.slice(start, i));
279
+ ws();
280
+ if (text[i] !== ':') fail('expected :');
281
+ i++; ws();
282
+ const from = i;
283
+ skipValue();
284
+ if (k === key) return text.slice(from, i);
285
+ ws();
286
+ if (text[i] === ',') { i++; continue; }
287
+ if (text[i] === '}') return null;
288
+ fail('expected , or }');
289
+ }
290
+ }
291
+
292
+ export const FORMAT = 'house-engine-math/1';
293
+
294
+ /**
295
+ * A pack from the public document GET /engine/v1/math/<slug>?version=<hash>
296
+ * answers: `{format, hash, manifest, tables{name: tsv}, report}`. Takes the
297
+ * response bytes (or its text), never the parsed object, so the manifest's
298
+ * bytes are the server's own; the manifest is canonicalised here, so a
299
+ * document the reference refuses (a repeated key, a control character, bytes
300
+ * that are not UTF-8) is refused before anything is drawn from it.
301
+ */
302
+ export function packFromDocument(source) {
303
+ const text = typeof source === 'string' ? source : textFromBytes(source);
304
+ const doc = JSON.parse(text);
305
+ if (!doc || typeof doc !== 'object' || doc.format !== FORMAT) throw new Error(`not a ${FORMAT} document`);
306
+ const manifestText = rawMember(text, 'manifest');
307
+ if (!manifestText || !doc.manifest || !Array.isArray(doc.manifest.modes)) throw new Error('the document has no manifest');
308
+ if (!doc.tables || typeof doc.tables !== 'object') throw new Error('the document has no tables');
309
+ const manifestCanonical = canonicalJSON(manifestText);
310
+ const tables = {};
311
+ for (const m of doc.manifest.modes) {
312
+ const names = m.kind === 'table' ? [m.table] : (m.steps || []).map((s) => s.table);
313
+ for (const n of names) {
314
+ if (tables[n]) continue;
315
+ if (typeof doc.tables[n] !== 'string') throw new Error(`the document lacks table ${n}`);
316
+ tables[n] = parseTable(n, doc.tables[n]);
317
+ }
318
+ }
319
+ return { manifest: doc.manifest, manifestText, manifestCanonical, tables, hash: typeof doc.hash === 'string' ? doc.hash : '', report: doc.report };
320
+ }
321
+
322
+ /**
323
+ * Loads and verifies one public maths document for the shell. The Provably Fair
324
+ * dialog and the sandbox test panel share this owner so they ask for the same
325
+ * version and hash the exact bytes the round server served.
326
+ */
327
+ export async function loadPack(api, game, version, fetcher = globalThis.fetch) {
328
+ if (!/^[0-9a-f]{64}$/.test(version)) throw new Error('version');
329
+ const url = `${api}/engine/v1/math/${encodeURIComponent(game)}?version=${version}`;
330
+ const res = await fetcher(url, { credentials: 'omit' });
331
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
332
+ const pack = packFromDocument(new Uint8Array(await res.arrayBuffer()));
333
+ pack.recomputed = await mathHash(pack);
334
+ return pack;
335
+ }
336
+
337
+ export const hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
338
+
339
+ export async function sha256Hex(text) {
340
+ return hex(new Uint8Array(await subtle.digest('SHA-256', utf8.encode(text))));
341
+ }
342
+
343
+ export async function mathHash(pack) {
344
+ let s = 'house-engine-math/1\n';
345
+ s += 'manifest\n' + (pack.manifestCanonical || canonicalJSON(pack.manifestText)) + '\n';
346
+ for (const name of Object.keys(pack.tables).sort(compareCodePoints)) s += 'table ' + name + '\n' + tableBytes(pack.tables[name]);
347
+ return sha256Hex(s);
348
+ }
349
+
350
+ // ---- engine-v1 stream and draw ----------------------------------------------------
351
+
352
+ /**
353
+ * The stream of §2: block_i = HMAC_SHA256(key = serverSeed, msg =
354
+ * "clientSeed:nonce:i"), consumed 8 bytes at a time, big-endian. `blocks`
355
+ * keeps every block computed (message and digest) for anyone walking a draw
356
+ * by hand; the draw itself reads nothing from it.
357
+ */
358
+ export function stream(serverSeed, clientSeed, nonce) {
359
+ let key = null, block = 0, buf = new Uint8Array(0), off = 0;
360
+ const blocks = [];
361
+ const byte = async () => {
362
+ if (off >= buf.length) {
363
+ if (!key) key = await subtle.importKey('raw', utf8.encode(serverSeed), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
364
+ const message = `${clientSeed}:${nonce}:${block}`;
365
+ buf = new Uint8Array(await subtle.sign('HMAC', key, utf8.encode(message)));
366
+ blocks.push({ block, message, digest: hex(buf) });
367
+ block++; off = 0;
368
+ }
369
+ return buf[off++];
370
+ };
371
+ return {
372
+ async u64() { let v = 0n; for (let k = 0; k < 8; k++) v = (v << 8n) | BigInt(await byte()); return v; },
373
+ blocks,
374
+ };
375
+ }
376
+
377
+ export const TWO64 = 1n << 64n;
378
+
379
+ /** One row of `table` from the stream `s`, with the rejected count and, for the page, the arithmetic. */
380
+ export async function pick(table, s) {
381
+ const rem = TWO64 % table.sum;
382
+ const limit = TWO64 - rem;
383
+ let rejected = 0;
384
+ for (;;) {
385
+ const v = await s.u64();
386
+ if (v < limit) {
387
+ const r = v % table.sum;
388
+ let lo = 0, hi = table.cum.length - 1;
389
+ while (lo < hi) { const mid = (lo + hi) >> 1; if (table.cum[mid] > r) hi = mid; else lo = mid + 1; }
390
+ return { raw: v, rejected, index: lo, table: table.name, sum: table.sum, limit, r };
391
+ }
392
+ rejected++;
393
+ }
394
+ }
395
+
396
+ const inRanges = (rs, i) => rs.some(([lo, hi]) => i >= lo && i <= hi);
397
+
398
+ /**
399
+ * A round re-derived: the same draws, multiplier and ladder outcome the server
400
+ * computed for (serverSeed, clientSeed, nonce) under `pack`, SPEC §2. A ladder
401
+ * is drawn every step at the bet; a cash-out after k steps pays ladderBp[k-1],
402
+ * which the receipt's `multBp` names.
403
+ */
404
+ export async function play(pack, modeId, betId, serverSeed, clientSeed, nonce) {
405
+ const mode = pack.manifest.modes.find((m) => m.id === modeId);
406
+ if (!mode) throw new Error(`no mode ${modeId}`);
407
+ const bet = mode.bets.find((b) => b.id === betId);
408
+ if (!bet) throw new Error(`no bet ${betId}`);
409
+ const s = stream(serverSeed, clientSeed, nonce);
410
+ if (mode.kind === 'table') {
411
+ const d = await pick(pack.tables[mode.table], s);
412
+ const tier = bet.tiers.find((t) => inRanges(t.outcomes, d.index));
413
+ return { mode: modeId, bet: betId, kind: 'table', draws: [d], multBp: tier ? tier.multBp : 0, blocks: s.blocks };
414
+ }
415
+ const draws = []; let survived = 0, busted = false;
416
+ for (const step of mode.steps) {
417
+ const d = await pick(pack.tables[step.table], s);
418
+ draws.push(d);
419
+ if (!busted && inRanges(bet.survive, d.index)) survived++; else busted = true;
420
+ }
421
+ busted = survived < mode.steps.length;
422
+ const multBp = mode.autoAdvance && !busted ? mode.ladderBp[mode.steps.length - 1] : 0;
423
+ return { mode: modeId, bet: betId, kind: 'ladder', draws, multBp, survived, busted, ladderBp: mode.ladderBp, blocks: s.blocks };
424
+ }
425
+
426
+ /**
427
+ * What a stake pays at a multiplier, as the server computes it (housegames-backend
428
+ * internal/services/engine.go enginePayout → moneymath.TruncateProduct): both
429
+ * sides in whole 1e-8 units, the product truncated to a unit, never rounded up.
430
+ */
431
+ export function payoutOf(amount, multBp) {
432
+ const a = Number(amount), m = Number(multBp);
433
+ if (!(a > 0) || !(m > 0) || !Number.isFinite(a) || !Number.isSafeInteger(m)) return 0;
434
+ const units = BigInt(Math.round(a * 1e8));
435
+ const mult = BigInt(Math.round((m / 10000) * 1e8));
436
+ if (units >= (1n << 62n) || mult >= (1n << 62n)) return 0;
437
+ return Number((units * mult) / 100000000n) / 1e8;
438
+ }