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,208 @@
1
+ // The code analysis of a whole bundle, for the worker thread (analyse-worker)
2
+ // and for anything that wants it in-process (tests, a trace): every script
3
+ // and page parsed and scoped first, the files then wired to one another — a
4
+ // module's imports to the exports of the file they name, a classic script's
5
+ // globals to every other script's, a page's elements to the scripts it loads
6
+ // (and to the modules they import), every write to a property or an attribute
7
+ // to a pool the reads consult, every call to the function it reaches in
8
+ // whatever file — and each file judged with all of that in reach, so a value
9
+ // put into the page is followed to where it is written.
10
+ import { readFileSync } from 'node:fs';
11
+ import { analyseData, analyseHtml, analyseXml, judgeSource, prepareSource, resolveSpecifier, scriptKind, RULES } from './analyse.mjs';
12
+
13
+ // Every <script …> tag of a page, its attributes read for src and type (scriptKind).
14
+ const SCRIPT_TAG = /<script\b([^>]*)>/gi;
15
+ const SRC_ATTR = /\ssrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i;
16
+ const TYPE_ATTR = /\stype\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i;
17
+ // The scripts a page loads by src: [{ target, kind }], kind 'script' (a classic
18
+ // script: its top-level declarations are the page's globals), 'module', or
19
+ // null for a data block the browser never runs.
20
+ function scriptLoads(path, text) {
21
+ const out = [];
22
+ for (const m of text.matchAll(SCRIPT_TAG)) {
23
+ const attrs = m[1];
24
+ const src = SRC_ATTR.exec(attrs);
25
+ if (!src) continue;
26
+ const target = resolveSpecifier(path, (src[1] ?? src[2] ?? src[3] ?? '').trim());
27
+ if (target == null) continue;
28
+ const type = TYPE_ATTR.exec(attrs);
29
+ const kind = scriptKind(type ? (type[1] ?? type[2] ?? type[3] ?? '') : '');
30
+ if (kind === 'importmap') continue;
31
+ out.push({ target, kind });
32
+ }
33
+ return out;
34
+ }
35
+
36
+ /**
37
+ * `files`: [{ path, abs, kind }] with kind 'xml', 'html', 'data' or 'script'
38
+ * (scan.mjs decides). Returns one result per file: { path, findings, units,
39
+ * wasm, sourceMappingURL }. `hook(unit)` is called with each prepared unit
40
+ * before judging (a trace instruments the analysis there).
41
+ */
42
+ export function analyseBundle(files, { read = (abs) => readFileSync(abs, 'utf8'), hook = null } = {}) {
43
+ const out = [];
44
+ const units = []; // { path, unit, page, result, inline }
45
+ const pages = new Map(); // path → { path, elements, overflow, loads: [paths], units }
46
+ const modules = new Map(); // path → unit (a .js/.mjs file), for imports
47
+ const jsons = new Map(); // path → parsed JSON (or undefined), for JSON imports
48
+ const texts = new Map(); // path → text of a .json data file
49
+ const fail = (result, e) => result.findings.push({ level: 'fail', rule: 'analyser', line: 1, what: `the analyser stopped on this file: ${e.message}` });
50
+
51
+ // The files every page loads by a plain <script src> (no type, or a
52
+ // JavaScript MIME type) are classic scripts: parsed as the browser parses
53
+ // them (a module-only construct is a syntax error there, as in the
54
+ // browser), with their top-level declarations the page's globals, which
55
+ // every other script reads. A file loaded both ways is read as a classic
56
+ // script (the over-approximation: more is global, nothing less).
57
+ const classic = new Set();
58
+ const texts0 = new Map();
59
+ for (const { path, abs, kind } of files) {
60
+ if (kind !== 'html') continue;
61
+ let text;
62
+ try { text = read(abs); } catch { continue; }
63
+ texts0.set(abs, text);
64
+ for (const { target, kind: k } of scriptLoads(path, text)) if (k === 'script') classic.add(target);
65
+ }
66
+
67
+ for (const { path, abs, kind } of files) {
68
+ const result = { path, findings: [], units: [], wasm: [], sourceMappingURL: null };
69
+ out.push(result);
70
+ try {
71
+ const text = texts0.has(abs) ? texts0.get(abs) : read(abs);
72
+ if (kind === 'xml') {
73
+ // An SVG or XML document runs its scripts and handler attributes
74
+ // when framed or embedded, not when drawn: read as the browser
75
+ // renders it (prefixes dropped; a DTD subset refused), any
76
+ // <script> refused outright (a game's scripts are .js files).
77
+ result.findings.push(...analyseXml(text).findings);
78
+ } else if (kind === 'data') {
79
+ // A text file that is not code or markup (.json, .txt, .csv, a
80
+ // template of any extension): markup the game may put into the
81
+ // page is walked as code's strings are. A .json the analyser
82
+ // cannot parse is refused: the page would read it as JSON, and
83
+ // what the analyser cannot read the same way it does not pass.
84
+ const r = analyseData(text);
85
+ result.findings.push(...r.findings);
86
+ if (/\.json$/i.test(path) && !r.parsed) result.findings.push({ level: RULES['data-file'].level, rule: 'data-file', line: 1, what: 'not valid JSON' });
87
+ if (/\.json$/i.test(path) && r.parsed) texts.set(path, text);
88
+ } else if (kind === 'html') {
89
+ const page = analyseHtml(text);
90
+ result.findings.push(...page.findings);
91
+ const info = { path, elements: page.elements, overflow: page.overflow, loads: [], units: [] };
92
+ pages.set(path, info);
93
+ for (const script of page.scripts) {
94
+ const unit = prepareSource(script.code, { mode: script.mode, lineOffset: script.line - 1 });
95
+ unit.path = path;
96
+ units.push({ path, unit, page: info, result, inline: true });
97
+ info.units.push(unit);
98
+ }
99
+ // The scripts the page loads by src (a <script src> has no body for the walk to return).
100
+ for (const { target } of scriptLoads(path, text)) if (!info.loads.includes(target)) info.loads.push(target);
101
+ } else {
102
+ const unit = prepareSource(text, { mode: classic.has(path) ? 'script' : 'auto' });
103
+ unit.path = path;
104
+ units.push({ path, unit, page: null, result, inline: false });
105
+ modules.set(path, unit);
106
+ }
107
+ } catch (e) {
108
+ // A shape of syntax the analyser did not expect fails the file, never passes it.
109
+ fail(result, e);
110
+ }
111
+ }
112
+
113
+ // ---- wiring -----------------------------------------------------------------
114
+
115
+ // A JSON module: its parsed value, read once.
116
+ const jsonOf = (path) => {
117
+ if (!jsons.has(path)) { const t = texts.get(path); let v; try { v = t == null ? undefined : JSON.parse(t.charCodeAt(0) === 0xfeff ? t.slice(1) : t); } catch { v = undefined; } jsons.set(path, v); }
118
+ return jsons.get(path);
119
+ };
120
+ // What a module specifier names, from a file: the module (its analysis), a JSON file ({ json }), or null.
121
+ const moduleFrom = (fromPath) => (specifier) => {
122
+ const target = resolveSpecifier(fromPath, specifier);
123
+ if (target == null) return null;
124
+ const m = modules.get(target);
125
+ if (m) return m.ast ? { analysis: m.analysis } : null;
126
+ if (/\.json$/i.test(target)) { const v = jsonOf(target); return v === undefined ? null : { json: v }; }
127
+ return null;
128
+ };
129
+
130
+ // The pool: every classic script's root bindings and every module's globals
131
+ // (window.X = …) by name; every write by its key; every element the code
132
+ // writes as markup.
133
+ const pool = { globals: new Map(), writes: new Map(), written: [], writtenOverflow: false };
134
+ for (const { unit } of units) {
135
+ const a = unit.analysis;
136
+ if (!unit.ast) continue;
137
+ for (const [name, binding] of a.root.names) {
138
+ if (a.mode !== 'script' && !binding.global) continue;
139
+ if (!binding.alias && !binding.hidden) continue;
140
+ if (!pool.globals.has(name)) pool.globals.set(name, []);
141
+ pool.globals.get(name).push({ binding, scope: a.root });
142
+ }
143
+ for (const w of a.writes) {
144
+ if (!a.targetOf(w)) continue;
145
+ const key = w.key ?? '*';
146
+ if (!pool.writes.has(key)) pool.writes.set(key, []);
147
+ pool.writes.get(key).push(w);
148
+ }
149
+ if (a.writtenOverflow) pool.writtenOverflow = true;
150
+ else pool.written.push(...a.written);
151
+ }
152
+ if (pool.written.length > 100000) { pool.written = []; pool.writtenOverflow = true; }
153
+
154
+ // The pages a script runs in: its own for an inline script; those whose
155
+ // <script src> names it for a file; those of its importers for a module
156
+ // imported by another (to a fixpoint, since imports chain).
157
+ const pagesOf = new Map();
158
+ for (const u of units) pagesOf.set(u.unit, new Set(u.page ? [u.page] : []));
159
+ for (const info of pages.values()) for (const target of info.loads) { const m = modules.get(target); if (m) pagesOf.get(m)?.add(info); }
160
+ for (let changed = true, rounds = 0; changed && rounds < 50; rounds++) {
161
+ changed = false;
162
+ for (const { path, unit } of units) {
163
+ if (!unit.ast) continue;
164
+ const mine = pagesOf.get(unit);
165
+ for (const specifier of unit.analysis.imports) {
166
+ const target = resolveSpecifier(path, specifier);
167
+ const m = target != null ? modules.get(target) : null;
168
+ if (!m) continue;
169
+ const theirs = pagesOf.get(m);
170
+ for (const p of mine) if (!theirs.has(p)) { theirs.add(p); changed = true; }
171
+ }
172
+ }
173
+ }
174
+ for (const { path, unit } of units) {
175
+ const a = unit.analysis;
176
+ a.modules = moduleFrom(path);
177
+ a.pool = pool;
178
+ a.pages = [...(pagesOf.get(unit) ?? [])];
179
+ }
180
+
181
+ // Every reference, in every file, to a function of another file (or a method
182
+ // of a written object) otherwise than as a callee — handed to forEach, a
183
+ // timer, a listener, kept in a name — escapes it: called from where the
184
+ // analyser does not see, its parameters stay unknown. Then every call, in
185
+ // every file, to a function the resolver reaches (a method of an object
186
+ // another file set on the global object, an export called by its importer,
187
+ // a method of a written object): a call site of that function, so its
188
+ // parameters are the arguments written there.
189
+ for (const { unit } of units) if (unit.ast) { unit.analysis.cache.clear(); unit.analysis.mentioned.clear(); unit.analysis.globalNames.clear(); unit.analysis.writesByKey = null; unit.analysis.writesBySyntax = null; } // (pass 1 resolved before the wiring)
190
+ for (const { unit } of units) if (unit.ast) unit.analysis.collectEscapes();
191
+ for (const { unit } of units) if (unit.ast) unit.analysis.collectSites();
192
+
193
+ // ---- judging -----------------------------------------------------------------
194
+
195
+ for (const { unit, result, inline } of units) {
196
+ try {
197
+ if (hook) hook(unit);
198
+ const r = judgeSource(unit);
199
+ result.findings.push(...r.findings);
200
+ result.wasm.push(...r.wasm);
201
+ if (inline) result.units.push({ kind: 'inline', line: unit.analysis.lineOffset + 1, minified: r.minified, names: r.names, short: r.short });
202
+ else { result.sourceMappingURL = r.sourceMappingURL; result.units.push({ kind: 'file', line: 1, minified: r.minified, names: r.names, short: r.short }); }
203
+ } catch (e) {
204
+ fail(result, e);
205
+ }
206
+ }
207
+ return out;
208
+ }
@@ -0,0 +1,294 @@
1
+ /**
2
+ * The fake clock a time-travel page runs under, and how the run drives it.
3
+ *
4
+ * The implementation is Playwright's own (the clock its context.clock
5
+ * installs: fake Date, timers, animation frames, idle callbacks, performance
6
+ * and Intl, flowing at real pace and jumped by runFor/fastForward), read from
7
+ * the installed playwright-core. Playwright installs it under a global the
8
+ * game could find (`globalThis.__pwClock`) and drives it by evaluating against
9
+ * that name in every frame; the run installs the same code from its own init
10
+ * script with no global at all, and drives it through the message channel
11
+ * travel.mjs clockTrap listens on under the run's token: the site page (the
12
+ * run's own) posts `token:clock:<op>:<id>:<ms>` into every frame and waits for
13
+ * each frame's `token:clockdone:<id>:<now>`. Nothing of it is reachable from a
14
+ * page's script, and a frame that did not answer is a frame whose clock did
15
+ * not move, which the run treats as the variant breaking rather than judging
16
+ * a game on clocks that disagree.
17
+ *
18
+ * Three pieces of Playwright's code are changed (patchClockSource): a timer's
19
+ * callback is called from a native timer task instead of from the clock's own
20
+ * loop, so a stack trace taken inside it holds the callback alone, as in
21
+ * production, and never the clock's frames (_callFirstTimer, _runTo); an
22
+ * exception it throws reaches the window as an uncaught error, as in
23
+ * production, where the run records it. An animation frame's callback is
24
+ * given the document's animation timeline reading (the embedder's
25
+ * `frameTime`, set by startClock: the native frame time plus the clock's
26
+ * lead, which is what the shimmed `document.timeline.currentTime` reads) in
27
+ * place of the clock's ticks, so inside the callback the two are equal as in
28
+ * production. And a read of the clock while it is paused from its real-time
29
+ * flow (inside a jump: runFor and fastForward pause it) asks the embedder to
30
+ * let the real time since the clock last moved flow into it (`flowInJump`,
31
+ * set by startClock), so inside a callback the jump fires performance.now()
32
+ * and Date.now() move across the callback's own work as they do in any
33
+ * browser, instead of standing still until the callback returns. The patch is
34
+ * applied to the exact text it expects and refused otherwise: a clock the run
35
+ * does not know is not a clock to travel on.
36
+ *
37
+ * A frame the game makes while the run is on (a nested page) installs its own
38
+ * clock at creation and reads the instant from its parent, same-origin, so it
39
+ * agrees with the frames that jumped before it existed; a frame with no
40
+ * same-origin parent (the top frames at launch) starts from the instant the
41
+ * run set, plus the real time since.
42
+ */
43
+ import { createRequire } from 'node:module';
44
+ import { dirname, join } from 'node:path';
45
+
46
+ let cached = null;
47
+
48
+ // What the patch replaces, and with what (clockSource's `_callFirstTimer` and
49
+ // the embedder it dispatches through).
50
+ const DISPATCH_FROM = ` let error;
51
+ try {
52
+ timer.func.apply(null, args);
53
+ } catch (e) {
54
+ error = e;
55
+ }
56
+ await new Promise((f) => this._embedder.setTimeout(f));
57
+ return { timerFound: true, error };`;
58
+ const DISPATCH_TO = ` await new Promise((f) => { this._embedder.callTimer(timer.func, args); this._embedder.setTimeout(f); });
59
+ return { timerFound: true, error: void 0 };`;
60
+ const EMBEDDER_FROM = ` setTimeout: (task, timeout) => {
61
+ const timerId = originals.bound.setTimeout(task, timeout);`;
62
+ const EMBEDDER_TO = ` callTimer: (func, args) => void originals.bound.setTimeout(func, 0, ...args),
63
+ setTimeout: (task, timeout) => {
64
+ const timerId = originals.bound.setTimeout(task, timeout);`;
65
+ const FRAME_FROM = ` if (timer.type === "AnimationFrame" /* AnimationFrame */)
66
+ args = [this._now.ticks];`;
67
+ const FRAME_TO = ` if (timer.type === "AnimationFrame" /* AnimationFrame */)
68
+ args = [typeof this._embedder.frameTime === "function" ? this._embedder.frameTime(this._now.ticks) : this._now.ticks];`;
69
+ // A read of the clock while it is paused from its real-time flow (inside a
70
+ // jump): the embedder may let the real time since flow into it.
71
+ const SYNC_FROM = ` _syncRealTime() {
72
+ if (!this._realTime)
73
+ return;`;
74
+ const SYNC_TO = ` _syncRealTime() {
75
+ if (!this._realTime) {
76
+ if (typeof this._embedder.flowInJump === "function")
77
+ this._embedder.flowInJump();
78
+ return;
79
+ }`;
80
+
81
+ /**
82
+ * Playwright's clock source with a timer's callback dispatched from a native
83
+ * timer task (see above): the callback goes to the native setTimeout as it is,
84
+ * with the timer's arguments, and the clock's loop waits for a task of its own
85
+ * queued right after it, so callbacks still run one per task and in order; an
86
+ * animation frame's callback given the embedder's frame time when it has one;
87
+ * and a read of the paused clock (inside a jump) handed to the embedder's
88
+ * `flowInJump` when it has one, so the clock can flow with the real time the
89
+ * jump's callbacks spend. All four pieces of text must be there exactly once,
90
+ * or the source is refused.
91
+ */
92
+ export function patchClockSource(src) {
93
+ if (typeof src !== 'string') throw new Error('the clock source is not text');
94
+ for (const [from, what] of [[DISPATCH_FROM, 'timer dispatch'], [EMBEDDER_FROM, 'embedder'], [FRAME_FROM, 'animation frame argument'], [SYNC_FROM, 'real-time sync']]) {
95
+ const n = src.split(from).length - 1;
96
+ if (n !== 1) throw new Error(`playwright-core's clock source is not what the run expects: its ${what} appears ${n} time(s), not once`);
97
+ }
98
+ return src.replace(DISPATCH_FROM, DISPATCH_TO).replace(EMBEDDER_FROM, EMBEDDER_TO).replace(FRAME_FROM, FRAME_TO).replace(SYNC_FROM, SYNC_TO);
99
+ }
100
+
101
+ /**
102
+ * Playwright's clock implementation, as its own context.clock injects it: a
103
+ * script that leaves `inject` and `install` on `module.exports`. Read from the
104
+ * installed playwright-core, patched (patchClockSource), and refused when it
105
+ * is not what the run expects: a matrix on a clock the run does not know is
106
+ * not a matrix.
107
+ */
108
+ export function clockSource() {
109
+ if (cached) return cached;
110
+ const require = createRequire(import.meta.url);
111
+ let file;
112
+ try { file = join(dirname(require.resolve('playwright-core')), 'lib', 'generated', 'clockSource.js'); } catch (e) { throw new Error(`playwright-core is not installed: ${e.message}`); }
113
+ let src;
114
+ try { src = require(file).source; } catch (e) { throw new Error(`playwright-core's clock source could not be read (${file}): ${e.message}`); }
115
+ if (typeof src !== 'string' || !src.includes('ClockController') || !src.includes('inject: () => inject') || !src.includes('module.exports')) throw new Error(`playwright-core's clock source is not what the run expects (${file})`);
116
+ cached = patchClockSource(src);
117
+ return cached;
118
+ }
119
+
120
+ /**
121
+ * Starts the fake clock in a scope (stringified into the page's init script,
122
+ * after the natives are captured and before anything else): Playwright's
123
+ * `inject` installs the fakes and lets them flow; `install(at)` sets the
124
+ * instant. `at` is the parent frame's now when the parent is the game's own
125
+ * (same-origin: a frame made mid-run agrees with the frames that jumped), else
126
+ * `o.at` plus the real time since `o.wall`, when the run wrote the script.
127
+ *
128
+ * The fake's monotonic clock is started where the native one stands (Playwright
129
+ * starts it at zero), so performance.now() and every native timestamp agree
130
+ * at boot and differ afterwards by the jumps alone. Those are kept: a history
131
+ * of what the fake clock was ahead of the native one after each move, read
132
+ * back by `leadAt` (the lead at a native instant: a timestamp taken before a
133
+ * jump keeps its time) and `toNative` (a fake instant as the native clock
134
+ * knows it). Inside a jump the clock flows: Playwright pauses its real-time
135
+ * flow for the jump, so the clock would stand still inside every callback the
136
+ * jump fires until the callback returned (a game measuring its own work
137
+ * against performance.now() in a timer would read zero, which no browser
138
+ * shows); here the real time since the clock last moved is let into it at
139
+ * every read inside the jump (the patched `_syncRealTime` asks
140
+ * `flowInJump`) and before every move (a timer's dispatch, the jump's end),
141
+ * so a callback that spends 3 ms sees 3 ms pass, the timer due next fires in
142
+ * order and late by what the callback took, as natively, and the jump still
143
+ * ends at its target with the callbacks' time inside it. And the jumps reach
144
+ * what the clock does not drive: every running animation (Web Animations,
145
+ * CSS) is moved on by what the clock moved, at its own rate, as the clock
146
+ * moves (before any timer due in the jump runs); the real time the jump
147
+ * itself took moves the animations on its own (the compositor's), and what
148
+ * of it the clock did not count is taken back at the end, so a transition
149
+ * finishes in game time, and what is on screen agrees with the clock. An
150
+ * animation frame's callback is handed the timeline's reading (the native
151
+ * frame time plus the lead), the same number `document.timeline.currentTime`
152
+ * gives inside it. The same in a worker (workerShim): no document, no
153
+ * animations, no parent; a timer global the scope never had (Playwright
154
+ * installs `requestIdleCallback` wherever it installs) is removed again, so
155
+ * the scope keeps its own shape. Returns what clockShim and clockTrap need of
156
+ * it: the fake to read, and the jump to drive (and the controller and the
157
+ * history, for the tests).
158
+ */
159
+ export function startClock(scope, o, exportsOf, natives) {
160
+ // Playwright's bundle exports thunks (`inject: () => inject`): the function is one call in.
161
+ const inject = exportsOf.inject();
162
+ if (typeof inject !== 'function') throw new Error('the clock source exports no inject');
163
+ // The clock reads real time through the `performance` it finds at injection
164
+ // and calls `now` on it by name; clockShim will route that name to the fake,
165
+ // so the clock is given a stand-in holding the native `now` bound, and the
166
+ // native object is put back by the shim. Nothing the game sees of it.
167
+ const nativeNow = natives.perfNow.d.value.bind(natives.performance);
168
+ const standIn = { now: nativeNow, timeOrigin: natives.originValue };
169
+ Object.defineProperty(scope, 'performance', { value: standIn, writable: true, enumerable: true, configurable: true });
170
+ const pw = inject(scope);
171
+ if (!pw || !pw.controller || typeof pw.controller.install !== 'function') throw new Error('the clock source gave no controller');
172
+ // A timer the scope never had (a worker has no requestIdleCallback) is not given to it.
173
+ for (const k of ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'requestAnimationFrame', 'cancelAnimationFrame', 'requestIdleCallback', 'cancelIdleCallback']) {
174
+ if (typeof natives[k] !== 'function' && Object.getOwnPropertyDescriptor(scope, k)) { try { delete scope[k]; } catch { /* left as it is */ } }
175
+ }
176
+ // Playwright's fake performance, read here before clockShim puts the native object back: its now and
177
+ // timeOrigin are the fake clock's (the shimmed getters read them from here, never from themselves).
178
+ const fakePerf = scope.performance;
179
+ let at = null;
180
+ try { if (scope.parent && scope.parent !== scope && typeof scope.parent.Date === 'function') at = scope.parent.Date.now(); } catch { at = null; }
181
+ if (typeof at !== 'number' || !Number.isFinite(at)) at = o.at + (natives.Date.now() - o.wall);
182
+ const C = pw.controller;
183
+ C.install(at);
184
+ if (!C._now || typeof C._now.ticks !== 'number' || typeof C._now.time !== 'number' || typeof C._advanceNow !== 'function' || typeof C._innerPause !== 'function' || typeof C._innerResume !== 'function') throw new Error('the clock source is not the controller the run expects');
185
+ // The monotonic clock starts where the native one stands; the origin is the wall time at its zero.
186
+ C._now.ticks = Math.ceil(nativeNow());
187
+ C._now.origin = C._now.time - C._now.ticks;
188
+ // The jumps: (native instant, lead) after each move of the clock.
189
+ const history = [{ at: nativeNow(), lead: C._now.ticks - nativeNow() }];
190
+ const note = () => {
191
+ const t = nativeNow(), lead = C._now.ticks - t, last = history[history.length - 1];
192
+ if (t <= last.at) last.lead = lead;
193
+ else if (Math.abs(last.lead - lead) >= 1) history.push({ at: t, lead });
194
+ };
195
+ const leadAt = (t) => {
196
+ if (!(t > history[0].at)) return history[0].lead;
197
+ let lo = 0, hi = history.length - 1;
198
+ while (lo < hi) { const mid = (lo + hi + 1) >> 1; if (history[mid].at <= t) lo = mid; else hi = mid - 1; }
199
+ return history[lo].lead;
200
+ };
201
+ // (The fake's ticks are whole milliseconds: an instant within one of a
202
+ // move's own is the move's, never the move before.)
203
+ const toNative = (f) => {
204
+ for (let i = history.length - 1; i >= 0; i--) { const t = f - history[i].lead; if (t >= history[i].at - 1) return Math.max(t, history[i].at); }
205
+ return f - history[0].lead;
206
+ };
207
+ // Every running animation of the document moved by `ms` of its own time.
208
+ const advance = (ms) => {
209
+ if (!ms || !scope.document || !natives.getAnimations || !natives.animCurrent || !natives.animState) return;
210
+ let list;
211
+ try { list = natives.getAnimations.call(scope.document); } catch { return; }
212
+ for (const a of list) {
213
+ try {
214
+ if (natives.animState.d.get.call(a) !== 'running') continue;
215
+ const cur = natives.animCurrent.d.get.call(a);
216
+ if (typeof cur !== 'number') continue;
217
+ const rate = natives.animRate ? natives.animRate.d.get.call(a) : 1;
218
+ natives.animCurrent.d.set.call(a, cur + ms * (typeof rate === 'number' ? rate : 1));
219
+ } catch { /* an animation that cannot be moved stays where it is */ }
220
+ }
221
+ };
222
+ // What an animation frame's callback is handed: the document's native frame
223
+ // time plus the clock's lead now, which is what the shimmed
224
+ // document.timeline.currentTime reads inside the callback; the ticks where
225
+ // there is no timeline (a worker).
226
+ C._embedder.frameTime = (ticks) => {
227
+ try {
228
+ const tl = scope.document && scope.document.timeline;
229
+ const v = tl && natives.timeline && typeof natives.timeline.d.get === 'function' ? natives.timeline.d.get.call(tl) : null;
230
+ return typeof v === 'number' ? v + (C._now.ticks - nativeNow()) : ticks;
231
+ } catch { return ticks; }
232
+ };
233
+ const origAdvance = C._advanceNow, origPause = C._innerPause, origResume = C._innerResume;
234
+ // The real time of a jump flows into the clock (see above). `stamp` is the
235
+ // native instant the clock last counted up to while paused (whole
236
+ // milliseconds, as the embedder reads the clock; null while the clock flows
237
+ // in real time by itself), `counted` how much real time the flow put into
238
+ // the clock during this pause. The flow moves the clock alone, never the
239
+ // animations: the compositor moves those by the same real time itself.
240
+ const embNow = () => C._embedder.performanceNow();
241
+ let lostFrom = null, stamp = null, counted = 0;
242
+ const flow = () => {
243
+ if (stamp === null) return;
244
+ const t = embNow(), since = t - stamp;
245
+ if (since > 0) { stamp = t; counted += since; origAdvance.call(C, C._now.ticks + since); note(); }
246
+ };
247
+ C._embedder.flowInJump = flow;
248
+ // A move of the clock (not its real-time flow, which _realTime marks): the real time since the last flows first, then the animations by the rest, then the clock, then the history.
249
+ C._advanceNow = function (to) { if (!this._realTime) { flow(); if (to > this._now.ticks) advance(to - this._now.ticks); } const r = origAdvance.call(this, to); if (!this._realTime) note(); return r; };
250
+ // A jump pauses the real-time flow and resumes it after: the real time between is the clock's only as far as the flow counted it, so the rest is taken back from the animations.
251
+ C._innerPause = function (...a) { if (lostFrom === null) { lostFrom = nativeNow(); counted = 0; stamp = embNow(); } return origPause.apply(this, a); };
252
+ C._innerResume = function (...a) { stamp = null; if (lostFrom !== null) { advance(lostFrom + counted - nativeNow()); lostFrom = null; counted = 0; } const r = origResume.apply(this, a); note(); return r; };
253
+ return {
254
+ fake: { now: () => C.now(), perfNow: () => fakePerf.now(), origin: () => fakePerf.timeOrigin, leadAt, toNative },
255
+ jump: (op, ms) => C[op](ms),
256
+ controller: C,
257
+ history,
258
+ };
259
+ }
260
+
261
+ /**
262
+ * The site page's side of the channel (evaluated in the run's own page): posts
263
+ * the command into every window below it, itself included, and resolves with
264
+ * each window's clock and location once all have answered; the caller bounds
265
+ * the wait and reads `window.__clock` (the site page's own, out of the game's
266
+ * reach) for who did not answer.
267
+ */
268
+ export function siteJump([token, op, ms]) {
269
+ return new Promise((resolve) => {
270
+ const id = Math.random().toString(36).slice(2, 12);
271
+ const posted = [];
272
+ const walk = (w) => {
273
+ try { w.postMessage(`${token}:clock:${op}:${id}:${ms}`, '*'); posted.push(w); } catch { /* a window that cannot be reached has no clock to move */ }
274
+ let n = 0;
275
+ try { n = w.frames.length; } catch { n = 0; }
276
+ for (let i = 0; i < n; i++) { try { walk(w.frames[i]); } catch { /* gone */ } }
277
+ };
278
+ const nows = [], from = [];
279
+ const prefix = `${token}:clockdone:${id}:`;
280
+ // The tally, for the run to read when a window did not answer in time.
281
+ const tally = { id, op, ms, posted: 0, from };
282
+ window.__clock = tally;
283
+ const done = (e) => {
284
+ if (typeof e.data !== 'string' || !e.data.startsWith(prefix)) return;
285
+ const [now, ...href] = e.data.slice(prefix.length).split(':');
286
+ nows.push(Number(now));
287
+ from.push(href.join(':'));
288
+ if (nows.length >= posted.length) { removeEventListener('message', done); resolve({ of: posted.length, nows, from }); }
289
+ };
290
+ addEventListener('message', done);
291
+ walk(window);
292
+ tally.posted = posted.length;
293
+ });
294
+ }
@@ -0,0 +1,56 @@
1
+ // Package conformance holds what the conformance run and the production servers
2
+ // share: the engine's Content-Security-Policy headers, rendered from csp.json
3
+ // (the one source; run.mjs renders the same file), and the endpoint browsers
4
+ // send the policy's violation reports to.
5
+ package conformance
6
+
7
+ import (
8
+ _ "embed"
9
+ "encoding/json"
10
+ "strings"
11
+ )
12
+
13
+ //go:embed csp.json
14
+ var cspJSON []byte
15
+
16
+ var templates struct {
17
+ Bundle string `json:"bundle"`
18
+ Shell string `json:"shell"`
19
+ Endpoints string `json:"endpoints"`
20
+ }
21
+
22
+ func init() {
23
+ // The file is part of the build: a template that does not parse is a defect
24
+ // of the build, found by the first test, never by a player.
25
+ if err := json.Unmarshal(cspJSON, &templates); err != nil {
26
+ panic("csp.json: " + err.Error())
27
+ }
28
+ }
29
+
30
+ func render(template string, pairs ...string) string {
31
+ out := strings.NewReplacer(pairs...).Replace(template)
32
+ if strings.Contains(out, "{") {
33
+ panic("csp.json: a placeholder has no value in " + out)
34
+ }
35
+ return out
36
+ }
37
+
38
+ // BundleCSP is the policy a studio bundle is served under: origin is the
39
+ // bundle's own ('self'), ancestors who may frame it, report where its
40
+ // browser sends violation reports (a path on the bundle's origin).
41
+ func BundleCSP(origin, ancestors, report string) string {
42
+ return render(templates.Bundle, "{origin}", origin, "{ancestors}", ancestors, "{report}", report)
43
+ }
44
+
45
+ // ShellCSP is the shell's policy: frames are the origins it may frame (the
46
+ // bundles), ancestors who may frame it, report where its violation reports go.
47
+ func ShellCSP(frames, ancestors, report string) string {
48
+ return render(templates.Shell, "{frames}", frames, "{ancestors}", ancestors, "{report}", report)
49
+ }
50
+
51
+ // ReportingEndpoints is the Reporting-Endpoints header a policy's `report-to
52
+ // csp` names: browsers with the Reporting API deliver there, the others to
53
+ // report-uri. Both point at the same path.
54
+ func ReportingEndpoints(report string) string {
55
+ return render(templates.Endpoints, "{report}", report)
56
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "about": "The two Content-Security-Policy headers of the House Engine, rendered by bundles.go, shell.go and run.mjs from this one file: {origin} is the bundle's own origin ('self' in production), {frames} the origins the shell may frame, {ancestors} who may frame the page, {report} the path browsers send violation reports to (report-to names the endpoint of the Reporting-Endpoints header, csp.go and csp.mjs render both). SPEC.md §4 explains each directive.",
3
+ "bundle": "sandbox allow-scripts allow-same-origin; default-src 'none'; script-src {origin} 'unsafe-inline' 'wasm-unsafe-eval'; worker-src {origin} blob:; style-src {origin} 'unsafe-inline'; img-src {origin} data: blob:; media-src {origin} data: blob:; font-src {origin} data:; connect-src {origin}; frame-src {origin}; frame-ancestors {ancestors}; base-uri 'none'; form-action 'none'; webrtc 'block'; report-uri {report}; report-to csp",
4
+ "shell": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; frame-src {frames}; img-src 'self' data:; frame-ancestors {ancestors}; base-uri 'none'; form-action 'none'; report-uri {report}; report-to csp",
5
+ "endpoints": "csp=\"{report}\""
6
+ }
@@ -0,0 +1,20 @@
1
+ // The engine's Content-Security-Policy headers, rendered from csp.json: the
2
+ // one source the production servers (csp.go) render the same way, so the run
3
+ // plays a bundle under the policy players get.
4
+ import { readFileSync } from 'node:fs';
5
+
6
+ const templates = JSON.parse(readFileSync(new URL('./csp.json', import.meta.url), 'utf8'));
7
+
8
+ function render(template, values) {
9
+ return template.replace(/\{(\w+)\}/g, (_, key) => {
10
+ if (values[key] === undefined) throw new Error(`csp.json: no value for {${key}}`);
11
+ return values[key];
12
+ });
13
+ }
14
+
15
+ /** The bundle origin's policy: its own origin, who may frame it, where reports go. */
16
+ export const bundleCSP = ({ origin, ancestors, report }) => render(templates.bundle, { origin, ancestors, report });
17
+ /** The shell's policy: which origins it frames, who may frame it, where reports go. */
18
+ export const shellCSP = ({ frames, ancestors, report }) => render(templates.shell, { frames, ancestors, report });
19
+ /** The Reporting-Endpoints header that `report-to csp` names. */
20
+ export const reportingEndpoints = (report) => render(templates.endpoints, { report });
@@ -0,0 +1,9 @@
1
+ // The gzip size of each file named in workerData, for scan.mjs: measured on a
2
+ // worker thread, so a bundle at the budget's edge (seconds of compression) never
3
+ // holds the event loop of the process that asked — the pipeline keeps answering
4
+ // its health checks and the studio while an upload is measured.
5
+ import { parentPort, workerData } from 'node:worker_threads';
6
+ import { readFileSync } from 'node:fs';
7
+ import { gzipSync } from 'node:zlib';
8
+
9
+ parentPort.postMessage(workerData.map((path) => gzipSync(readFileSync(path)).length));
@@ -0,0 +1 @@
1
+ { "type": "module" }