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.
- package/README.md +20 -0
- package/engine-cli/connect.mjs +73 -0
- package/engine-cli/hbengine.mjs +565 -0
- package/engine-cli/package.json +1 -0
- package/engine-cli/templates.mjs +14 -0
- package/engine-conformance/analyse-worker.mjs +12 -0
- package/engine-conformance/analyse.mjs +4458 -0
- package/engine-conformance/bundle.mjs +208 -0
- package/engine-conformance/clock.mjs +294 -0
- package/engine-conformance/csp.go +56 -0
- package/engine-conformance/csp.json +6 -0
- package/engine-conformance/csp.mjs +20 -0
- package/engine-conformance/gzip-sizes.mjs +9 -0
- package/engine-conformance/package.json +1 -0
- package/engine-conformance/picture.mjs +55 -0
- package/engine-conformance/report.go +349 -0
- package/engine-conformance/run.mjs +1217 -0
- package/engine-conformance/scan.mjs +476 -0
- package/engine-conformance/testdata/clock-overlay/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-overlay/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-overlay/bundle/game.js +83 -0
- package/engine-conformance/testdata/clock-overlay/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-overlay/bundle/index.html +34 -0
- package/engine-conformance/testdata/clock-probe/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-probe/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-probe/bundle/game.js +65 -0
- package/engine-conformance/testdata/clock-probe/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-probe/bundle/index.html +35 -0
- package/engine-conformance/testdata/clock-probe/bundle/probe-worker.js +46 -0
- package/engine-conformance/testdata/clock-probe/bundle/probe.js +210 -0
- package/engine-conformance/testdata/clock-probe/math/dice.tsv +100 -0
- package/engine-conformance/testdata/clock-probe/math/manifest.json +2925 -0
- package/engine-conformance/testdata/clock-worker/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-worker/bundle/clock-worker.js +14 -0
- package/engine-conformance/testdata/clock-worker/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-worker/bundle/game.js +61 -0
- package/engine-conformance/testdata/clock-worker/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-worker/bundle/index.html +35 -0
- package/engine-conformance/testdata/clock-worker/math/dice.tsv +100 -0
- package/engine-conformance/testdata/clock-worker/math/manifest.json +2925 -0
- package/engine-conformance/travel.mjs +1209 -0
- package/engine-conformance/vendor/acorn/LICENSE +21 -0
- package/engine-conformance/vendor/acorn/acorn.mjs +6266 -0
- package/engine-conformance/wasm-releases.json +168 -0
- package/engine-examples/README.md +23 -0
- package/engine-examples/coin/AGENTS.md +3 -0
- package/engine-examples/coin/README.md +7 -0
- package/engine-examples/coin/bundle/assets.json +1 -0
- package/engine-examples/coin/bundle/game.css +5 -0
- package/engine-examples/coin/bundle/game.js +19 -0
- package/engine-examples/coin/bundle/hbtest.json +5 -0
- package/engine-examples/coin/bundle/index.html +16 -0
- package/engine-examples/coin/math/coin.tsv +2 -0
- package/engine-examples/coin/math/manifest.json +17 -0
- package/engine-examples/crash/AGENTS.md +3 -0
- package/engine-examples/crash/README.md +14 -0
- package/engine-examples/crash/bundle/assets.json +1 -0
- package/engine-examples/crash/bundle/game.css +8 -0
- package/engine-examples/crash/bundle/game.js +90 -0
- package/engine-examples/crash/bundle/hbtest.json +5 -0
- package/engine-examples/crash/bundle/index.html +20 -0
- package/engine-examples/crash/math/crash-01.tsv +2 -0
- package/engine-examples/crash/math/crash-02.tsv +2 -0
- package/engine-examples/crash/math/crash-03.tsv +2 -0
- package/engine-examples/crash/math/crash-04.tsv +2 -0
- package/engine-examples/crash/math/crash-05.tsv +2 -0
- package/engine-examples/crash/math/crash-06.tsv +2 -0
- package/engine-examples/crash/math/crash-07.tsv +2 -0
- package/engine-examples/crash/math/crash-08.tsv +2 -0
- package/engine-examples/crash/math/crash-09.tsv +2 -0
- package/engine-examples/crash/math/crash-10.tsv +2 -0
- package/engine-examples/crash/math/crash-11.tsv +2 -0
- package/engine-examples/crash/math/crash-12.tsv +2 -0
- package/engine-examples/crash/math/crash-13.tsv +2 -0
- package/engine-examples/crash/math/crash-14.tsv +2 -0
- package/engine-examples/crash/math/crash-15.tsv +2 -0
- package/engine-examples/crash/math/crash-16.tsv +2 -0
- package/engine-examples/crash/math/crash-17.tsv +2 -0
- package/engine-examples/crash/math/crash-18.tsv +2 -0
- package/engine-examples/crash/math/crash-19.tsv +2 -0
- package/engine-examples/crash/math/crash-20.tsv +2 -0
- package/engine-examples/crash/math/crash-21.tsv +2 -0
- package/engine-examples/crash/math/crash-22.tsv +2 -0
- package/engine-examples/crash/math/crash-23.tsv +2 -0
- package/engine-examples/crash/math/crash-24.tsv +2 -0
- package/engine-examples/crash/math/crash-25.tsv +2 -0
- package/engine-examples/crash/math/crash-26.tsv +2 -0
- package/engine-examples/crash/math/crash-27.tsv +2 -0
- package/engine-examples/crash/math/crash-28.tsv +2 -0
- package/engine-examples/crash/math/crash-29.tsv +2 -0
- package/engine-examples/crash/math/crash-30.tsv +2 -0
- package/engine-examples/crash/math/crash-31.tsv +2 -0
- package/engine-examples/crash/math/crash-32.tsv +2 -0
- package/engine-examples/crash/math/crash-33.tsv +2 -0
- package/engine-examples/crash/math/crash-34.tsv +2 -0
- package/engine-examples/crash/math/crash-35.tsv +2 -0
- package/engine-examples/crash/math/crash-36.tsv +2 -0
- package/engine-examples/crash/math/crash-37.tsv +2 -0
- package/engine-examples/crash/math/crash-38.tsv +2 -0
- package/engine-examples/crash/math/crash-39.tsv +2 -0
- package/engine-examples/crash/math/crash-40.tsv +2 -0
- package/engine-examples/crash/math/crash-41.tsv +2 -0
- package/engine-examples/crash/math/crash-42.tsv +2 -0
- package/engine-examples/crash/math/crash-43.tsv +2 -0
- package/engine-examples/crash/math/crash-44.tsv +2 -0
- package/engine-examples/crash/math/crash-45.tsv +2 -0
- package/engine-examples/crash/math/crash-46.tsv +2 -0
- package/engine-examples/crash/math/crash-47.tsv +2 -0
- package/engine-examples/crash/math/crash-48.tsv +2 -0
- package/engine-examples/crash/math/crash-49.tsv +2 -0
- package/engine-examples/crash/math/crash-50.tsv +2 -0
- package/engine-examples/crash/math/crash-51.tsv +2 -0
- package/engine-examples/crash/math/crash-52.tsv +2 -0
- package/engine-examples/crash/math/crash-53.tsv +2 -0
- package/engine-examples/crash/math/crash-54.tsv +2 -0
- package/engine-examples/crash/math/crash-55.tsv +2 -0
- package/engine-examples/crash/math/crash-56.tsv +2 -0
- package/engine-examples/crash/math/crash-57.tsv +2 -0
- package/engine-examples/crash/math/crash-58.tsv +2 -0
- package/engine-examples/crash/math/crash-59.tsv +2 -0
- package/engine-examples/crash/math/crash-60.tsv +2 -0
- package/engine-examples/crash/math/crash-61.tsv +2 -0
- package/engine-examples/crash/math/crash-62.tsv +2 -0
- package/engine-examples/crash/math/crash-63.tsv +2 -0
- package/engine-examples/crash/math/crash-64.tsv +2 -0
- package/engine-examples/crash/math/gen.mjs +31 -0
- package/engine-examples/crash/math/manifest.json +1 -0
- package/engine-examples/dice/AGENTS.md +3 -0
- package/engine-examples/dice/README.md +25 -0
- package/engine-examples/dice/bundle/assets.json +1 -0
- package/engine-examples/dice/bundle/game.css +17 -0
- package/engine-examples/dice/bundle/game.js +61 -0
- package/engine-examples/dice/bundle/hbtest.json +4 -0
- package/engine-examples/dice/bundle/index.html +34 -0
- package/engine-examples/dice/math/dice.tsv +100 -0
- package/engine-examples/dice/math/manifest.json +2925 -0
- package/engine-examples/keno/AGENTS.md +3 -0
- package/engine-examples/keno/README.md +11 -0
- package/engine-examples/keno/bundle/assets.json +1 -0
- package/engine-examples/keno/bundle/game.css +14 -0
- package/engine-examples/keno/bundle/game.js +86 -0
- package/engine-examples/keno/bundle/hbtest.json +4 -0
- package/engine-examples/keno/bundle/i18n.json +342 -0
- package/engine-examples/keno/bundle/index.html +20 -0
- package/engine-examples/keno/bundle/model.json +1 -0
- package/engine-examples/keno/math/gen.mjs +35 -0
- package/engine-examples/keno/math/keno-picks-1.tsv +2 -0
- package/engine-examples/keno/math/keno-picks-10.tsv +11 -0
- package/engine-examples/keno/math/keno-picks-2.tsv +3 -0
- package/engine-examples/keno/math/keno-picks-3.tsv +4 -0
- package/engine-examples/keno/math/keno-picks-4.tsv +5 -0
- package/engine-examples/keno/math/keno-picks-5.tsv +6 -0
- package/engine-examples/keno/math/keno-picks-6.tsv +7 -0
- package/engine-examples/keno/math/keno-picks-7.tsv +8 -0
- package/engine-examples/keno/math/keno-picks-8.tsv +9 -0
- package/engine-examples/keno/math/keno-picks-9.tsv +10 -0
- package/engine-examples/keno/math/manifest.json +1 -0
- package/engine-examples/keno/math-kit/pack.mjs +165 -0
- package/engine-examples/keno/math-kit/package.json +8 -0
- package/engine-examples/keno/math-kit/slot.mjs +94 -0
- package/engine-examples/keno/math-kit/tumble.mjs +94 -0
- package/engine-examples/lines/AGENTS.md +3 -0
- package/engine-examples/lines/README.md +20 -0
- package/engine-examples/lines/bundle/assets.json +1 -0
- package/engine-examples/lines/bundle/game.css +16 -0
- package/engine-examples/lines/bundle/game.js +125 -0
- package/engine-examples/lines/bundle/hbtest.json +4 -0
- package/engine-examples/lines/bundle/i18n.json +242 -0
- package/engine-examples/lines/bundle/index.html +23 -0
- package/engine-examples/lines/bundle/model.json +1 -0
- package/engine-examples/lines/math/gen.mjs +54 -0
- package/engine-examples/lines/math-kit/pack.mjs +165 -0
- package/engine-examples/lines/math-kit/package.json +8 -0
- package/engine-examples/lines/math-kit/slot.mjs +94 -0
- package/engine-examples/lines/math-kit/tumble.mjs +94 -0
- package/engine-examples/mines/AGENTS.md +3 -0
- package/engine-examples/mines/README.md +13 -0
- package/engine-examples/mines/bundle/assets.json +1 -0
- package/engine-examples/mines/bundle/game.css +12 -0
- package/engine-examples/mines/bundle/game.js +58 -0
- package/engine-examples/mines/bundle/hbtest.json +8 -0
- package/engine-examples/mines/bundle/index.html +16 -0
- package/engine-examples/mines/math/gen.mjs +40 -0
- package/engine-examples/mines/math/manifest.json +1 -0
- package/engine-examples/mines/math/mines-1-1.tsv +2 -0
- package/engine-examples/mines/math/mines-1-2.tsv +2 -0
- package/engine-examples/mines/math/mines-1-22.tsv +2 -0
- package/engine-examples/mines/math/mines-1-23.tsv +2 -0
- package/engine-examples/mines/math/mines-1-24.tsv +2 -0
- package/engine-examples/mines/math/mines-1-3.tsv +2 -0
- package/engine-examples/mines/math/mines-10-1.tsv +2 -0
- package/engine-examples/mines/math/mines-10-10.tsv +2 -0
- package/engine-examples/mines/math/mines-10-11.tsv +2 -0
- package/engine-examples/mines/math/mines-10-12.tsv +2 -0
- package/engine-examples/mines/math/mines-10-13.tsv +2 -0
- package/engine-examples/mines/math/mines-10-14.tsv +2 -0
- package/engine-examples/mines/math/mines-10-15.tsv +2 -0
- package/engine-examples/mines/math/mines-10-2.tsv +2 -0
- package/engine-examples/mines/math/mines-10-3.tsv +2 -0
- package/engine-examples/mines/math/mines-10-4.tsv +2 -0
- package/engine-examples/mines/math/mines-10-5.tsv +2 -0
- package/engine-examples/mines/math/mines-10-6.tsv +2 -0
- package/engine-examples/mines/math/mines-10-7.tsv +2 -0
- package/engine-examples/mines/math/mines-10-8.tsv +2 -0
- package/engine-examples/mines/math/mines-10-9.tsv +2 -0
- package/engine-examples/mines/math/mines-11-1.tsv +2 -0
- package/engine-examples/mines/math/mines-11-10.tsv +2 -0
- package/engine-examples/mines/math/mines-11-11.tsv +2 -0
- package/engine-examples/mines/math/mines-11-12.tsv +2 -0
- package/engine-examples/mines/math/mines-11-13.tsv +2 -0
- package/engine-examples/mines/math/mines-11-14.tsv +2 -0
- package/engine-examples/mines/math/mines-11-2.tsv +2 -0
- package/engine-examples/mines/math/mines-11-3.tsv +2 -0
- package/engine-examples/mines/math/mines-11-4.tsv +2 -0
- package/engine-examples/mines/math/mines-11-5.tsv +2 -0
- package/engine-examples/mines/math/mines-11-6.tsv +2 -0
- package/engine-examples/mines/math/mines-11-7.tsv +2 -0
- package/engine-examples/mines/math/mines-11-8.tsv +2 -0
- package/engine-examples/mines/math/mines-11-9.tsv +2 -0
- package/engine-examples/mines/math/mines-12-1.tsv +2 -0
- package/engine-examples/mines/math/mines-12-10.tsv +2 -0
- package/engine-examples/mines/math/mines-12-11.tsv +2 -0
- package/engine-examples/mines/math/mines-12-12.tsv +2 -0
- package/engine-examples/mines/math/mines-12-13.tsv +2 -0
- package/engine-examples/mines/math/mines-12-2.tsv +2 -0
- package/engine-examples/mines/math/mines-12-3.tsv +2 -0
- package/engine-examples/mines/math/mines-12-4.tsv +2 -0
- package/engine-examples/mines/math/mines-12-5.tsv +2 -0
- package/engine-examples/mines/math/mines-12-6.tsv +2 -0
- package/engine-examples/mines/math/mines-12-7.tsv +2 -0
- package/engine-examples/mines/math/mines-12-8.tsv +2 -0
- package/engine-examples/mines/math/mines-12-9.tsv +2 -0
- package/engine-examples/mines/math/mines-13-1.tsv +2 -0
- package/engine-examples/mines/math/mines-13-10.tsv +2 -0
- package/engine-examples/mines/math/mines-13-11.tsv +2 -0
- package/engine-examples/mines/math/mines-13-12.tsv +2 -0
- package/engine-examples/mines/math/mines-13-2.tsv +2 -0
- package/engine-examples/mines/math/mines-13-3.tsv +2 -0
- package/engine-examples/mines/math/mines-13-4.tsv +2 -0
- package/engine-examples/mines/math/mines-13-5.tsv +2 -0
- package/engine-examples/mines/math/mines-13-6.tsv +2 -0
- package/engine-examples/mines/math/mines-13-7.tsv +2 -0
- package/engine-examples/mines/math/mines-13-8.tsv +2 -0
- package/engine-examples/mines/math/mines-13-9.tsv +2 -0
- package/engine-examples/mines/math/mines-14-1.tsv +2 -0
- package/engine-examples/mines/math/mines-14-10.tsv +2 -0
- package/engine-examples/mines/math/mines-14-11.tsv +2 -0
- package/engine-examples/mines/math/mines-14-2.tsv +2 -0
- package/engine-examples/mines/math/mines-14-3.tsv +2 -0
- package/engine-examples/mines/math/mines-14-4.tsv +2 -0
- package/engine-examples/mines/math/mines-14-5.tsv +2 -0
- package/engine-examples/mines/math/mines-14-6.tsv +2 -0
- package/engine-examples/mines/math/mines-14-7.tsv +2 -0
- package/engine-examples/mines/math/mines-14-8.tsv +2 -0
- package/engine-examples/mines/math/mines-14-9.tsv +2 -0
- package/engine-examples/mines/math/mines-15-1.tsv +2 -0
- package/engine-examples/mines/math/mines-15-10.tsv +2 -0
- package/engine-examples/mines/math/mines-15-2.tsv +2 -0
- package/engine-examples/mines/math/mines-15-3.tsv +2 -0
- package/engine-examples/mines/math/mines-15-4.tsv +2 -0
- package/engine-examples/mines/math/mines-15-5.tsv +2 -0
- package/engine-examples/mines/math/mines-15-6.tsv +2 -0
- package/engine-examples/mines/math/mines-15-7.tsv +2 -0
- package/engine-examples/mines/math/mines-15-8.tsv +2 -0
- package/engine-examples/mines/math/mines-15-9.tsv +2 -0
- package/engine-examples/mines/math/mines-16-1.tsv +2 -0
- package/engine-examples/mines/math/mines-16-2.tsv +2 -0
- package/engine-examples/mines/math/mines-16-3.tsv +2 -0
- package/engine-examples/mines/math/mines-16-4.tsv +2 -0
- package/engine-examples/mines/math/mines-16-5.tsv +2 -0
- package/engine-examples/mines/math/mines-16-6.tsv +2 -0
- package/engine-examples/mines/math/mines-16-7.tsv +2 -0
- package/engine-examples/mines/math/mines-16-8.tsv +2 -0
- package/engine-examples/mines/math/mines-16-9.tsv +2 -0
- package/engine-examples/mines/math/mines-17-1.tsv +2 -0
- package/engine-examples/mines/math/mines-17-2.tsv +2 -0
- package/engine-examples/mines/math/mines-17-3.tsv +2 -0
- package/engine-examples/mines/math/mines-17-4.tsv +2 -0
- package/engine-examples/mines/math/mines-17-5.tsv +2 -0
- package/engine-examples/mines/math/mines-17-6.tsv +2 -0
- package/engine-examples/mines/math/mines-17-7.tsv +2 -0
- package/engine-examples/mines/math/mines-17-8.tsv +2 -0
- package/engine-examples/mines/math/mines-18-1.tsv +2 -0
- package/engine-examples/mines/math/mines-18-2.tsv +2 -0
- package/engine-examples/mines/math/mines-18-3.tsv +2 -0
- package/engine-examples/mines/math/mines-18-4.tsv +2 -0
- package/engine-examples/mines/math/mines-18-5.tsv +2 -0
- package/engine-examples/mines/math/mines-18-6.tsv +2 -0
- package/engine-examples/mines/math/mines-18-7.tsv +2 -0
- package/engine-examples/mines/math/mines-19-1.tsv +2 -0
- package/engine-examples/mines/math/mines-19-2.tsv +2 -0
- package/engine-examples/mines/math/mines-19-3.tsv +2 -0
- package/engine-examples/mines/math/mines-19-4.tsv +2 -0
- package/engine-examples/mines/math/mines-19-5.tsv +2 -0
- package/engine-examples/mines/math/mines-19-6.tsv +2 -0
- package/engine-examples/mines/math/mines-2-1.tsv +2 -0
- package/engine-examples/mines/math/mines-2-19.tsv +2 -0
- package/engine-examples/mines/math/mines-2-2.tsv +2 -0
- package/engine-examples/mines/math/mines-2-20.tsv +2 -0
- package/engine-examples/mines/math/mines-2-21.tsv +2 -0
- package/engine-examples/mines/math/mines-2-22.tsv +2 -0
- package/engine-examples/mines/math/mines-2-23.tsv +2 -0
- package/engine-examples/mines/math/mines-2-3.tsv +2 -0
- package/engine-examples/mines/math/mines-2-4.tsv +2 -0
- package/engine-examples/mines/math/mines-2-5.tsv +2 -0
- package/engine-examples/mines/math/mines-20-1.tsv +2 -0
- package/engine-examples/mines/math/mines-20-2.tsv +2 -0
- package/engine-examples/mines/math/mines-20-3.tsv +2 -0
- package/engine-examples/mines/math/mines-20-4.tsv +2 -0
- package/engine-examples/mines/math/mines-20-5.tsv +2 -0
- package/engine-examples/mines/math/mines-21-1.tsv +2 -0
- package/engine-examples/mines/math/mines-21-2.tsv +2 -0
- package/engine-examples/mines/math/mines-21-3.tsv +2 -0
- package/engine-examples/mines/math/mines-21-4.tsv +2 -0
- package/engine-examples/mines/math/mines-22-1.tsv +2 -0
- package/engine-examples/mines/math/mines-22-2.tsv +2 -0
- package/engine-examples/mines/math/mines-22-3.tsv +2 -0
- package/engine-examples/mines/math/mines-23-1.tsv +2 -0
- package/engine-examples/mines/math/mines-23-2.tsv +2 -0
- package/engine-examples/mines/math/mines-24-1.tsv +2 -0
- package/engine-examples/mines/math/mines-3-1.tsv +2 -0
- package/engine-examples/mines/math/mines-3-17.tsv +2 -0
- package/engine-examples/mines/math/mines-3-18.tsv +2 -0
- package/engine-examples/mines/math/mines-3-19.tsv +2 -0
- package/engine-examples/mines/math/mines-3-2.tsv +2 -0
- package/engine-examples/mines/math/mines-3-20.tsv +2 -0
- package/engine-examples/mines/math/mines-3-21.tsv +2 -0
- package/engine-examples/mines/math/mines-3-22.tsv +2 -0
- package/engine-examples/mines/math/mines-3-3.tsv +2 -0
- package/engine-examples/mines/math/mines-3-4.tsv +2 -0
- package/engine-examples/mines/math/mines-3-5.tsv +2 -0
- package/engine-examples/mines/math/mines-3-6.tsv +2 -0
- package/engine-examples/mines/math/mines-4-1.tsv +2 -0
- package/engine-examples/mines/math/mines-4-13.tsv +2 -0
- package/engine-examples/mines/math/mines-4-14.tsv +2 -0
- package/engine-examples/mines/math/mines-4-15.tsv +2 -0
- package/engine-examples/mines/math/mines-4-16.tsv +2 -0
- package/engine-examples/mines/math/mines-4-17.tsv +2 -0
- package/engine-examples/mines/math/mines-4-18.tsv +2 -0
- package/engine-examples/mines/math/mines-4-19.tsv +2 -0
- package/engine-examples/mines/math/mines-4-2.tsv +2 -0
- package/engine-examples/mines/math/mines-4-20.tsv +2 -0
- package/engine-examples/mines/math/mines-4-21.tsv +2 -0
- package/engine-examples/mines/math/mines-4-3.tsv +2 -0
- package/engine-examples/mines/math/mines-4-4.tsv +2 -0
- package/engine-examples/mines/math/mines-4-5.tsv +2 -0
- package/engine-examples/mines/math/mines-4-6.tsv +2 -0
- package/engine-examples/mines/math/mines-4-7.tsv +2 -0
- package/engine-examples/mines/math/mines-4-8.tsv +2 -0
- package/engine-examples/mines/math/mines-4-9.tsv +2 -0
- package/engine-examples/mines/math/mines-5-1.tsv +2 -0
- package/engine-examples/mines/math/mines-5-10.tsv +2 -0
- package/engine-examples/mines/math/mines-5-11.tsv +2 -0
- package/engine-examples/mines/math/mines-5-12.tsv +2 -0
- package/engine-examples/mines/math/mines-5-13.tsv +2 -0
- package/engine-examples/mines/math/mines-5-14.tsv +2 -0
- package/engine-examples/mines/math/mines-5-15.tsv +2 -0
- package/engine-examples/mines/math/mines-5-16.tsv +2 -0
- package/engine-examples/mines/math/mines-5-17.tsv +2 -0
- package/engine-examples/mines/math/mines-5-18.tsv +2 -0
- package/engine-examples/mines/math/mines-5-19.tsv +2 -0
- package/engine-examples/mines/math/mines-5-2.tsv +2 -0
- package/engine-examples/mines/math/mines-5-20.tsv +2 -0
- package/engine-examples/mines/math/mines-5-3.tsv +2 -0
- package/engine-examples/mines/math/mines-5-4.tsv +2 -0
- package/engine-examples/mines/math/mines-5-5.tsv +2 -0
- package/engine-examples/mines/math/mines-5-6.tsv +2 -0
- package/engine-examples/mines/math/mines-5-7.tsv +2 -0
- package/engine-examples/mines/math/mines-5-8.tsv +2 -0
- package/engine-examples/mines/math/mines-5-9.tsv +2 -0
- package/engine-examples/mines/math/mines-6-1.tsv +2 -0
- package/engine-examples/mines/math/mines-6-10.tsv +2 -0
- package/engine-examples/mines/math/mines-6-11.tsv +2 -0
- package/engine-examples/mines/math/mines-6-12.tsv +2 -0
- package/engine-examples/mines/math/mines-6-13.tsv +2 -0
- package/engine-examples/mines/math/mines-6-14.tsv +2 -0
- package/engine-examples/mines/math/mines-6-15.tsv +2 -0
- package/engine-examples/mines/math/mines-6-16.tsv +2 -0
- package/engine-examples/mines/math/mines-6-17.tsv +2 -0
- package/engine-examples/mines/math/mines-6-18.tsv +2 -0
- package/engine-examples/mines/math/mines-6-19.tsv +2 -0
- package/engine-examples/mines/math/mines-6-2.tsv +2 -0
- package/engine-examples/mines/math/mines-6-3.tsv +2 -0
- package/engine-examples/mines/math/mines-6-4.tsv +2 -0
- package/engine-examples/mines/math/mines-6-5.tsv +2 -0
- package/engine-examples/mines/math/mines-6-6.tsv +2 -0
- package/engine-examples/mines/math/mines-6-7.tsv +2 -0
- package/engine-examples/mines/math/mines-6-8.tsv +2 -0
- package/engine-examples/mines/math/mines-6-9.tsv +2 -0
- package/engine-examples/mines/math/mines-7-1.tsv +2 -0
- package/engine-examples/mines/math/mines-7-10.tsv +2 -0
- package/engine-examples/mines/math/mines-7-11.tsv +2 -0
- package/engine-examples/mines/math/mines-7-12.tsv +2 -0
- package/engine-examples/mines/math/mines-7-13.tsv +2 -0
- package/engine-examples/mines/math/mines-7-14.tsv +2 -0
- package/engine-examples/mines/math/mines-7-15.tsv +2 -0
- package/engine-examples/mines/math/mines-7-16.tsv +2 -0
- package/engine-examples/mines/math/mines-7-17.tsv +2 -0
- package/engine-examples/mines/math/mines-7-18.tsv +2 -0
- package/engine-examples/mines/math/mines-7-2.tsv +2 -0
- package/engine-examples/mines/math/mines-7-3.tsv +2 -0
- package/engine-examples/mines/math/mines-7-4.tsv +2 -0
- package/engine-examples/mines/math/mines-7-5.tsv +2 -0
- package/engine-examples/mines/math/mines-7-6.tsv +2 -0
- package/engine-examples/mines/math/mines-7-7.tsv +2 -0
- package/engine-examples/mines/math/mines-7-8.tsv +2 -0
- package/engine-examples/mines/math/mines-7-9.tsv +2 -0
- package/engine-examples/mines/math/mines-8-1.tsv +2 -0
- package/engine-examples/mines/math/mines-8-10.tsv +2 -0
- package/engine-examples/mines/math/mines-8-11.tsv +2 -0
- package/engine-examples/mines/math/mines-8-12.tsv +2 -0
- package/engine-examples/mines/math/mines-8-13.tsv +2 -0
- package/engine-examples/mines/math/mines-8-14.tsv +2 -0
- package/engine-examples/mines/math/mines-8-15.tsv +2 -0
- package/engine-examples/mines/math/mines-8-16.tsv +2 -0
- package/engine-examples/mines/math/mines-8-17.tsv +2 -0
- package/engine-examples/mines/math/mines-8-2.tsv +2 -0
- package/engine-examples/mines/math/mines-8-3.tsv +2 -0
- package/engine-examples/mines/math/mines-8-4.tsv +2 -0
- package/engine-examples/mines/math/mines-8-5.tsv +2 -0
- package/engine-examples/mines/math/mines-8-6.tsv +2 -0
- package/engine-examples/mines/math/mines-8-7.tsv +2 -0
- package/engine-examples/mines/math/mines-8-8.tsv +2 -0
- package/engine-examples/mines/math/mines-8-9.tsv +2 -0
- package/engine-examples/mines/math/mines-9-1.tsv +2 -0
- package/engine-examples/mines/math/mines-9-10.tsv +2 -0
- package/engine-examples/mines/math/mines-9-11.tsv +2 -0
- package/engine-examples/mines/math/mines-9-12.tsv +2 -0
- package/engine-examples/mines/math/mines-9-13.tsv +2 -0
- package/engine-examples/mines/math/mines-9-14.tsv +2 -0
- package/engine-examples/mines/math/mines-9-15.tsv +2 -0
- package/engine-examples/mines/math/mines-9-16.tsv +2 -0
- package/engine-examples/mines/math/mines-9-2.tsv +2 -0
- package/engine-examples/mines/math/mines-9-3.tsv +2 -0
- package/engine-examples/mines/math/mines-9-4.tsv +2 -0
- package/engine-examples/mines/math/mines-9-5.tsv +2 -0
- package/engine-examples/mines/math/mines-9-6.tsv +2 -0
- package/engine-examples/mines/math/mines-9-7.tsv +2 -0
- package/engine-examples/mines/math/mines-9-8.tsv +2 -0
- package/engine-examples/mines/math/mines-9-9.tsv +2 -0
- package/engine-examples/plinko/AGENTS.md +3 -0
- package/engine-examples/plinko/README.md +9 -0
- package/engine-examples/plinko/bundle/assets.json +1 -0
- package/engine-examples/plinko/bundle/game.css +5 -0
- package/engine-examples/plinko/bundle/game.js +92 -0
- package/engine-examples/plinko/bundle/hbtest.json +4 -0
- package/engine-examples/plinko/bundle/index.html +16 -0
- package/engine-examples/plinko/bundle/model.json +1 -0
- package/engine-examples/plinko/math/gen.mjs +40 -0
- package/engine-examples/plinko/math/manifest.json +1 -0
- package/engine-examples/plinko/math/plinko-12-high.tsv +13 -0
- package/engine-examples/plinko/math/plinko-12-low.tsv +13 -0
- package/engine-examples/plinko/math/plinko-12-medium.tsv +13 -0
- package/engine-examples/plinko/math/plinko-16-high.tsv +17 -0
- package/engine-examples/plinko/math/plinko-16-low.tsv +17 -0
- package/engine-examples/plinko/math/plinko-16-medium.tsv +17 -0
- package/engine-examples/plinko/math/plinko-8-high.tsv +9 -0
- package/engine-examples/plinko/math/plinko-8-low.tsv +9 -0
- package/engine-examples/plinko/math/plinko-8-medium.tsv +9 -0
- package/engine-examples/plinko/math-kit/pack.mjs +165 -0
- package/engine-examples/plinko/math-kit/package.json +8 -0
- package/engine-examples/plinko/math-kit/slot.mjs +94 -0
- package/engine-examples/plinko/math-kit/tumble.mjs +94 -0
- package/engine-examples/slot/AGENTS.md +3 -0
- package/engine-examples/slot/README.md +26 -0
- package/engine-examples/slot/bundle/assets.json +1 -0
- package/engine-examples/slot/bundle/game.css +19 -0
- package/engine-examples/slot/bundle/game.js +42 -0
- package/engine-examples/slot/bundle/hbtest.json +4 -0
- package/engine-examples/slot/bundle/index.html +23 -0
- package/engine-examples/slot/math/gen.mjs +79 -0
- package/engine-examples/slot/math/manifest.json +1 -0
- package/engine-examples/slot/math/slot.tsv +110592 -0
- package/engine-examples/tower/AGENTS.md +3 -0
- package/engine-examples/tower/README.md +21 -0
- package/engine-examples/tower/bundle/assets.json +1 -0
- package/engine-examples/tower/bundle/game.css +8 -0
- package/engine-examples/tower/bundle/game.js +37 -0
- package/engine-examples/tower/bundle/hbtest.json +5 -0
- package/engine-examples/tower/bundle/index.html +16 -0
- package/engine-examples/tower/math/floor.tsv +4 -0
- package/engine-examples/tower/math/manifest.json +61 -0
- package/engine-examples/tumble/AGENTS.md +3 -0
- package/engine-examples/tumble/README.md +18 -0
- package/engine-examples/tumble/bundle/assets.json +1 -0
- package/engine-examples/tumble/bundle/game.css +13 -0
- package/engine-examples/tumble/bundle/game.js +119 -0
- package/engine-examples/tumble/bundle/hbtest.json +4 -0
- package/engine-examples/tumble/bundle/i18n.json +122 -0
- package/engine-examples/tumble/bundle/index.html +20 -0
- package/engine-examples/tumble/bundle/model.json +1 -0
- package/engine-examples/tumble/math/gen.mjs +38 -0
- package/engine-examples/tumble/math-kit/pack.mjs +165 -0
- package/engine-examples/tumble/math-kit/package.json +8 -0
- package/engine-examples/tumble/math-kit/slot.mjs +94 -0
- package/engine-examples/tumble/math-kit/tumble.mjs +94 -0
- package/engine-examples/wheel/README.md +14 -0
- package/engine-examples/wheel/bundle/assets.json +1 -0
- package/engine-examples/wheel/bundle/game.css +7 -0
- package/engine-examples/wheel/bundle/game.js +51 -0
- package/engine-examples/wheel/bundle/hbtest.json +4 -0
- package/engine-examples/wheel/bundle/index.html +19 -0
- package/engine-examples/wheel/math/high.tsv +24 -0
- package/engine-examples/wheel/math/low.tsv +24 -0
- package/engine-examples/wheel/math/manifest.json +48 -0
- package/engine-examples/wheel/math/medium.tsv +24 -0
- package/engine-math/pack.mjs +165 -0
- package/engine-math/package.json +1 -0
- package/engine-math/slot.mjs +94 -0
- package/engine-math/tumble.mjs +94 -0
- package/engine-mcp/package.json +1 -0
- package/engine-mcp/server.mjs +151 -0
- package/engine-sdk/GUIDE.md +855 -0
- package/engine-sdk/assets.js +92 -0
- package/engine-sdk/autoplay.js +82 -0
- package/engine-sdk/bet-field.js +391 -0
- package/engine-sdk/kit/fiat-coins.js +51 -0
- package/engine-sdk/kit/numeric-input.js +116 -0
- package/engine-sdk/kit/wallet-format.js +423 -0
- package/engine-sdk/marks.js +22 -0
- package/engine-sdk/messages.js +289 -0
- package/engine-sdk/money.js +89 -0
- package/engine-sdk/package.json +1 -0
- package/engine-sdk/protocol.js +263 -0
- package/engine-sdk/sdk.d.ts +138 -0
- package/engine-sdk/sdk.js +330 -0
- package/engine-sdk/stake.js +68 -0
- package/engine-sdk/ui.d.ts +36 -0
- package/engine-sdk/ui.js +340 -0
- package/engine-shell/core/faults.js +127 -0
- package/engine-shell/core/rgs.js +193 -0
- package/engine-shell/core/router.js +917 -0
- package/engine-shell/host/boot.js +408 -0
- package/engine-shell/host/engine-v1.js +438 -0
- package/engine-shell/host/fair.js +285 -0
- package/engine-shell/host/fonts/SOURCE.json +11 -0
- package/engine-shell/host/fonts/figtree-400.woff2 +0 -0
- package/engine-shell/host/fonts/figtree-500.woff2 +0 -0
- package/engine-shell/host/fonts/figtree-600.woff2 +0 -0
- package/engine-shell/host/fonts/red-hat-display-700.woff2 +0 -0
- package/engine-shell/host/index.html +230 -0
- package/engine-shell/host/page.js +809 -0
- package/engine-shell/host/sandbox-panel.js +681 -0
- package/engine-shell/host/sandbox-scenarios.js +554 -0
- package/engine-shell/host/site-bridge.js +87 -0
- package/engine-shell/host/strings.js +916 -0
- package/engine-shell/package.json +1 -0
- package/engine-shell/verifiers/badcat/coin.tsv +2 -0
- package/engine-shell/verifiers/badcat/dice-2.tsv +11 -0
- package/engine-shell/verifiers/badcat/dice-5.tsv +26 -0
- package/engine-shell/verifiers/badcat/dice-9.tsv +46 -0
- package/engine-shell/verifiers/badcat/manifest.json +3344 -0
- package/engine-shell/verifiers/badcat/rps.tsv +3 -0
- package/engine-shell/verifiers/badcat/triple.tsv +66 -0
- package/engine-shell/verifiers/engine-verify.js +358 -0
- package/engine-shell/verifiers/package.json +5 -0
- package/engine-shell/verifiers/parity/manifest.json +27 -0
- package/engine-shell/verifiers/parity/wide.tsv +5 -0
- package/engine-shell/verifiers/parity-vectors.json +244 -0
- package/engine-shell/verifiers/vectors.json +100 -0
- package/package.json +10 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
body { margin: 0; background: #08141a; color: #F9F9FB; font: 500 14px/1.4 'Figtree', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
|
2
|
+
.dice { width: 100%; max-width: 560px; box-sizing: border-box; padding: 20px; display: grid; gap: 20px; }
|
|
3
|
+
.facts { display: flex; flex-wrap: wrap; gap: 16px; justify-content: space-between; font-size: 13px; color: #8F96A8; font-variant-numeric: tabular-nums; }
|
|
4
|
+
.facts b { color: #F9F9FB; font-weight: 700; font-size: 15px; }
|
|
5
|
+
.track { position: relative; height: 48px; background: #0b1f28; border: 1px solid #0D2A38; border-radius: 24px; }
|
|
6
|
+
.win { position: absolute; top: 0; bottom: 0; background: rgba(40, 243, 243, 0.22); border-left: 1px solid #28F3F3; border-right: 1px solid #28F3F3; border-radius: inherit; }
|
|
7
|
+
.scale { position: absolute; inset: 100% 12px auto 12px; padding-top: 6px; display: flex; justify-content: space-between; color: #8F96A8; font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
8
|
+
.marker { position: absolute; top: 50%; left: 22px; transform: translate(-50%, -50%); min-width: 44px; height: 36px; padding: 0 10px; display: grid; place-items: center; border-radius: 18px; background: #28F3F3; color: #041015; font-weight: 700; font-size: 15px; font-variant-numeric: tabular-nums; box-shadow: 0 6px 20px rgba(0,0,0,.4); transition: left 620ms cubic-bezier(.2,.8,.2,1); }
|
|
9
|
+
.marker.win { background: #58e879; }
|
|
10
|
+
.marker.lose { background: #FA2E5D; color: #F9F9FB; }
|
|
11
|
+
.pick { display: grid; gap: 12px; }
|
|
12
|
+
.side { display: inline-flex; padding: 3px; gap: 3px; background: rgba(9,102,125,0.15); border-radius: 8px; justify-self: start; }
|
|
13
|
+
.side button { height: 32px; padding: 0 14px; border: 0; border-radius: 6px; background: transparent; color: #8F96A8; font: inherit; font-weight: 600; cursor: pointer; }
|
|
14
|
+
.side button[aria-pressed="true"] { background: #0D2A38; color: #F9F9FB; }
|
|
15
|
+
.target { display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: center; }
|
|
16
|
+
.target input[type="range"] { width: 100%; accent-color: #28F3F3; }
|
|
17
|
+
.target output { color: #F9F9FB; font-weight: 700; font-variant-numeric: tabular-nums; min-width: 28px; text-align: right; }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Dice: roll under or over N on 0–99. The zone picks the bet by setting
|
|
2
|
+
// game.bet; /engine-sdk/ui.js draws the panel around it.
|
|
3
|
+
import { mount } from '/engine-sdk/ui.js';
|
|
4
|
+
|
|
5
|
+
const el = document.getElementById('game');
|
|
6
|
+
const marker = document.getElementById('marker');
|
|
7
|
+
const winBand = document.getElementById('win');
|
|
8
|
+
const mult = document.getElementById('mult');
|
|
9
|
+
const chance = document.getElementById('chance');
|
|
10
|
+
const maxwin = document.getElementById('maxwin');
|
|
11
|
+
const target = document.getElementById('target');
|
|
12
|
+
const val = document.getElementById('val');
|
|
13
|
+
const sides = el.querySelectorAll('[data-side]');
|
|
14
|
+
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
15
|
+
const pct = (x) => (x / 99 * 100) + '%';
|
|
16
|
+
|
|
17
|
+
let side = 'under';
|
|
18
|
+
let n = 50;
|
|
19
|
+
|
|
20
|
+
const game = await mount(el, {
|
|
21
|
+
async reveal(round) {
|
|
22
|
+
// The probe (probe.js) reads the clocks again at every round.
|
|
23
|
+
document.dispatchEvent(new CustomEvent('probe-round'));
|
|
24
|
+
const roll = round.data[0].roll;
|
|
25
|
+
marker.classList.remove('win', 'lose');
|
|
26
|
+
marker.textContent = '?';
|
|
27
|
+
marker.style.transition = 'none';
|
|
28
|
+
marker.style.left = '22px';
|
|
29
|
+
void marker.offsetWidth;
|
|
30
|
+
marker.style.transition = '';
|
|
31
|
+
await wait(40);
|
|
32
|
+
marker.style.left = `clamp(22px, ${pct(roll)}, calc(100% - 22px))`; // the marker stays on the track at 0 and 99
|
|
33
|
+
await wait(620);
|
|
34
|
+
marker.textContent = String(roll);
|
|
35
|
+
marker.classList.add(round.payout > 0 ? 'win' : 'lose');
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const { engine, field } = game;
|
|
39
|
+
const money = engine.money;
|
|
40
|
+
// The pairing this frame plays under: its width and the money the game was given (travel.browser.test.mjs holds a variant's to one the frame-size run plays).
|
|
41
|
+
console.error('clock-probe display ' + JSON.stringify({ w: innerWidth, isFiat: money.isFiat, unit: money.unit }));
|
|
42
|
+
const bets = new Map(engine.config.modes[0].bets.map((b) => [b.id, b]));
|
|
43
|
+
const range = () => (side === 'under' ? [0, n - 1] : [n + 1, 99]);
|
|
44
|
+
|
|
45
|
+
function paint() {
|
|
46
|
+
val.textContent = String(n);
|
|
47
|
+
const [lo, hi] = range();
|
|
48
|
+
winBand.style.left = pct(lo);
|
|
49
|
+
winBand.style.width = pct(hi - lo);
|
|
50
|
+
chance.textContent = String(hi - lo + 1);
|
|
51
|
+
const bet = bets.get(`${side}-${n}`);
|
|
52
|
+
const m = bet.maxMultBp / 10000;
|
|
53
|
+
mult.textContent = m.toFixed(2) + '×';
|
|
54
|
+
maxwin.textContent = money.sign + money.format(money.payout(field.amount, m)) + (money.sign ? '' : ' ' + money.unit);
|
|
55
|
+
game.bet = bet.id;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const s of sides) s.addEventListener('click', () => {
|
|
59
|
+
side = s.dataset.side;
|
|
60
|
+
for (const o of sides) o.setAttribute('aria-pressed', String(o === s));
|
|
61
|
+
paint();
|
|
62
|
+
});
|
|
63
|
+
target.addEventListener('input', () => { n = +target.value; paint(); });
|
|
64
|
+
field.addEventListener('change', paint);
|
|
65
|
+
paint();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
|
+
<title>Clock probe</title>
|
|
7
|
+
<link rel="stylesheet" href="./game.css">
|
|
8
|
+
<script type="module" src="/engine-sdk/bet-field.js"></script>
|
|
9
|
+
<script type="module" src="./game.js"></script>
|
|
10
|
+
<script src="./probe.js"></script>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<hb-game id="game">
|
|
14
|
+
<div slot="zone" class="dice" aria-live="polite">
|
|
15
|
+
<div class="facts">
|
|
16
|
+
<span>Payout <b id="mult">2.00×</b></span>
|
|
17
|
+
<span>Chance <b id="chance">50</b>%</span>
|
|
18
|
+
<span>Max win <b id="maxwin">—</b></span>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="track" id="track">
|
|
21
|
+
<div class="win" id="win"></div>
|
|
22
|
+
<div class="scale"><span>0</span><span>25</span><span>50</span><span>75</span><span>99</span></div>
|
|
23
|
+
<div class="marker" id="marker">?</div>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="pick">
|
|
26
|
+
<div class="side" role="tablist">
|
|
27
|
+
<button type="button" data-side="under" aria-pressed="true">Under</button>
|
|
28
|
+
<button type="button" data-side="over">Over</button>
|
|
29
|
+
</div>
|
|
30
|
+
<label class="target"><input id="target" type="range" min="2" max="98" step="1" value="50" aria-label="target"><output id="val">50</output></label>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</hb-game>
|
|
34
|
+
</body>
|
|
35
|
+
</html>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// A test bundle's worker (probe.js): asked once, it reads every clock a worker
|
|
2
|
+
// has, what its Date, timers, performance and File look like, where it runs
|
|
3
|
+
// from, and what a worker of its own reads, and answers with all of it. The
|
|
4
|
+
// same text runs as a same-origin script and as a blob. Every delta is a
|
|
5
|
+
// number (ms), never a verdict: the page writes it fixed-width and the test
|
|
6
|
+
// holds it to a threshold.
|
|
7
|
+
const str = (f) => { try { return Function.prototype.toString.call(f); } catch { return 'not a function'; } };
|
|
8
|
+
const suspicious = (names) => names.filter((k) => /^(__|\$cdc|_pw|cdc_)|pw|playwright|puppeteer|selenium|webdriver|clock|travel|driver|automation|headless/i.test(k)).sort();
|
|
9
|
+
const depth = (s) => String(s || '').split('\n').filter((l) => /^\s+at /.test(l)).length;
|
|
10
|
+
// What the worker's clocks read when it started: a browser keeps a worker's
|
|
11
|
+
// timeOrigin, and so Date.now() - performance.now(), for the worker's life.
|
|
12
|
+
const origin0 = performance.timeOrigin, delta0 = Date.now() - performance.now();
|
|
13
|
+
// Real work (probe.js burn/SPIN): a busy loop calibrated here, outside any
|
|
14
|
+
// timer, by doubling its count until it takes six milliseconds or more as it
|
|
15
|
+
// then runs, spun inside a timer callback when asked.
|
|
16
|
+
const burn = (n) => { let x = 0; for (let i = 0; i < n; i++) x += i & 1; return x; };
|
|
17
|
+
const SPIN = (() => { let n = 1e5; for (let i = 0; i < 16; i++) { const p = performance.now(); burn(n); if (performance.now() - p >= 6) break; n *= 2; } return n; })();
|
|
18
|
+
self.onmessage = async () => {
|
|
19
|
+
const nested = new Worker(URL.createObjectURL(new Blob(['postMessage(Date.now())'], { type: 'text/javascript' })));
|
|
20
|
+
const nestedNow = await new Promise((r) => { nested.onmessage = (e) => r(e.data); });
|
|
21
|
+
nested.terminate();
|
|
22
|
+
const dtf = new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', hourCycle: 'h23' });
|
|
23
|
+
const stack = await new Promise((r) => setTimeout(() => r(depth(new Error('x').stack)), 0));
|
|
24
|
+
// A scheduler task's 300 ms delay fires when a 300 ms setTimeout armed with it does.
|
|
25
|
+
const fired = await Promise.all([new Promise((r) => setTimeout(() => r([Date.now(), performance.now()]), 300)), scheduler.postTask(() => [Date.now(), performance.now()], { delay: 300 })]);
|
|
26
|
+
const post = Math.max(Math.abs(fired[1][0] - fired[0][0]), Math.abs(fired[1][1] - fired[0][1]));
|
|
27
|
+
// Real work inside a timer callback: performance.now() moves across it (the smallest of three callbacks).
|
|
28
|
+
const spins = [];
|
|
29
|
+
for (let i = 0; i < 3; i++) spins.push(await new Promise((r) => setTimeout(() => { const p = performance.now(); burn(SPIN); r(performance.now() - p); }, 0)));
|
|
30
|
+
postMessage({
|
|
31
|
+
now: Date.now(), perfTime: performance.timeOrigin + performance.now(), loc: location.protocol, nestedNow,
|
|
32
|
+
originMoved: performance.timeOrigin - origin0, deltaMoved: (Date.now() - performance.now()) - delta0, post, spin: Math.min(...spins),
|
|
33
|
+
other: { postName: scheduler.postTask.name, postStr: str(scheduler.postTask), abortName: AbortSignal.timeout.name, abortStr: str(AbortSignal.timeout), waitName: Atomics.waitAsync.name, waitStr: str(Atomics.waitAsync), ric: typeof requestIdleCallback, raf: typeof requestAnimationFrame, perfOwn: Object.getOwnPropertyDescriptor(self, 'performance') === undefined },
|
|
34
|
+
date: { name: Date.name, len: Date.length, own: Object.getOwnPropertyNames(Date), isFake: 'isFake' in Date, str: str(Date), nowStr: str(Date.now), ctor: new Date().constructor === Date, inst: new Date() instanceof Date },
|
|
35
|
+
timers: { name: setTimeout.name, len: setTimeout.length, str: str(setTimeout), small: (() => { const t = setTimeout(() => {}, 0); clearTimeout(t); return t < 1e6; })() },
|
|
36
|
+
perf: { own: Object.getOwnPropertyNames(performance), inst: performance instanceof Performance, originStr: str(Object.getOwnPropertyDescriptor(Performance.prototype, 'timeOrigin').get), toJSON: performance.toJSON().timeOrigin === performance.timeOrigin, markStr: str(performance.mark) },
|
|
37
|
+
file: { name: File.name, str: str(File), protoCtor: new File([''], 'x').constructor === File, dated: new File([''], 'x', { lastModified: 5 }).lastModified === 5 },
|
|
38
|
+
globals: suspicious(Object.getOwnPropertyNames(self)),
|
|
39
|
+
intl: Date.parse(dtf.format()) - Date.parse(dtf.format(new Date())),
|
|
40
|
+
temporal: typeof Temporal === 'object' ? Temporal.Now.instant().epochMilliseconds - Date.now() : 0,
|
|
41
|
+
eventTs: new Event('x').timeStamp - performance.now(),
|
|
42
|
+
fileLM: new File([''], 'x').lastModified - Date.now(),
|
|
43
|
+
mark: performance.mark('probe').startTime - performance.now(),
|
|
44
|
+
stack,
|
|
45
|
+
});
|
|
46
|
+
};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// A test bundle's probe (travel.browser.test.mjs): everything a game could read
|
|
2
|
+
// of its clocks and its surroundings to tell a time-travel run from a plain
|
|
3
|
+
// run, written to the console as error lines the run records (consoleErrors).
|
|
4
|
+
// Every number in a report is written as its size alone, eight digits wide, so a
|
|
5
|
+
// report that differs from another run's only in a measurement is the same
|
|
6
|
+
// text to the run's eye (travel.mjs shape: digits are #) and breaks into the
|
|
7
|
+
// same chunks; only a difference of shape (a name, a descriptor, a property, a
|
|
8
|
+
// global) is a new console error that fails the variant. Four kinds of line:
|
|
9
|
+
// clock-probe shape NNN/NNN <chunk> what the clocks and the scope look like
|
|
10
|
+
// clock-probe boot NNN/NNN <chunk> the deltas between clocks at boot, in
|
|
11
|
+
// the page and in its workers (a
|
|
12
|
+
// same-origin script, a blob, a worker's
|
|
13
|
+
// own worker)
|
|
14
|
+
// clock-probe walk NNN/NNN <chunk> the same oracles read by a timer due
|
|
15
|
+
// twenty minutes in: inside the walk
|
|
16
|
+
// clock-probe round <n> k=v … the oracles read at every round, two-letter
|
|
17
|
+
// keys so the line fits the run's 160 characters
|
|
18
|
+
// clock-probe roundb <n> k=v … the rest of them (one line holds ten)
|
|
19
|
+
// clock-probe display {…} the frame's width and the money the game
|
|
20
|
+
// was given (the pairing a variant plays under)
|
|
21
|
+
// The oracles are the readings a fake clock does not reach by itself: a File's
|
|
22
|
+
// lastModified against Date.now(), document.lastModified against the page's
|
|
23
|
+
// Last-Modified header, a performance mark's startTime against
|
|
24
|
+
// performance.now(), a fetched resource's responseEnd against the
|
|
25
|
+
// performance.now() read before and after the fetch, a running animation's
|
|
26
|
+
// startTime and currentTime against document.timeline.currentTime, 300 ms of
|
|
27
|
+
// performance.now() against the animation's own progress, a scheduler task's
|
|
28
|
+
// 300 ms delay and a 300 ms AbortSignal.timeout against a 300 ms setTimeout
|
|
29
|
+
// armed with them, an animation frame's timestamp against the timeline
|
|
30
|
+
// inside the callback, the last instant this origin's storage saw against
|
|
31
|
+
// Date.now() (a clock that ran backwards between visits), the stack depth
|
|
32
|
+
// inside a setTimeout and a requestAnimationFrame callback, how far
|
|
33
|
+
// performance.now() and Date.now() move across six milliseconds or more of
|
|
34
|
+
// real work inside a setTimeout callback and inside an animation frame's (a
|
|
35
|
+
// clock that stands still inside the callbacks a jump fires reads zero there;
|
|
36
|
+
// a browser's never does), and the same-origin worker's Date.now() against the
|
|
37
|
+
// page's, its performance.timeOrigin and Date.now() - performance.now()
|
|
38
|
+
// against their first readings, its own postTask delay and its own spin.
|
|
39
|
+
(async () => {
|
|
40
|
+
const str = (f) => { try { return Function.prototype.toString.call(f); } catch { return 'not a function'; } };
|
|
41
|
+
const desc = (o, k) => { try { const d = Object.getOwnPropertyDescriptor(o, k); return d ? [typeof d.value, typeof d.get, typeof d.set, d.writable, d.enumerable, d.configurable].join('/') : 'none'; } catch { return 'error'; } };
|
|
42
|
+
const owner = (o, k) => { for (let p = o; p; p = Object.getPrototypeOf(p)) if (Object.getOwnPropertyDescriptor(p, k)) return p; return null; };
|
|
43
|
+
const suspicious = (names) => names.filter((k) => /^(__|\$cdc|_pw|cdc_)|pw|playwright|puppeteer|selenium|webdriver|clock|travel|driver|automation|headless/i.test(k)).sort();
|
|
44
|
+
// Storage is the game's own (carried from one variant to the next by design): only a key a harness would plant counts.
|
|
45
|
+
const planted = (names) => names.filter((k) => /^(__|\$cdc|_pw|cdc_|pw_)|playwright|puppeteer|selenium|webdriver/i.test(k)).sort();
|
|
46
|
+
const own = (o) => Object.getOwnPropertyNames(o);
|
|
47
|
+
const abs = (v) => Math.abs(Math.round(v));
|
|
48
|
+
// A number as the report writes it: its size, eight digits, zero-padded (a delta past 99,999,999 ms is clamped: still plainly not a second); anything that is not a finite number says so.
|
|
49
|
+
const fixed = (v) => (typeof v === 'number' ? (Number.isFinite(v) ? String(Math.min(abs(v), 99999999)).padStart(8, '0') : 'nan') : v);
|
|
50
|
+
const say = (tag, o) => {
|
|
51
|
+
const text = JSON.stringify(o, (k, v) => fixed(v));
|
|
52
|
+
const size = 120, chunks = Math.ceil(text.length / size); // the run keeps a console line to 160 characters
|
|
53
|
+
for (let i = 0; i < chunks; i++) console.error(`clock-probe ${tag} ${String(i + 1).padStart(3, '0')}/${String(chunks).padStart(3, '0')} ${text.slice(i * size, (i + 1) * size)}`);
|
|
54
|
+
};
|
|
55
|
+
// The stack depth inside a callback: frames below the Error line (production: the callback alone, one). The Error is made in the callback itself, so no helper's frame counts.
|
|
56
|
+
const depth = (s) => String(s || '').split('\n').filter((l) => /^\s+at /.test(l)).length;
|
|
57
|
+
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
58
|
+
// Real work: a busy loop of `n` iterations, never bounded by a clock (a
|
|
59
|
+
// clock that stood still would never end it). Calibrated once here, outside
|
|
60
|
+
// any timer, where performance.now() flows at real pace in every run: the
|
|
61
|
+
// count is doubled until the loop itself takes six milliseconds or more,
|
|
62
|
+
// measured on the loop as it then runs (warmed by the doubling, so a loop
|
|
63
|
+
// the engine later optimises does not come out a fraction of the time
|
|
64
|
+
// measured cold); the same function spins in the callbacks, so a reading
|
|
65
|
+
// under a millisecond there is the clock's doing, never the loop's.
|
|
66
|
+
const burn = (n) => { let x = 0; for (let i = 0; i < n; i++) x += i & 1; return x; };
|
|
67
|
+
const SPIN = (() => { let n = 1e5; for (let i = 0; i < 16; i++) { const p = performance.now(); burn(n); if (performance.now() - p >= 6) break; n *= 2; } return n; })();
|
|
68
|
+
const header = (await fetch(location.href)).headers.get('last-modified');
|
|
69
|
+
|
|
70
|
+
// ---- the shape: deterministic, the same in every run of the same browser.
|
|
71
|
+
const fp = {};
|
|
72
|
+
fp.globals = suspicious(own(window));
|
|
73
|
+
fp.symbols = Object.getOwnPropertySymbols(window).length === 0;
|
|
74
|
+
fp.navigator = { webdriver: navigator.webdriver, plugins: navigator.plugins.length > 0, ua: navigator.userAgent, uaData: !!navigator.userAgentData, headless: /headless/i.test(navigator.userAgent) };
|
|
75
|
+
fp.location = { search: location.search, hash: location.hash, name: window.name, referrer: document.referrer, cookie: document.cookie, ls: planted(Object.keys(localStorage)), ss: planted(Object.keys(sessionStorage)), lastModifiedHeader: typeof header === 'string' && header.length > 20 };
|
|
76
|
+
const D = Date;
|
|
77
|
+
fp.date = { name: D.name, len: D.length, own: own(D), isFake: 'isFake' in D, str: str(D), nowName: D.now.name, nowStr: str(D.now), desc: desc(window, 'Date'), protoCtor: new D().constructor === D, protoCtorDesc: desc(D.prototype, 'constructor'), inst: new D() instanceof D, tag: Object.prototype.toString.call(new D()), callStr: typeof D() === 'string', utc: D.UTC(2027, 0, 1) === 1798761600000,
|
|
78
|
+
sub: (() => { try { class X extends D { x() { return 1; } } const x = new X(); return x.x() === 1 && x instanceof X && x instanceof D && typeof x.getTime() === 'number'; } catch { return false; } })() };
|
|
79
|
+
fp.timers = {};
|
|
80
|
+
for (const k of ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'requestAnimationFrame', 'cancelAnimationFrame', 'requestIdleCallback', 'cancelIdleCallback']) { const f = window[k]; fp.timers[k] = typeof f === 'function' ? { name: f.name, len: f.length, str: str(f), desc: desc(window, k) } : typeof f; }
|
|
81
|
+
const t1 = setTimeout(() => {}, 0), t2 = setTimeout(() => {}, 0); clearTimeout(t1); clearTimeout(t2);
|
|
82
|
+
const i1 = setInterval(() => {}, 1000); clearInterval(i1);
|
|
83
|
+
const r1 = requestAnimationFrame(() => {}); cancelAnimationFrame(r1);
|
|
84
|
+
fp.timerIds = { small: t2 < 1e6 && r1 < 1e6 && i1 < 1e6, seq: t2 === t1 + 1 && i1 === t2 + 1, frameFromOne: r1 === 1, unknownClears: clearTimeout(123456) === undefined };
|
|
85
|
+
fp.perf = { inst: performance instanceof Performance, proto: Object.getPrototypeOf(performance) === Performance.prototype, own: own(performance), desc: desc(window, 'performance'), nowName: performance.now.name, nowStr: str(performance.now), nowDesc: desc(Performance.prototype, 'now'), originDesc: desc(Performance.prototype, 'timeOrigin'), originStr: str(Object.getOwnPropertyDescriptor(Performance.prototype, 'timeOrigin').get), markName: performance.mark.name, markStr: str(performance.mark), measureStr: str(performance.measure), entryStartDesc: desc(PerformanceEntry.prototype, 'startTime'), entryStartStr: str(Object.getOwnPropertyDescriptor(PerformanceEntry.prototype, 'startTime').get), resourceEndDesc: desc(PerformanceResourceTiming.prototype, 'responseEnd'), timing: Math.abs(performance.timing.navigationStart - performance.timeOrigin) < 60000 };
|
|
86
|
+
const dtf = new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', hourCycle: 'h23' });
|
|
87
|
+
fp.intl = { own: own(Intl).sort(), dtfName: Intl.DateTimeFormat.name, dtfStr: str(Intl.DateTimeFormat), inst: dtf instanceof Intl.DateTimeFormat, instOwn: own(dtf), formatSame: dtf.format === dtf.format, formatDesc: desc(Intl.DateTimeFormat.prototype, 'format'), formatStr: str(dtf.format), partsStr: str(dtf.formatToParts), static: typeof Intl.DateTimeFormat.supportedLocalesOf, resolved: typeof dtf.resolvedOptions().timeZone };
|
|
88
|
+
fp.event = { tsDesc: desc(Event.prototype, 'timeStamp'), tsStr: str(Object.getOwnPropertyDescriptor(Event.prototype, 'timeStamp').get) };
|
|
89
|
+
const tlOwner = owner(document.timeline, 'currentTime');
|
|
90
|
+
fp.timeline = { owner: tlOwner && tlOwner.constructor && tlOwner.constructor.name, str: tlOwner ? str(Object.getOwnPropertyDescriptor(tlOwner, 'currentTime').get) : 'none' };
|
|
91
|
+
fp.animation = { startDesc: desc(Animation.prototype, 'startTime'), startStr: str(Object.getOwnPropertyDescriptor(Animation.prototype, 'startTime').get), currentDesc: desc(Animation.prototype, 'currentTime'), currentStr: str(Object.getOwnPropertyDescriptor(Animation.prototype, 'currentTime').get) };
|
|
92
|
+
fp.file = { name: File.name, len: File.length, str: str(File), desc: desc(window, 'File'), protoCtor: new File([''], 'x').constructor === File, inst: new File([''], 'x') instanceof File, blobCtor: new Blob(['']).constructor === Blob, dated: new File([''], 'x', { lastModified: 5 }).lastModified === 5 };
|
|
93
|
+
fp.temporal = typeof Temporal === 'object' ? { instantStr: str(Temporal.Now.instant), instantName: Temporal.Now.instant.name, zonedStr: str(Temporal.Now.zonedDateTimeISO), own: own(Temporal.Now).sort() } : typeof Temporal;
|
|
94
|
+
fp.other = { postName: scheduler.postTask.name, postLen: scheduler.postTask.length, postStr: str(scheduler.postTask), postDesc: desc(Object.getPrototypeOf(scheduler), 'postTask'), schedOwn: own(scheduler), yieldStr: str(scheduler.yield), abortName: AbortSignal.timeout.name, abortStr: str(AbortSignal.timeout), abortDesc: desc(AbortSignal, 'timeout'), abortSignal: AbortSignal.timeout(1000) instanceof AbortSignal, waitStr: str(Atomics.waitAsync), waitName: Atomics.waitAsync.name, waitLen: Atomics.waitAsync.length };
|
|
95
|
+
say('shape', fp);
|
|
96
|
+
|
|
97
|
+
// ---- the oracles, read at boot, in the walk and at every round.
|
|
98
|
+
const parse = (parts) => { const g = (t) => Number(parts.find((p) => p.type === t).value); return Date.UTC(g('year'), g('month') - 1, g('day'), g('hour') % 24, g('minute'), g('second')); };
|
|
99
|
+
const read = async (tag) => {
|
|
100
|
+
const d = {};
|
|
101
|
+
d.fileLM = new File([''], 'x').lastModified - Date.now();
|
|
102
|
+
d.docLM = Date.parse(document.lastModified) - Date.parse(header);
|
|
103
|
+
d.mark = performance.mark('probe-' + tag).startTime - performance.now();
|
|
104
|
+
// A running animation: where it is against the timeline, and its pace against performance.now() over 300 ms of this clock.
|
|
105
|
+
// Longer than any jump the walk makes (a day and a half an hour), so
|
|
106
|
+
// the animation never finishes and its progress tracks the clock across
|
|
107
|
+
// an hourly leap; a short animation would cap at its end mid-jump (as it
|
|
108
|
+
// would in real time) and read as a difference the clock did not cause.
|
|
109
|
+
const a = document.documentElement.animate([{ opacity: 1 }, { opacity: 1 }], 100 * 365 * 24 * 3600 * 1000);
|
|
110
|
+
await a.ready;
|
|
111
|
+
d.anim = document.timeline.currentTime - a.startTime - a.currentTime;
|
|
112
|
+
// A fetched resource's responseEnd lies between the clock read before
|
|
113
|
+
// the fetch and the clock read after it (an entry on another clock than
|
|
114
|
+
// the page's falls outside by their difference). Read as a window, not
|
|
115
|
+
// against one later reading: the response ends in one task and is read
|
|
116
|
+
// in a later one, and a jump of the clock between the two (the walk's
|
|
117
|
+
// leaps are posted from outside) would otherwise read as the jump.
|
|
118
|
+
const url = new URL('./hbtest.json?probe=' + tag + '-' + Math.random().toString(36).slice(2), location.href).href;
|
|
119
|
+
const p1 = performance.now();
|
|
120
|
+
await (await fetch(url)).text();
|
|
121
|
+
const p2 = performance.now();
|
|
122
|
+
const e = performance.getEntriesByName(url).at(-1);
|
|
123
|
+
d.res = e ? Math.max(0, p1 - e.responseEnd, e.responseEnd - p2) : NaN;
|
|
124
|
+
const c0 = a.currentTime, p0 = performance.now();
|
|
125
|
+
await wait(300);
|
|
126
|
+
d.pace = (performance.now() - p0) - (a.currentTime - c0);
|
|
127
|
+
a.cancel();
|
|
128
|
+
d.stack = await new Promise((r) => setTimeout(() => r(depth(new Error('x').stack)), 0));
|
|
129
|
+
d.rafStack = await new Promise((r) => requestAnimationFrame(() => r(depth(new Error('x').stack))));
|
|
130
|
+
// The other timers: a scheduler task's delay and a signal's timeout fire
|
|
131
|
+
// when a setTimeout of the same delay armed at the same instant does
|
|
132
|
+
// (a machine that sleeps delays all three alike; a clock that drives
|
|
133
|
+
// one and not the others pulls them apart).
|
|
134
|
+
const fired = await Promise.all([
|
|
135
|
+
new Promise((r) => setTimeout(() => r([Date.now(), performance.now()]), 300)),
|
|
136
|
+
scheduler.postTask(() => [Date.now(), performance.now()], { delay: 300 }),
|
|
137
|
+
new Promise((r) => AbortSignal.timeout(300).addEventListener('abort', () => r([Date.now(), performance.now()]), { once: true })),
|
|
138
|
+
]);
|
|
139
|
+
d.post = Math.max(Math.abs(fired[1][0] - fired[0][0]), Math.abs(fired[1][1] - fired[0][1]));
|
|
140
|
+
d.abort = Math.max(Math.abs(fired[2][0] - fired[0][0]), Math.abs(fired[2][1] - fired[0][1]));
|
|
141
|
+
// Inside an animation frame its timestamp is the timeline's reading.
|
|
142
|
+
d.rafTs = await new Promise((r) => requestAnimationFrame((ts) => r(ts - document.timeline.currentTime)));
|
|
143
|
+
// The last instant this origin's storage saw is never ahead of the clock (a visit is remembered each time).
|
|
144
|
+
d.back = (() => { try { const seen = Number(localStorage.getItem('probe-seen') || 0), now = Date.now(); localStorage.setItem('probe-seen', String(Math.max(seen, now))); return Math.max(0, seen - now); } catch { return NaN; } })();
|
|
145
|
+
// Real work inside a timer callback and inside an animation frame's:
|
|
146
|
+
// performance.now() and Date.now() move across it in any browser. The
|
|
147
|
+
// smallest of three timer callbacks, so the reading is the clock's
|
|
148
|
+
// behaviour and not one lucky callback's.
|
|
149
|
+
const spins = [];
|
|
150
|
+
for (let i = 0; i < 3; i++) spins.push(await new Promise((r) => setTimeout(() => { const p = performance.now(), t = Date.now(); burn(SPIN); r([performance.now() - p, Date.now() - t]); }, 0)));
|
|
151
|
+
d.spinT = Math.min(...spins.map((s) => s[0]));
|
|
152
|
+
d.spinD = Math.min(...spins.map((s) => s[1]));
|
|
153
|
+
d.spinF = await new Promise((r) => requestAnimationFrame(() => { const p = performance.now(); burn(SPIN); r(performance.now() - p); }));
|
|
154
|
+
return d;
|
|
155
|
+
};
|
|
156
|
+
const boot = await read('boot');
|
|
157
|
+
boot.perfVsDate = Date.now() - (performance.timeOrigin + performance.now());
|
|
158
|
+
boot.intlFormat = Date.parse(dtf.format()) - Date.parse(dtf.format(new Date()));
|
|
159
|
+
boot.intlParts = parse(dtf.formatToParts()) - parse(dtf.formatToParts(new Date()));
|
|
160
|
+
boot.eventTs = new Event('x').timeStamp - performance.now();
|
|
161
|
+
boot.toJSON = performance.toJSON().timeOrigin - performance.timeOrigin;
|
|
162
|
+
boot.temporal = typeof Temporal === 'object' ? Temporal.Now.instant().epochMilliseconds - Date.now() : 0;
|
|
163
|
+
boot.temporalZoned = typeof Temporal === 'object' ? Temporal.Now.zonedDateTimeISO('UTC').epochMilliseconds - Date.now() : 0;
|
|
164
|
+
boot.temporalPlain = typeof Temporal === 'object' ? Date.parse(Temporal.Now.plainDateTimeISO('UTC').toString() + 'Z') - Date.now() : 0;
|
|
165
|
+
boot.frame = await new Promise((r) => requestAnimationFrame((ts) => r(Math.max(Math.abs(document.timeline.currentTime - performance.now()), Math.abs(ts - performance.now())))));
|
|
166
|
+
const d0 = Date.now(), p0 = performance.now();
|
|
167
|
+
await wait(300);
|
|
168
|
+
boot.pacingWait = Date.now() - d0;
|
|
169
|
+
boot.pacing = (Date.now() - d0) - (performance.now() - p0);
|
|
170
|
+
// A mark made now, read back after the clock has moved (the walk, the rounds), must keep its time; and a mark made then must read as the clock then reads (keptNow).
|
|
171
|
+
const kept = performance.mark('probe-kept');
|
|
172
|
+
const keptAt = kept.startTime, keptNow = performance.now();
|
|
173
|
+
// The workers: the same text as a same-origin script and as a blob, each
|
|
174
|
+
// asked what it reads; a worker's own worker too.
|
|
175
|
+
const text = await (await fetch('./probe-worker.js')).text();
|
|
176
|
+
const sw = new Worker('./probe-worker.js');
|
|
177
|
+
const bw = new Worker(URL.createObjectURL(new Blob([text], { type: 'text/javascript' })));
|
|
178
|
+
const ask = (w) => new Promise((r) => { const h = (e) => { w.removeEventListener('message', h); r(e.data); }; w.addEventListener('message', h); w.postMessage('probe'); });
|
|
179
|
+
fp.workers = {};
|
|
180
|
+
boot.workers = {};
|
|
181
|
+
for (const [kind, w] of [['scriptWorker', sw], ['blobWorker', bw]]) {
|
|
182
|
+
const a = await ask(w);
|
|
183
|
+
fp.workers[kind] = { loc: a.loc, date: a.date, timers: a.timers, perf: a.perf, file: a.file, globals: a.globals, other: a.other };
|
|
184
|
+
boot.workers[kind] = { now: a.now - Date.now(), perf: a.perfTime - a.now, nested: a.nestedNow - Date.now(), eventTs: a.eventTs, intl: a.intl, temporal: a.temporal, fileLM: a.fileLM, mark: a.mark, stack: a.stack, post: a.post, om: a.originMoved, dm: a.deltaMoved, spin: a.spin };
|
|
185
|
+
}
|
|
186
|
+
say('shape-workers', fp.workers);
|
|
187
|
+
say('boot', boot);
|
|
188
|
+
// The same oracles inside the walk's first leap (a timer due twenty minutes in) …
|
|
189
|
+
setTimeout(async () => { const d = await read('walk'); d.kept = performance.getEntriesByName('probe-kept')[0].startTime - keptAt; d.keptNow = (performance.mark('probe-kept-walk').startTime - keptAt) - (performance.now() - keptNow); say('walk', d); }, 20 * 60 * 1000);
|
|
190
|
+
// … and at every round, with the page's clock against the same-origin worker's: one short line each.
|
|
191
|
+
let n = 0, busy = false;
|
|
192
|
+
document.addEventListener('probe-round', async () => {
|
|
193
|
+
n++;
|
|
194
|
+
if (busy) return;
|
|
195
|
+
busy = true;
|
|
196
|
+
const round = n;
|
|
197
|
+
try {
|
|
198
|
+
const d = await read('round' + round);
|
|
199
|
+
const a = await ask(sw);
|
|
200
|
+
d.worker = a.now - Date.now();
|
|
201
|
+
d.kept = performance.getEntriesByName('probe-kept')[0].startTime - keptAt;
|
|
202
|
+
// The same-origin worker's timeOrigin and Date.now() - performance.now() against its first readings, its own postTask delay and its own spin.
|
|
203
|
+
d.om = a.originMoved; d.dm = a.deltaMoved; d.wp = a.post; d.ws = a.spin;
|
|
204
|
+
const short = { fl: 'fileLM', dl: 'docLM', mk: 'mark', rs: 'res', an: 'anim', pc: 'pace', st: 'stack', rf: 'rafStack', wk: 'worker', kp: 'kept' };
|
|
205
|
+
const more = { rt: 'rafTs', po: 'post', ab: 'abort', bk: 'back', om: 'om', dm: 'dm', wp: 'wp', sp: 'spinT', sd: 'spinD', sf: 'spinF', ws: 'ws' };
|
|
206
|
+
console.error(`clock-probe round ${String(round).padStart(5, '0')} ${Object.keys(short).map((k) => `${k}=${fixed(d[short[k]])}`).join(' ')}`);
|
|
207
|
+
console.error(`clock-probe roundb ${String(round).padStart(5, '0')} ${Object.keys(more).map((k) => `${k}=${fixed(d[more[k]])}`).join(' ')}`);
|
|
208
|
+
} finally { busy = false; }
|
|
209
|
+
});
|
|
210
|
+
})();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
1 {"roll":0}
|
|
2
|
+
1 {"roll":1}
|
|
3
|
+
1 {"roll":2}
|
|
4
|
+
1 {"roll":3}
|
|
5
|
+
1 {"roll":4}
|
|
6
|
+
1 {"roll":5}
|
|
7
|
+
1 {"roll":6}
|
|
8
|
+
1 {"roll":7}
|
|
9
|
+
1 {"roll":8}
|
|
10
|
+
1 {"roll":9}
|
|
11
|
+
1 {"roll":10}
|
|
12
|
+
1 {"roll":11}
|
|
13
|
+
1 {"roll":12}
|
|
14
|
+
1 {"roll":13}
|
|
15
|
+
1 {"roll":14}
|
|
16
|
+
1 {"roll":15}
|
|
17
|
+
1 {"roll":16}
|
|
18
|
+
1 {"roll":17}
|
|
19
|
+
1 {"roll":18}
|
|
20
|
+
1 {"roll":19}
|
|
21
|
+
1 {"roll":20}
|
|
22
|
+
1 {"roll":21}
|
|
23
|
+
1 {"roll":22}
|
|
24
|
+
1 {"roll":23}
|
|
25
|
+
1 {"roll":24}
|
|
26
|
+
1 {"roll":25}
|
|
27
|
+
1 {"roll":26}
|
|
28
|
+
1 {"roll":27}
|
|
29
|
+
1 {"roll":28}
|
|
30
|
+
1 {"roll":29}
|
|
31
|
+
1 {"roll":30}
|
|
32
|
+
1 {"roll":31}
|
|
33
|
+
1 {"roll":32}
|
|
34
|
+
1 {"roll":33}
|
|
35
|
+
1 {"roll":34}
|
|
36
|
+
1 {"roll":35}
|
|
37
|
+
1 {"roll":36}
|
|
38
|
+
1 {"roll":37}
|
|
39
|
+
1 {"roll":38}
|
|
40
|
+
1 {"roll":39}
|
|
41
|
+
1 {"roll":40}
|
|
42
|
+
1 {"roll":41}
|
|
43
|
+
1 {"roll":42}
|
|
44
|
+
1 {"roll":43}
|
|
45
|
+
1 {"roll":44}
|
|
46
|
+
1 {"roll":45}
|
|
47
|
+
1 {"roll":46}
|
|
48
|
+
1 {"roll":47}
|
|
49
|
+
1 {"roll":48}
|
|
50
|
+
1 {"roll":49}
|
|
51
|
+
1 {"roll":50}
|
|
52
|
+
1 {"roll":51}
|
|
53
|
+
1 {"roll":52}
|
|
54
|
+
1 {"roll":53}
|
|
55
|
+
1 {"roll":54}
|
|
56
|
+
1 {"roll":55}
|
|
57
|
+
1 {"roll":56}
|
|
58
|
+
1 {"roll":57}
|
|
59
|
+
1 {"roll":58}
|
|
60
|
+
1 {"roll":59}
|
|
61
|
+
1 {"roll":60}
|
|
62
|
+
1 {"roll":61}
|
|
63
|
+
1 {"roll":62}
|
|
64
|
+
1 {"roll":63}
|
|
65
|
+
1 {"roll":64}
|
|
66
|
+
1 {"roll":65}
|
|
67
|
+
1 {"roll":66}
|
|
68
|
+
1 {"roll":67}
|
|
69
|
+
1 {"roll":68}
|
|
70
|
+
1 {"roll":69}
|
|
71
|
+
1 {"roll":70}
|
|
72
|
+
1 {"roll":71}
|
|
73
|
+
1 {"roll":72}
|
|
74
|
+
1 {"roll":73}
|
|
75
|
+
1 {"roll":74}
|
|
76
|
+
1 {"roll":75}
|
|
77
|
+
1 {"roll":76}
|
|
78
|
+
1 {"roll":77}
|
|
79
|
+
1 {"roll":78}
|
|
80
|
+
1 {"roll":79}
|
|
81
|
+
1 {"roll":80}
|
|
82
|
+
1 {"roll":81}
|
|
83
|
+
1 {"roll":82}
|
|
84
|
+
1 {"roll":83}
|
|
85
|
+
1 {"roll":84}
|
|
86
|
+
1 {"roll":85}
|
|
87
|
+
1 {"roll":86}
|
|
88
|
+
1 {"roll":87}
|
|
89
|
+
1 {"roll":88}
|
|
90
|
+
1 {"roll":89}
|
|
91
|
+
1 {"roll":90}
|
|
92
|
+
1 {"roll":91}
|
|
93
|
+
1 {"roll":92}
|
|
94
|
+
1 {"roll":93}
|
|
95
|
+
1 {"roll":94}
|
|
96
|
+
1 {"roll":95}
|
|
97
|
+
1 {"roll":96}
|
|
98
|
+
1 {"roll":97}
|
|
99
|
+
1 {"roll":98}
|
|
100
|
+
1 {"roll":99}
|