tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

commit 60e168ce0a8f5087930b0ab8e425e3b533f7753e
parent 181077afe2b6d7d6a7eb6edd4d57300624f92203
Author: James Eversole <james@eversole.co>
Date:   Fri, 23 Oct 2026 19:23:07 -0500

Adopt structured IO v2 across runtimes, compilers and applications

Implement scoped ownership, structured tasks, cancellation and explicit OS process authority; migrate native facilities, portable compiler storage, handler adapters, registry and browser terminal workflows. Current native/Zig/browser hosts execute canonical IO2 only; retain historical inspection, immutable releases, independent negative/security controls and archived v1 runtime paths. Separate owned-handle ABI3 from IO protocol2. Publish authenticated final compiler/support inputs and84 immutable0.7.0 releases, preserving497 earlier records. Freeze and retain original acceptance identities rather than relabelling evidence. Native/Zig fresh-store reproduction, program smokes, source freshness, bindings, registry, Node, dist and both browser backends passed; applicable acceptance rerun on this exact curated tree. Local staging only.

Diffstat:
MAGENTS.md | 4++++
Mbootstrap/build-stable.arboricx | 0
Abootstrap/compiler-source-map.io-v2.v1 | 32++++++++++++++++++++++++++++++++
Mbootstrap/compiler-source-map.v1 | 17++++++++++-------
Mbootstrap/rebuild/bootstrap-kit.json | 13+++++++------
Mbootstrap/rebuild/io-adapter.arboricx | 0
Mbootstrap/rebuild/rebuilt-compiler.arboricx | 0
Mbootstrap/stable-compiler-source-map.v1 | 17++++++++++-------
Mbootstrap/stable-compiler.json | 84++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mdemos/arboricx-inspection/main.tri | 64++++++++++++++++++----------------------------------------------
Mdemos/arboricx-inspection/tricu.workspace | 9+++++++++
Ademos/handlers/artifact-get.tri | 35+++++++++++++++++++++++++++++++++++
Ademos/handlers/main.tri | 13+++++++++++++
Mdemos/interaction-inspection.tri | 4++--
Mdemos/interactionTrees.tri | 81++++++++++++++++++++++++-------------------------------------------------------
Mdemos/interactionTrees/arboricxServer.tri | 41+++++++++++++++++------------------------
Mdemos/interactionTrees/echoServer.tri | 54+++++++++++++++++++++++++-----------------------------
Mdemos/interactionTrees/environment.tri | 40++++++++++++++++++++--------------------
Mdemos/interactionTrees/forkAwait.tri | 36++++++++++++++++++------------------
Mdemos/interactionTrees/getLineAsync.tri | 47+++++++++++++++++++++--------------------------
Mdemos/interactionTrees/greet.tri | 16++++++----------
Mdemos/interactionTrees/httpServer.tri | 48+++++++++++++++++++++++++++++++-----------------
Mdemos/interactionTrees/safeRead.tri | 31+++++++++++++++----------------
Mdemos/interactionTrees/shout.tri | 39++++++++++++++++-----------------------
Mdemos/interactionTrees/state.tri | 35+++++++++++++----------------------
Mdemos/interactionTrees/writeThenRead.tri | 40++++++++++++++++++++--------------------
Mdemos/interactionTrees/yield.tri | 66++++++++++++++++++++++++++++++++++--------------------------------
Ademos/io-v2/clock.tri | 32++++++++++++++++++++++++++++++++
Ademos/io-v2/context.tri | 5+++++
Ademos/io-v2/copy.tri | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/directory.tri | 46++++++++++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/indexed-read.tri | 16++++++++++++++++
Ademos/io-v2/indexed-write.tri | 44++++++++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/lines.tri | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/objects.tri | 14++++++++++++++
Ademos/io-v2/positioned.tri | 39+++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/process.tri | 38++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/publish.tri | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/registry.tri | 41+++++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/streams.tri | 31+++++++++++++++++++++++++++++++
Ademos/io-v2/tcp.tri | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ademos/io-v2/text.tri | 24++++++++++++++++++++++++
Ademos/package-inspector-v2/main.tri | 18++++++++++++++++++
Ademos/package-inspector-v2/tricu.project | 4++++
Mdemos/package-inspector/main.tri | 33++++++++++++++-------------------
Mdemos/package-inspector/tricu.project | 6+++---
Mdemos/runArboricxBundle.tri | 46++++++++++++++++++++--------------------------
Mdocs/build-and-run.md | 55+++++++++++++++++++++++++++++++++++++++++++------------
Mdocs/compiler-rebuilding.md | 196++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mdocs/compiler.md | 37++++++++++++++++++-------------------
Mdocs/contracts.md | 297++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mdocs/host-abi.md | 90+++++++++++++++++++++++++++++++++++--------------------------------------------
Mdocs/language-features.md | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mdocs/language.md | 48+++++++++++++++++++++---------------------------
Mdocs/modules.md | 39++++++++++++++++++++++++++++-----------
Mdocs/parser.md | 8++++----
Mdocs/storage.md | 10+++++++---
Mdocs/tri-tools.md | 100+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mdocs/validation.md | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mext/demo-site/README.md | 368++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Aext/demo-site/bundles/00ebc0925d110a0733709f2aac0dedf4de7b59f33ac3eee107965496806cd77d.arboricx | 0
Aext/demo-site/bundles/035a709a17feb361abac0844c73f9b5f3c664549e5d81567963c3193816f9808.arboricx | 0
Aext/demo-site/bundles/070c4d26248d75ce9d1d66c19a4a7ad92558b9e14a837791b72f50e07afc5463.arboricx | 0
Aext/demo-site/bundles/08e1b5eb98d8314fdea7216819f0481df4354ee7a56192e692828f60e2b759a2.arboricx | 0
Aext/demo-site/bundles/09d6d5f32aa2f27b2d1864e7ee19955df8692190821072b67bbf75d3babe146a.arboricx | 0
Aext/demo-site/bundles/0a5d5d83f73a91959f8ff34a45643dae056456817c22569879e4675caf461086.arboricx | 0
Aext/demo-site/bundles/0ad0c17feaf6a8241ade01283210a1cded009eb4dd1cf99adcd31ea44017afba.arboricx | 0
Aext/demo-site/bundles/0ce352486ef2b94dc5aed1a40d5339ab0e90c41bb98e9959e26bcc2fb1e7590a.arboricx | 0
Aext/demo-site/bundles/1572e500aef32b7e069ecc985778f56cb5d4914ce99221d328817e2f10e168e8.arboricx | 0
Aext/demo-site/bundles/15c3b172ba505e4904ced400deca1e6fdadeac1734b732191291c92ff8d245f5.arboricx | 0
Aext/demo-site/bundles/16de0d621776b79ff676b9415405b19e3088a2409d51f2efac850acca249b119.arboricx | 0
Aext/demo-site/bundles/18b24788592f74b9c564c30214dff0231cb86338661d944ab697e7ae4ca1103e.arboricx | 0
Aext/demo-site/bundles/1a1f3ea6c15e88e5929f151df64687a3dcb28b2d5f2091acb89c39081d4fe28d.arboricx | 0
Aext/demo-site/bundles/1bced95d1d42116194b26c53779cc68900fd7c03263266603966243ae91f8545.arboricx | 0
Aext/demo-site/bundles/1e053fdb9d77ecbebd9c1d8b861dccae0c83098b7555d4fa68b310b1e5113b6e.arboricx | 0
Aext/demo-site/bundles/20794e110733d119d3df5ae5a30a6f4917a12a8d4b4b4e55a887eea2148bc78d.arboricx | 0
Aext/demo-site/bundles/20a5c725f2dc2c02598364f26fc86e122913bde38600fd6512da52e2dcef55de.arboricx | 0
Aext/demo-site/bundles/258aa91dbed2e1a0170a8f0c2a87d3e11e9ac861ff74711460c14f735c6af1a2.arboricx | 0
Aext/demo-site/bundles/25a9d7530dbb272a32516897c08f0672dd84676da8c3bc0828bcf2824ca5b5e9.arboricx | 0
Aext/demo-site/bundles/275c92fc65c5f9ae4caa0a5d4183c5ccd6055c815b17f82da4b952761f8ad987.arboricx | 0
Aext/demo-site/bundles/2eb5dd5e3a372ef4e5c1ed612ca73eff2b0c9f97ebb651fb7ab22d5db9a15ca3.arboricx | 0
Aext/demo-site/bundles/2f6f3a1a9615134165e44ebb529d73608983f421bf2b483bc3f11e85cad27fa8.arboricx | 0
Aext/demo-site/bundles/320fbebc4604cdff3c3a3dba649966018ffb0c65241666108be05723b33284ad.arboricx | 0
Aext/demo-site/bundles/3670141b6ee280efe14f6f0dd2ef9469560cdbfaa08d2510ca37ddc9802fd18d.arboricx | 0
Aext/demo-site/bundles/36d76d7c128599998792fbeaa231ee416dc79b09c36fa20e1b1b5d2ead97076a.arboricx | 0
Aext/demo-site/bundles/3bd031b7077f8cfe87da22b1fb5960fbf911fdaa5756821185b6431bc74be43b.arboricx | 0
Aext/demo-site/bundles/3c7cc88a1e7d6f11892357e15647186fa400b4aa50c7dbe7b9beb259b6ed48c2.arboricx | 0
Aext/demo-site/bundles/3f0be618d76da309e1ebc74099978d56b1bf9c5eb0d8e732c71b91ef223fd15a.arboricx | 0
Aext/demo-site/bundles/3f8b3fc627479635890fa038e5fbac100db0d3014ed2f63c41ce1c4f856fb9bf.arboricx | 0
Aext/demo-site/bundles/3ff98271f9a512c04a8f200aa7488929edad50e1c7aa3864b243145f03cd3c9a.arboricx | 0
Aext/demo-site/bundles/422e1a6e5cc0e164bf627e19278e598665fe0edc05161c37f27b1c196e58da60.arboricx | 0
Aext/demo-site/bundles/43799eb78baaff0314d6014ef7692661759901d4a29608c9a169176beb13b231.arboricx | 0
Aext/demo-site/bundles/440618420eda88e9d8867ac5f99ade97dd8d678c44aa120995a96062e57c2045.arboricx | 0
Aext/demo-site/bundles/445db0a1f362b908bd0cfd4e8540e51630fa0ff3c639ce54a1f282bf7e6b2a78.arboricx | 0
Aext/demo-site/bundles/48194ad053a11151b87d129db717d01ea34381d9684281dfe876c41884508265.arboricx | 0
Aext/demo-site/bundles/4872d3ee67db34a9ce6844e5a80befe217d6e248441d875d335da0e706e2b501.arboricx | 0
Aext/demo-site/bundles/48ae69290f1858d9534fe1f60f7540ecaca466b6ec1f48a208c7b73981277fef.arboricx | 0
Aext/demo-site/bundles/4b329565f4c47401fe505e35090ec332e52b266c72cebe6b7d78985efeef1ba9.arboricx | 0
Aext/demo-site/bundles/4b5a7389a4b3886049863f107d7ca840c19080f27ef99d76845de2264ac3d6e7.arboricx | 0
Aext/demo-site/bundles/50d7c2af3c4d5aec428d9f97226992c7b12a72ec67dda5540e2e7d83d3ed0715.arboricx | 0
Aext/demo-site/bundles/50d923a5471cc42bff0a82da06b21ab6616ce90e76e94c3fa3bf4f0ab70c49bd.arboricx | 0
Aext/demo-site/bundles/54d1e5652a001bf19668bda2f660e7ede120ce6e8670e02a95daf18469ab9d0d.arboricx | 0
Aext/demo-site/bundles/5d3381723aafa9c5e4c4c3a6975de9481eb3160cbe52aca24076900528ef2603.arboricx | 0
Aext/demo-site/bundles/5d89ef0e68e4343c7a1da04c9dc6a82ff2e2089e770a1303f575b851c2814c4a.arboricx | 0
Aext/demo-site/bundles/5ec6ccb4827f0548444e480c0e6196ddaa7a570a44ebc6a91a922d7e59bf3012.arboricx | 0
Aext/demo-site/bundles/5fb1fab64ccc6554ebeedb74c35f616833f64e007aa787d37eb544739ca7d371.arboricx | 0
Aext/demo-site/bundles/62f77e46a310ae6dbfcd73d096126f0f6a16c61b98842d826a0b26783f733981.arboricx | 0
Aext/demo-site/bundles/6762fb2d66c52d04b4701b2508dd9ef6a8c62fcb66bfb607424f6b2caa488b85.arboricx | 0
Aext/demo-site/bundles/684497e21954bc0392fab20009d93efafb4ed15315ae94e345f7e5c0530d0a13.arboricx | 0
Aext/demo-site/bundles/6d156c31a1ecb7762df9fd94702dbf08773d81ca739b7cf806bc12ffe30ff10a.arboricx | 0
Aext/demo-site/bundles/6e185ece27a038b020b9b3f3a305009209849436697fd19f5aa888a383f92f40.arboricx | 0
Aext/demo-site/bundles/7a59f1f37047cadd22f97fb29e07f549e5f3a41b63b9af35e013669eb7a4893f.arboricx | 0
Aext/demo-site/bundles/7b6b438b2e903d6364eed8e948a814a1ebf2d08dfa9960fd7c1b278222f8cf4e.arboricx | 0
Aext/demo-site/bundles/7c5ec2c441d6ab4ef23edc3e9941dbf7bb4a7718d9e6e93f8826171e3f92f3bc.arboricx | 0
Aext/demo-site/bundles/7e698de68504e56e6c93d7822b3fe8dca79fa7aadd30710be63a3b4710fda061.arboricx | 0
Aext/demo-site/bundles/7ff7d2c5eead111a6364ca7f0dc0cc1e60910a3ffdd9bca18b8c624856e22f8d.arboricx | 0
Aext/demo-site/bundles/832388f8b0380e7f2bce6ca3e79f38ee6236c1d25232ea2cd5b6119c6eb41fba.arboricx | 0
Aext/demo-site/bundles/848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4.arboricx | 0
Aext/demo-site/bundles/890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa.arboricx | 0
Aext/demo-site/bundles/8b38dd5934bbc51f999e3a2da1ad33736991c7dd9aab5144b716611fc5a80810.arboricx | 0
Aext/demo-site/bundles/8ee2c0b4fec670afc9db5552e6907ace29ae2c0bda07897e70d451f9bb56160c.arboricx | 0
Aext/demo-site/bundles/925989ee208fe9cfd108c6093da5648b87ace44e134c7b28b0772eb5a5c540fc.arboricx | 0
Aext/demo-site/bundles/9cda2cdf30790c2d13dc51183a4fb321a00bec8599683004280f7317cf7a49ab.arboricx | 0
Aext/demo-site/bundles/9daafde4c396549744c888778485c9d2316d89db659181e1c46c3b7ed5a0f64f.arboricx | 0
Aext/demo-site/bundles/9f63810fee2e20762f1a198535448a2187a90065a35428ec82a11aad85575643.arboricx | 0
Aext/demo-site/bundles/a11920a322cbec778cf81d92ce6bdf28ae3ccfc772f87be92545f8462136304a.arboricx | 0
Aext/demo-site/bundles/a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d.arboricx | 0
Aext/demo-site/bundles/a2c5b9dc0013aa6df913b6ac5cef4467d15c237b1a3c5a7205ce434252cd4865.arboricx | 0
Aext/demo-site/bundles/a7babd04ce08dd587a9cc0e71d7d52e2850eb824118a3ea8b82b6b4b73f63521.arboricx | 0
Aext/demo-site/bundles/a96e8e66c1759e1893a70a849c68eda171baf833b651d152102a8a7013f6e608.arboricx | 0
Aext/demo-site/bundles/a9c978eb8475b65bd73bd6ac6474e6242df285cea8d73d5dcb19ca5d30512949.arboricx | 0
Aext/demo-site/bundles/ab19d9010aba0c7fcb6995c1c096872dc3bfb22a5798407011c01adfb7c57d8a.arboricx | 0
Aext/demo-site/bundles/acada8dbbd27922e757f2331fae97eedcadae1f0db369f693bbb8d413c3b0b00.arboricx | 0
Aext/demo-site/bundles/b0bb9f08d3a902c1b9e9649e58687d3c06e09421dd0c6d326ca854196da75878.arboricx | 0
Aext/demo-site/bundles/b1f763c54f8cbc87aebf1bb16fbe73805e09661b4cf8550edbdb9877ecf7a32c.arboricx | 0
Aext/demo-site/bundles/b60da1233b8b2a10ba7179e7f8fe39a07be8d2546c89ab36fadab28e2dfd8b23.arboricx | 0
Aext/demo-site/bundles/b6f32449d85257fbd0a589d29ef05ea4a503c4a7e6105926d408b0551a34fd77.arboricx | 0
Aext/demo-site/bundles/b75e428ce6d587893829792a87c3a374993d22c900eea793f2713e822440cb9f.arboricx | 0
Aext/demo-site/bundles/bb3135842e3cebfe6bd10e0766a6711e4d46a8cf157af1dcc9f97e25795ac0a1.arboricx | 0
Aext/demo-site/bundles/bba8e1d66b151e6b2aa42e8c73d61fa325e81d0388c0316b3e57fb0ddee3dde9.arboricx | 0
Aext/demo-site/bundles/bfc61066613fa893c0d9e6e37659617bcaff9e97a8d633cffd1d84066857462d.arboricx | 0
Aext/demo-site/bundles/bffce842f768003315e90f106d296297477db17bdd2bd61752e7197a89042402.arboricx | 0
Aext/demo-site/bundles/c2595128cd7056a33b634abc4a67bb99736b9cda08b6788e2936d8cd51fbaff1.arboricx | 0
Aext/demo-site/bundles/c3639067b4bdb79daa66253b95202c490f7545652864e718d1d558de0128a66a.arboricx | 0
Aext/demo-site/bundles/c60b350474c0c82734204dfd18f5780d97d296e091bef977f37157bff57102e9.arboricx | 0
Aext/demo-site/bundles/c632e6845677fc13e40f02599f66a4e243fd91f9cef79a840ec4eb6953f66905.arboricx | 0
Aext/demo-site/bundles/c80f9fa19c06b6c30f7bea3c21b9fec4516c9aafc4ec9045185f9fdda64106dd.arboricx | 0
Aext/demo-site/bundles/cd7be3dbf2c10f7a7e7911765cb65a5207a1558f289406e291d07cb077a69a34.arboricx | 0
Aext/demo-site/bundles/cda1110a3577a751c2673bc7a1ce2c3994dec541a3c1f6034b6a6769eaee4809.arboricx | 0
Aext/demo-site/bundles/cdda8a8efdd40b9f2d5beac446646831d11a6f1cf916a40d68f8064b90dda45a.arboricx | 0
Aext/demo-site/bundles/ce83251eed3aa18a8bc53aeedda62dbd90ed91ecd270bc56460457b435a788c4.arboricx | 0
Aext/demo-site/bundles/d00af17115d7a201333b92e386997f57c1808f5819a89d18312a96bcdfe777a8.arboricx | 0
Aext/demo-site/bundles/d015f17a71941870f8ab9ba5cbbef82b7657a9217a64edf54e0b512cf3ea3bc9.arboricx | 0
Aext/demo-site/bundles/d6bef87913b4b0859d181920121f95663325b679404c11b236941ee10dce0f1c.arboricx | 0
Aext/demo-site/bundles/d9f2858d088128d306525252c74025eb49565187b39da7e88c88b758cc4035d1.arboricx | 0
Aext/demo-site/bundles/de2b06e659b460d0ed6407f614396d1e9f6b43c455788c6660c88b153fc97fbc.arboricx | 0
Aext/demo-site/bundles/e07c225c29eef3588dbb65cf0178e8b82a7d774786d0e392052877f7c4aecf5f.arboricx | 0
Aext/demo-site/bundles/e1376f99435b5869d7f53b489a5d7552b382f670710a6e0e970f13ddc330948b.arboricx | 0
Aext/demo-site/bundles/e56378259592a4867829ca5447904b30ac5e21bd496eb68ad4d872c1af973d69.arboricx | 0
Aext/demo-site/bundles/e75e7178eb19bba565e1422eb140411e34c02df3db1fe32224fc434c6e6f5b69.arboricx | 0
Aext/demo-site/bundles/e965542627b83e4d55e3ce15748650c94ef0d447324d7dde772c0db1b0651788.arboricx | 0
Aext/demo-site/bundles/eb3c997bcd9d2cd2be93191865894f9627946462526ed3cffe3fbbdad6e3f682.arboricx | 0
Aext/demo-site/bundles/ec65f6eac30f1487dc64e519ae0be9769695edbff58e529719424a95059c6875.arboricx | 0
Aext/demo-site/bundles/ef1971af4ec38386365ef2fc2a9e4fe05f2dd8af3c8ca887a91789d9959a0ed2.arboricx | 0
Aext/demo-site/bundles/ef3bbb54373753b15a2d3125e0f260efe2a1e7b464d451819afcc02721683bb2.arboricx | 0
Aext/demo-site/bundles/f056d6c372cd700f325f6077a043492c9f4fb5d59e179ddfc00f69e608c7d969.arboricx | 0
Aext/demo-site/bundles/f78cfa7217deb642299e0fa66d0d714311ec7a196dee4baa621881b7190c97d9.arboricx | 0
Aext/demo-site/bundles/fbc9e40b382d4ed621669432f2b1fd6f9353dfaba099f4b070b344eb3a30c4a6.arboricx | 0
Aext/demo-site/bundles/ffdd1a95b974e1745b846cfdfeaf0bcbf4b3b60e010f1d7184fe3333220a03cb.arboricx | 0
Mext/demo-site/catalogue.json | 6+++---
Mext/demo-site/compiler-reference.v1 | 25++++++++++++++-----------
Mext/demo-site/compiler.json | 4++--
Mext/demo-site/demos.json | 21++++++++++++++-------
Mext/demo-site/demos/greet.tri | 32++++++++++++++++++++------------
Mext/demo-site/dist/app.js | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mext/demo-site/dist/conformance/compiler-worker.js | 28+---------------------------
Mext/demo-site/dist/conformance/worker.js | 28+---------------------------
Mext/demo-site/dist/definition-inspector.js | 48++++++++++--------------------------------------
Mext/demo-site/dist/execution.js | 3++-
Mext/demo-site/dist/haskell-session.js | 45+++++++++++++++++++++------------------------
Aext/demo-site/dist/io-v2-core.js | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-driver.js | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-events.js | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-host-work.js | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-output.js | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-private.js | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-profile.js | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-protocol.js | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-resources.js | 225+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-session.js | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-tasks.js | 325+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-terminal-view.js | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-terminal.js | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/dist/io-v2-utf8.js | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mext/demo-site/dist/io.js | 66+++---------------------------------------------------------------
Mext/demo-site/dist/wasm-worker.js | 66++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mext/demo-site/dist/zig-helpers.js | 5+++++
Mext/demo-site/environments.json | 2+-
Mext/demo-site/lexer-demo.json | 2+-
Mext/demo-site/packages.json | 2240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mext/demo-site/parser-demo.json | 2+-
Mext/demo-site/prepare-libraries.py | 28++++++++++++++++++++++++++--
Mext/demo-site/prepare.py | 5+++--
Mext/demo-site/src/app.ts | 47++++++++++++++++++++++++++++++++++++++---------
Mext/demo-site/src/conformance/compiler-worker.ts | 24++----------------------
Mext/demo-site/src/conformance/worker.ts | 25+++----------------------
Mext/demo-site/src/definition-inspector.ts | 34++++++++++++----------------------
Mext/demo-site/src/execution.ts | 3++-
Mext/demo-site/src/haskell-session.ts | 33+++++++++++++++++----------------
Aext/demo-site/src/io-v2-core.ts | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-driver.ts | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-events.ts | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-host-work.ts | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-output.ts | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-private.ts | 37+++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-profile.ts | 43+++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-protocol.ts | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-resources.ts | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-session.ts | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-tasks.ts | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-terminal-view.ts | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-terminal.ts | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/src/io-v2-utf8.ts | 39+++++++++++++++++++++++++++++++++++++++
Mext/demo-site/src/io.ts | 56+++++---------------------------------------------------
Mext/demo-site/src/protocol.ts | 11+++++------
Mext/demo-site/src/wasm-worker.ts | 56+++++++++++++++++++++++++++++++++++++-------------------
Mext/demo-site/src/zig-helpers.ts | 6++++++
Mext/demo-site/style.css | 4++--
Mext/demo-site/test/backend-browser.mjs | 6+++---
Mext/demo-site/test/backend-probe.mjs | 2+-
Mext/demo-site/test/backend-resume-probe.mjs | 56+++++++++++++++++++++++++++++++++++++++-----------------
Mext/demo-site/test/backend-server.mjs | 12+++++++++++-
Mext/demo-site/test/bundle.test.mjs | 3++-
Mext/demo-site/test/compiler-conformance.json | 7++++---
Mext/demo-site/test/compiler-conformance.mjs | 10++++++----
Mext/demo-site/test/compiler.test.mjs | 7++++++-
Mext/demo-site/test/conformance.json | 2+-
Mext/demo-site/test/contracts-conformance.json | 4++--
Mext/demo-site/test/demo-cost.mjs | 12+++++-------
Mext/demo-site/test/editable-conformance.mjs | 4++--
Mext/demo-site/test/gc.test.mjs | 29-----------------------------
Mext/demo-site/test/graph.test.mjs | 59+++++++++++++++++++++++++++--------------------------------
Mext/demo-site/test/inline-io-probe.mjs | 110+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Aext/demo-site/test/io-v2-core-probe.mjs | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-core.test.mjs | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-events.test.mjs | 33+++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-host-work.test.mjs | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-lifecycle-probe.mjs | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-output.test.mjs | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-protocol-probe.mjs | 33+++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-protocol.test.mjs | 37+++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-public-probe.mjs | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-public.test.mjs | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-resources.test.mjs | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-session-probe.mjs | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-session.test.mjs | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-source-probe.mjs | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-tasks.test.mjs | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-terminal.test.mjs | 37+++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-v2-utf8.test.mjs | 36++++++++++++++++++++++++++++++++++++
Aext/demo-site/test/io-version-boundary.test.mjs | 16++++++++++++++++
Aext/demo-site/test/io-version-probe.mjs | 40++++++++++++++++++++++++++++++++++++++++
Mext/demo-site/test/live-ux-probe.mjs | 6+++---
Mext/demo-site/test/module-probe.mjs | 23+++++++++++++++++------
Mext/demo-site/test/modules.test.mjs | 33+++++++++++++++++++++++++++++----
Mext/demo-site/test/prepare-compiler.py | 11+++++++++++
Mext/demo-site/test/probe-api.mjs | 7+++++++
Mext/demo-site/test/resume.test.mjs | 11+++++------
Mext/demo-site/test/site.test.mjs | 3++-
Aext/demo-site/test/terminal-host.mjs | 28++++++++++++++++++++++++++++
Mext/demo-site/test/zig-negative-fixtures.mjs | 38+++++++++++++++++++++++++++++++++++++-
Mext/demo-site/test/zig-session-probe.mjs | 47++++++++++++++++++++++++++++++++++-------------
Mext/haskell-wasm/BrowserRuntime.hs | 89++++++++++++++++++-------------------------------------------------------------
Aext/haskell-wasm/BrowserTrees.hs | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mext/haskell-wasm/README.md | 106++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mext/haskell-wasm/Reactor.hs | 88++++++++++++++++++++++++-------------------------------------------------------
Mext/haskell-wasm/browser-client.mjs | 3++-
Mext/haskell-wasm/browser-parity.mjs | 71++++++++++++++++++++++++++++++++++++-----------------------------------
Mext/haskell-wasm/browser-worker.mjs | 64+++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mext/haskell-wasm/browser.nix | 2++
Mext/haskell-wasm/prepare-parity.mjs | 11++++++-----
Mext/js/src/lib.js | 2+-
Mext/php/src/ffi.php | 4++--
Mext/wasm/check.mjs | 2+-
Mext/wasm/runtime.mjs | 12+++++++++++-
Mext/wasm/site-session.mjs | 49++++++++++++++++---------------------------------
Mext/zig/README.md | 313++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mext/zig/build.zig | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mext/zig/include/arboricx.h | 15+++++++--------
Mext/zig/src/c_abi.zig | 16++++++----------
Mext/zig/src/indexed.zig | 326++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Dext/zig/src/io_driver.zig | 1593-------------------------------------------------------------------------------
Aext/zig/src/io_v2_core.zig | 445+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_core_probe.zig | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_digest.zig | 41+++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_file_host.zig | 864+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_file_probe.zig | 4++++
Aext/zig/src/io_v2_files.zig | 397+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_native.zig | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_paths.zig | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_protocol.zig | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_resources.zig | 375+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_snapshot.zig | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_stage.zig | 208+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_stage_state.zig | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_tasks.zig | 582++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aext/zig/src/io_v2_utf8.zig | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mext/zig/src/main.zig | 28++++++++++------------------
Mext/zig/tests/handles_test.c | 2+-
Mext/zig/tests/helpers.h | 6------
Mext/zig/tests/io_run_test.c | 225++++++++++++-------------------------------------------------------------------
Mext/zig/tests/performance_smoke.c | 4++--
Mext/zig/tests/python_ffi_test.py | 62+++++++++++++++++++++++++++++++++++++++++++++-----------------
Mflake.nix | 57++++++++++++++++++++++++++++++++++++++++++++-------------
Alib/arboricx/handlers.tri | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/arboricx/inspect.tri | 6+++---
Mlib/arboricx/manifest.tri | 4++--
Mlib/arboricx/server.tri | 141++++++++++++++-----------------------------------------------------------------
Alib/arboricx/streaming-v2.tri | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/arboricx/streaming.tri | 4++++
Mlib/graph.tri | 7+++----
Alib/handlers.tri | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/handlers/artifacts.tri | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/handlers/log.tri | 38++++++++++++++++++++++++++++++++++++++
Alib/handlers/testing.tri | 28++++++++++++++++++++++++++++
Alib/handlers/upload.tri | 5+++++
Mlib/http.tri | 121++++++++++++-------------------------------------------------------------------
Alib/io-v2.tri | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/io-v2/artifacts.tri | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/io-v2/http-transport.tri | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/io-v2/objects.tri | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/io-v2/storage.tri | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/io-v2/text.tri | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/io.tri | 186++-----------------------------------------------------------------------------
Alib/packages/io-v2.tri | 17+++++++++++++++++
Mlib/packages/io.tri | 18+++++++++---------
Mlib/patterns.tri | 4++--
Mlib/socket.tri | 108++++++++++++-------------------------------------------------------------------
Mlib/source/build.tri | 2+-
Mlib/source/bundle.tri | 2+-
Mlib/source/export-cache.tri | 13+++++++++----
Alib/source/io-v2.tri | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/source/lock.tri | 2+-
Mlib/source/map.tri | 2+-
Mlib/source/module.tri | 2+-
Mlib/source/object.tri | 2+-
Mlib/source/program.tri | 7+++----
Mlib/source/stable.tri | 11+++++------
Msrc/ContentStore/Indexed.hs | 14+++++++++++++-
Asrc/ContentStore/Snapshot.hs | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/IODriver.hs | 1634+------------------------------------------------------------------------------
Asrc/IOV2/Clock.hs | 28++++++++++++++++++++++++++++
Asrc/IOV2/Codec.hs | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Context.hs | 26++++++++++++++++++++++++++
Asrc/IOV2/Descriptor.hs | 29+++++++++++++++++++++++++++++
Asrc/IOV2/Digest.hs | 30++++++++++++++++++++++++++++++
Asrc/IOV2/Driver.hs | 451+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Filesystem.hs | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Network.hs | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Process.hs | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Protocol.hs | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Resolver.hs | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Resources.hs | 612+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Staging.hs | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Tasks.hs | 323+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/UTF8.hs | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/IOV2/Wire.hs | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/InteractionInspection.hs | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/Main.hs | 83++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/Module/Provenance.hs | 53++++++++++++++++++++++++++++++++++++++---------------
Msrc/PortableCompiler.hs | 16+++++++++-------
Msrc/Runtime/Value.hs | 18++++++++++++++++--
Msrc/Wire.hs | 140++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtest/IOBoundaryTests.hs | 126++++++++++---------------------------------------------------------------------
Mtest/IODriverTests.hs | 1314++-----------------------------------------------------------------------------
Atest/IOV2ClockTests.hs | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2ContextTests.hs | 39+++++++++++++++++++++++++++++++++++++++
Atest/IOV2FilesystemTests.hs | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2NetworkTests.hs | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2PositionTests.hs | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2ProcessTests.hs | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2ProtocolTests.hs | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2ResolverTests.hs | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2StandardTests.hs | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2StorageTests.hs | 455+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2SyncTests.hs | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2TaskTests.hs | 328+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2Tests.hs | 356+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IOV2UTF8Tests.hs | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/IndexedWriterTests.hs | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/InteractionInspectionTests.hs | 34++++++++++++++++++++++++++++++++--
Mtest/LibraryValueTests.hs | 14+++++++-------
Mtest/PortableCompilerTests.hs | 45+++++++++++++++++++++++++++++++++++++--------
Mtest/ReferenceFixtures.hs | 4++--
Dtest/ScriptedEffects.hs | 28----------------------------
Atest/SnapshotTests.hs | 306+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/Spec.hs | 538+++++++++++++++++--------------------------------------------------------------
Mtest/SupportFixture.hs | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mtest/arboricx_deployment.py | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mtest/arboricx_server_inspection.py | 55+++++++++++++++++++++++++++----------------------------
Atest/body_retention.py | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/bootstrap_acceptance.py | 19++++++++++++++++++-
Mtest/bootstrap_archive.py | 54+++++++++++++++++++++++++++++++++++++++++-------------
Mtest/bootstrap_cross_host.py | 101++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mtest/bootstrap_cross_host_controls.py | 9++++++++-
Mtest/bootstrap_generations.py | 51++++++++++++++++++++++++++++++++++++++++++---------
Mtest/bootstrap_inputs.py | 15++++++++++++++-
Mtest/bootstrap_retention.py | 31+++++++++++++++++++++++++++++--
Mtest/bootstrap_retention_probe.py | 25+++++++++++++++++++------
Mtest/bootstrap_retention_test.py | 16++++++++++++++++
Mtest/bootstrap_smoke.py | 3++-
Atest/compiler_io_version.py | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/default_frontend.py | 13+++++++++++--
Atest/effect_interfaces.py | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/effect_interfaces/README.md | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/effect_interfaces/server.tri | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/effect_interfaces/tricu.workspace | 14++++++++++++++
Atest/effect_interfaces/workflow.tri | 384+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/effect_server.py | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/executable_arguments.py | 21+++++++++++++++------
Mtest/fixture_sources.py | 45++++++++++++++++++++++++++++++++++++++++++---
Mtest/fixtures/compiler-library.arboricx | 0
Mtest/fixtures/compiler-library.v1 | 25++++++++++++++-----------
Atest/fixtures/reference-source/060a440835999d443ef767ea5d4e9efe616a7186e1bb04947d6f9f80a21b0a4b.tri | 16++++++++++++++++
Atest/fixtures/reference-source/06944982576cd127727cf48915b1842237308ac7e1b517b3b21a8846fd577dc3.tri | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/06e81c8b62b8f26ba20faa3280b381ddd55ecf246876a21b028175bbeb87f896.tri | 41+++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/0d38a5a7511d4a08f648dfa28beaa312f9139bf9639f7779f2f83ac45134f439.tri | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/0f98a415e9bcdd223cd27eba9b501e91fe6b3778a2d5ea59218815081ab3d767.tri | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/137f8881288b64071a60fc4e29ae2b276d32bac79f748b8f2174838b9da1ffc0.tri | 398+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/1551418951ec75f1a0f1d47e570b645ccc1e7e4f5bb1de6dd0c4583c4f2fb3a5.tri | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/1ca2682e6a35d949196ff3b1d2aabed47af57e7ef43b8e32b2b7b19ca004f64f.tri | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/2373335a22f1a6c7d00c7dd612ec8498deb8502eaf48bd6e8aa9f9407a1bfc13.tri | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/297b2195345b9bf4c49857aacd48545604157a9a0f7b3cb5d8ec7759e2e41e83.tri | 259+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/2b971f5cfdfea12210f6ed1801a3660735e1da97d4d372cf751eb76879750991.tri | 22++++++++++++++++++++++
Atest/fixtures/reference-source/369b3d37d4704c1c28f038df3392572ab906c11bbdde16710f97b33fdc3e54fd.tri | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/37cfec8f76370ad98a5d8a0b1a3d36f9af5b0597fedf996f6ed29e2c94c0358c.tri | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/386257391872e7b73befc2bcc3634706acf4acf0ff791bc60161179434c9a813.tri | 31+++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/3bfd3a8ea35a6316e1ef9cc53ac2418a9dbd6115c69d129a1b2de81c87877e7a.tri | 342+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/40783b2c9c5750583151dda9d04b435d3d0d8a63b6f33cccfa94fd8ed958c14a.tri | 6++++++
Atest/fixtures/reference-source/49be76ef3607cedcafd4e6a599b90a7f0601b5d030e1431011e879342c9b0e91.tri | 567+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/4dc6a84c739f524f09e5378ddb79140fd11629988a8a451c0e823f6c1dc21e96.tri | 17+++++++++++++++++
Atest/fixtures/reference-source/4dcb43187b3cbd168fcb3ad5269be3e96257aa30773bca933e6cf37dfbdee066.tri | 20++++++++++++++++++++
Atest/fixtures/reference-source/4e2c4278cd4efaa9c2d9d7db51fb2a7a0c99fe2e9ac76bc3ea89bd26e47ba4b7.tri | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/4f64f9f4175b18c4e97caa24249d3bbe1261a57c6215de53c5ed4efe7fc546ab.tri | 38++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/50691f992e348fb47baccc119c3809f7e33fdcce5c801b23ed8635962f346655.tri | 35+++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/52c3f1b1f0d29975e3932850451dc8e3cee93ac475dac869d8e9eba7838bd830.tri | 297+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/5445a4f505c572889455435960a144663e224fa40a9c1cce1d9a96a8e3678fef.tri | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/575f7c3c8834bd2780f444a0078e72366fe2f0de6c4e00b4c3722e4da8bb9693.tri | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/5e4d9186f5235078c786432b6ac8a2952ee8c87bed8f808503d5107daaa07bac.tri | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/61799bafcab20f90faea5562f2c25c89bdab164571ac6073e4c029c1f86e5b66.tri | 13+++++++++++++
Atest/fixtures/reference-source/64ba09f05b561ed1effe383dfad832e354f306a40675f478c7aecfaefd6d78d5.tri | 28++++++++++++++++++++++++++++
Atest/fixtures/reference-source/64e2248dbd4a05fc78b9d92cd775bb111951af95e384b91000e7f7aa32410013.tri | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/65c006672139d98c27b5022a8641c73b6d0c9c29dd42f6cfe8b4820928664807.tri | 20++++++++++++++++++++
Atest/fixtures/reference-source/677c3b5487e0715a82a9ec6d4c5afd4ac83433ec06350cafcc7c2147e8325b7e.tri | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/708216b9a9b131be7a24abacef443e0322b2ff78f84b929245f486c1b2ac1d02.tri | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/73cfe94509cdbc3c234bf4dabfac1796f5c6cf92565fcdd5a673a865bb8cd65b.tri | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/79c07186e9a3a36ce4c1de54b331c42dec6b95aa974156677c9336fd1c0849d7.tri | 18++++++++++++++++++
Atest/fixtures/reference-source/7bce76817a1c9a6df5253b7f983a395d833e3622533a2d209c12a5c0f111aa5f.tri | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/811886e206ba2912893ebba28afe4a4085b8838fc5a097b165ffb56637c93524.tri | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/8338f32bdf08b7743933809b2acce42899d0020e83c95cd0b4d25b0a8b5c07df.tri | 297+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/834a2c8b9c9007364036bb22159ca0c5050c1536e87c4c40bf5573efee863e30.tri | 25+++++++++++++++++++++++++
Atest/fixtures/reference-source/870df776ac7142fac5aad7f0169dfd08b2934f2bfa61ad3ae1c9b949f8f75088.tri | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/87fa3ab396fb5177d0fcd0ab5375e94d3cd11876e2edf398e2f74a08d5f767e1.tri | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/8b2e1cd6ef5acc8454a690875d2e8415869892f84c3fb5198c18fc8e665d0f32.tri | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/9acae0f91625a1f6e13601276f2e11b319d346d872e85d758377fd649da5d5d8.tri | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/b1eccce1b5e4702466df56e1b48755797d6d148dcc38656f821faaa4d90e34da.tri | 277+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/b842a39f410e28c89c843ac8f1d3a8db7afa2e11a5ce12bf5c29193a6fb6b3c6.tri | 18++++++++++++++++++
Atest/fixtures/reference-source/b881a086294ff264567d758dd2c1ef7a3470faa909ec588e9f7bb8c4430f7bfc.tri | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/bc09110023edc9a6aa7d947e51a09385f600d4fef66e8fd765808ad14f7ecc34.tri | 17+++++++++++++++++
Atest/fixtures/reference-source/bea75689ba66f19e241f97369ea7ee1393fabbaa7c7eac9b9a2d0ca1e455a1bf.tri | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/c60b2bdf222db8ed812c2af4da3609cceefc8689c8aeb80fa52795d69193d0f5.tri | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/c7c5c3133cf91206d8bdb13fd6972aed4bca3fae8b16308bafc6e25236c2cd0c.tri | 4++++
Atest/fixtures/reference-source/d52075fd243ce733529740023181faa44c7c3022a9f28ee7ea2aaa078d95f7b2.tri | 15+++++++++++++++
Atest/fixtures/reference-source/dd2f44109a0d14aa50e6a670067832720ff882f1ce7d8fac094d82280cbfb005.tri | 25+++++++++++++++++++++++++
Atest/fixtures/reference-source/de6cdc548d44df7d835386a49116364e4ce8df8729801ca85dc975000e66af7f.tri | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/e014061b2107cb0d8d1d2366742c8f35640c9a7dd85b80fc78d4afc7813bf0cc.tri | 20++++++++++++++++++++
Atest/fixtures/reference-source/e98fc7955feb673b86ac9176a211136493d6247b011cb80995fb53464028f976.tri | 48++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/ee1531f86d1faaef4ce7b1ca9151bd2e7b10c3fc0574d0db67ddfce6c012c4e3.tri | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/efa6213780eb641f7ec9ae6006d105a8110232ac06b455c335a5552324e58591.tri | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/fcd88bebeab55f69199836dfcdd8fe9007c5dcb7c7e951c7b4e7a4107ce85e44.tri | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/reference-source/fe110d74ff62d9f16683bae0adcf82c65ce5c4c18cf2ada50927bf2509e0fa5e.tri | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/support-cohort-v4.v1 | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/fixtures/support-library.arboricx | 0
Mtest/fixtures/support-library.v1 | 153++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mtest/fixtures/support-rebuilt.arboricx | 0
Mtest/fixtures/support-receipts/arboricx.receipt | 14+++++++-------
Mtest/fixtures/support-receipts/arboricx_common.receipt | 8++++----
Mtest/fixtures/support-receipts/arboricx_dispatch.receipt | 4++--
Atest/fixtures/support-receipts/arboricx_handlers.manifest | 6++++++
Atest/fixtures/support-receipts/arboricx_handlers.receipt | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/fixtures/support-receipts/arboricx_manifest.receipt | 10+++++-----
Mtest/fixtures/support-receipts/arboricx_nodes.receipt | 10+++++-----
Mtest/fixtures/support-receipts/arboricx_server.manifest | 27+++++++++------------------
Mtest/fixtures/support-receipts/arboricx_server.receipt | 241+++++++++++++++++++++----------------------------------------------------------
Atest/fixtures/support-receipts/arboricx_streaming_v2.manifest | 5+++++
Atest/fixtures/support-receipts/arboricx_streaming_v2.receipt | 373+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/fixtures/support-receipts/binary.receipt | 4++--
Mtest/fixtures/support-receipts/demos_contractBasics.receipt | 4++--
Mtest/fixtures/support-receipts/demos_contractEffects.receipt | 4++--
Mtest/fixtures/support-receipts/demos_equality.receipt | 4++--
Mtest/fixtures/support-receipts/demos_interactionTrees.manifest | 4++--
Mtest/fixtures/support-receipts/demos_interactionTrees.receipt | 472++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_arboricxServer.manifest | 3++-
Mtest/fixtures/support-receipts/demos_interactionTrees_arboricxServer.receipt | 513++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_echoServer.manifest | 4++--
Mtest/fixtures/support-receipts/demos_interactionTrees_echoServer.receipt | 530+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_environment.manifest | 2+-
Mtest/fixtures/support-receipts/demos_interactionTrees_environment.receipt | 492++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_forkAwait.manifest | 4++--
Mtest/fixtures/support-receipts/demos_interactionTrees_forkAwait.receipt | 472++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_getLineAsync.manifest | 2+-
Mtest/fixtures/support-receipts/demos_interactionTrees_getLineAsync.receipt | 472++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_greet.manifest | 2+-
Mtest/fixtures/support-receipts/demos_interactionTrees_greet.receipt | 317+++++++++++++++++++++----------------------------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_httpServer.manifest | 4++--
Mtest/fixtures/support-receipts/demos_interactionTrees_httpServer.receipt | 667+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_safeRead.manifest | 5+++--
Mtest/fixtures/support-receipts/demos_interactionTrees_safeRead.receipt | 317+++++++++++++++++++++----------------------------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_shout.manifest | 6+++---
Mtest/fixtures/support-receipts/demos_interactionTrees_shout.receipt | 472++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_state.manifest | 3++-
Mtest/fixtures/support-receipts/demos_interactionTrees_state.receipt | 472++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_writeThenRead.manifest | 5+++--
Mtest/fixtures/support-receipts/demos_interactionTrees_writeThenRead.receipt | 472++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtest/fixtures/support-receipts/demos_interactionTrees_yield.manifest | 4++--
Mtest/fixtures/support-receipts/demos_interactionTrees_yield.receipt | 500++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mtest/fixtures/support-receipts/demos_levelOrderTraversal.receipt | 4++--
Mtest/fixtures/support-receipts/demos_patternMatching.receipt | 6+++---
Mtest/fixtures/support-receipts/demos_runArboricxBundle.manifest | 5+++--
Mtest/fixtures/support-receipts/demos_runArboricxBundle.receipt | 481+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mtest/fixtures/support-receipts/demos_size.receipt | 4++--
Mtest/fixtures/support-receipts/demos_toSource.receipt | 4++--
Atest/fixtures/support-receipts/handler_artifacts.manifest | 9+++++++++
Atest/fixtures/support-receipts/handler_artifacts.receipt | 292+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/support-receipts/handler_log.manifest | 8++++++++
Atest/fixtures/support-receipts/handler_log.receipt | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/support-receipts/handlers.manifest | 20++++++++++++++++++++
Atest/fixtures/support-receipts/handlers.receipt | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/fixtures/support-receipts/http.manifest | 36++++++++++--------------------------
Mtest/fixtures/support-receipts/http.receipt | 111+++++--------------------------------------------------------------------------
Mtest/fixtures/support-receipts/intensional.receipt | 4++--
Atest/fixtures/support-receipts/io.manifest | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/support-receipts/io.receipt | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/support-receipts/io_v2_artifacts.manifest | 4++++
Atest/fixtures/support-receipts/io_v2_artifacts.receipt | 374+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/fixtures/support-receipts/io_v2_http.manifest | 5+++++
Atest/fixtures/support-receipts/io_v2_http.receipt | 321+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/fixtures/support-receipts/patterns.receipt | 6+++---
Mtest/fixtures/support-receipts/socket.manifest | 47+++++++++++++----------------------------------
Mtest/fixtures/support-receipts/socket.receipt | 310++++++++++++++++++++-----------------------------------------------------------
Mtest/fixtures/support-receipts/source_build_stable.manifest | 2+-
Mtest/fixtures/support-receipts/source_build_stable.receipt | 80++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtest/fixtures/support-receipts/source_export_cache.manifest | 13+++++++------
Mtest/fixtures/support-receipts/source_export_cache.receipt | 495+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mtest/fixtures/support-receipts/source_staged_conditional_experiment.receipt | 26+++++++++++++-------------
Mtest/fixtures/support-receipts/source_staged_list_experiment.receipt | 12++++++------
Mtest/fixtures/support-receipts/source_staging_experiment.receipt | 8++++----
Mtest/fixtures/support-receipts/tests_stagedConditionalRunner.receipt | 12++++++------
Mtest/fixtures/support-receipts/tests_stagingExperiment.receipt | 12++++++------
Atest/generation_io.py | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/handler_costs.py | 302++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/handler_library.py | 301+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/handlers/README.md | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/handlers/equivalent.tri | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/handlers/library.tri | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/handlers/observe.tri | 34++++++++++++++++++++++++++++++++++
Atest/handlers/state.tri | 20++++++++++++++++++++
Atest/handlers/tricu.workspace | 24++++++++++++++++++++++++
Mtest/interaction_inspection.py | 16++++++++++++++--
Atest/io-v2/README.md | 1436+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io-v2/artifact-service.tri | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io-v2/browser.tri | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io-v2/core-controls.tri | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io-v2/core-retention.tri | 26++++++++++++++++++++++++++
Atest/io-v2/indexed-transport.tri | 14++++++++++++++
Atest/io-v2/native-cli.tri | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io-v2/protocol.tri | 24++++++++++++++++++++++++
Atest/io-v2/registry-log-failure.tri | 10++++++++++
Atest/io-v2/tasks.tri | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io-v2/zig-digest.tri | 27+++++++++++++++++++++++++++
Atest/io-v2/zig-files.tri | 40++++++++++++++++++++++++++++++++++++++++
Atest/io-v2/zig-paths.tri | 28++++++++++++++++++++++++++++
Atest/io-v2/zig-stages.tri | 24++++++++++++++++++++++++
Atest/io-v2/zig-utf8.tri | 17+++++++++++++++++
Mtest/io_dependencies.py | 12++++++------
Atest/io_v2_artifact_service.py | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_callers.py | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_clock.py | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_compiler_store.py | 292+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_context.py | 37+++++++++++++++++++++++++++++++++++++
Atest/io_v2_demo_servers.py | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_directory.py | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_files.py | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_generation.py | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_http.py | 236+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_indexed.py | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_indexed_write.py | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_lines.py | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_native_cli.py | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_objects.py | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_positioned.py | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_process.py | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_protocol.py | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_provenance.py | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_publication.py | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_streams.py | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_tasks.py | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_tcp.py | 296+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_text.py | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_zig_core.py | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_zig_digest.py | 39+++++++++++++++++++++++++++++++++++++++
Atest/io_v2_zig_files.py | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_zig_paths.py | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_zig_publication.py | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/io_v2_zig_stages.py | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/library_inspector.py | 60+++++++++++++++++++++++++++++++++++++++++++++++-------------
Mtest/native_indexed_store.py | 120+++----------------------------------------------------------------------------
Mtest/native_library_migration.py | 127++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mtest/native_source_map_fixture.py | 15+++++++++------
Mtest/native_source_utf8.py | 76++++++++++++++--------------------------------------------------------------
Mtest/package_registry.py | 16++++++++++------
Mtest/portable_cli.py | 27+++++++++++++++++++--------
Atest/prepare_compiler_kit.py | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/prepare_generation_worker.py | 7++++++-
Mtest/process_rss.py | 8++++----
Atest/process_rss_test.py | 45+++++++++++++++++++++++++++++++++++++++++++++
Mtest/provenance.py | 55++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mtest/repair_support_fixture.py | 55+++++++++++++++++++++++++++++++++++++++++++++++++++----
Atest/rts_metrics.py | 24++++++++++++++++++++++++
Atest/rts_metrics_test.py | 37+++++++++++++++++++++++++++++++++++++
Mtest/shipped_compiler.py | 4+++-
Dtest/sourceEliminationProbe.tri | 20--------------------
Mtest/source_order.py | 2++
Dtest/stableBundleRuntime.tri | 25-------------------------
Mtest/stable_bundle.py | 202+------------------------------------------------------------------------------
Mtest/stable_costs.py | 79+------------------------------------------------------------------------------
Atest/streaming_protocol.py | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/unit.py | 212++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtest/verified_tree_loading.py | 154++-----------------------------------------------------------------------------
Mtest/zig_generation_executor.py | 52++++++++++++++++++++++++++++++++++++++++++++--------
Mtricu-apps/arboricxServer.tri | 34+++++++---------------------------
Atricu-apps/arboricxStreamingV2.tri | 8++++++++
Mtricu.cabal | 53++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtricu.workspace | 15+++++++++++++++
644 files changed, 45836 insertions(+), 13018 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md @@ -27,6 +27,10 @@ executable rollback. Neither replaces testing. Use YYYY.MM.DD CalVer in tricu.ca agent progress reports, provisional findings, in-flight plans, merge/generation reminders, handoff notes, or scratch investigations there—even inside an existing guide. Keep those in conversation, commit messages, or external scratch artifacts. +Describe behavior affirmatively in documentation and comments. Use contrasts +with excluded behavior only when a positive description cannot accurately convey +the distinction. + Document supported usage, semantics, implementation contracts, and reproducible validation procedures; keep implementation details beside source/tests. Do not present unvalidated changes as shipped behavior. diff --git a/bootstrap/build-stable.arboricx b/bootstrap/build-stable.arboricx Binary files differ. diff --git a/bootstrap/compiler-source-map.io-v2.v1 b/bootstrap/compiler-source-map.io-v2.v1 @@ -0,0 +1,32 @@ +arboricx.source-map.v1 +native source_index source.index lib/source/index.tri prelude +native source_map source.map lib/source/map.tri prelude,compiler_io,source_object,source_module,source_resolve,source_build +native source_unit source.unit lib/source/unit.tri prelude,parser,source_order,source_compile,source_program,source_resolve,source_ast,source_stable,source_index +native prelude prelude lib/prelude.tri base,bytes,conversions +native compiler_io compiler.io lib/source/io-v2.tri base,io_v2,io_v2_storage,io_v2_text +native source_object source.object lib/source/object.tri prelude,compiler_io,native_math +native source_module source.module lib/source/module.tri prelude,compiler_io,source_object,source_unit,source_resolve,source_lexer,parser +native source_resolve source.resolve lib/source/resolve.tri prelude,parser,source_parser,source_ast,source_lexer,source_core,source_equations,source_pattern +native source_build source.build lib/source/build.tri prelude,compiler_io,source_object,source_module,source_lock,source_bundle,source_resolve,source_unit +native parser parser lib/parser.tri prelude +native source_order source.order lib/source/order.tri prelude,parser,graph,source_program,source_resolve,source_ast,source_equations,source_index +native source_compile source.compile lib/source/compile.tri prelude,parser,source_resolve,source_core +native source_program source.program lib/source/program.tri prelude,parser,source_parser,source_lexer,source_ast,source_equations +native source_ast source.ast lib/source/ast.tri prelude,source_lexer +native base base lib/base.tri native_math +native bytes bytes lib/bytes.tri base +native conversions conversions lib/conversions.tri base,native_math +native source_lexer source.lexer lib/source/lexer.tri prelude,parser,lexer,native_math +native source_parser source.parser lib/source/parser.tri prelude,parser,source_lexer,source_ast,source_equations +native source_core source.core lib/source/core.tri prelude +native source_lock source.lock lib/source/lock.tri prelude,compiler_io,source_module,source_object,source_resolve +native source_bundle source.bundle lib/source/bundle.tri prelude,compiler_io,source_object,source_module,source_resolve +native graph graph lib/graph.tri base +native lexer lexer lib/lexer.tri base,parser +native source_pattern source.pattern lib/source/pattern.tri prelude,parser,source_core +native source_equations source.equations lib/source/equations.tri prelude,parser,source_lexer,source_ast,source_pattern,source_core +native source_stable source.stable lib/source/stable.tri prelude,parser,source_core,source_ast,source_compile,source_resolve,source_program,source_order +native native_math math.native lib/math/native.tri - +native io_v2 io.v2 lib/io-v2.tri base,native_math +native io_v2_storage io.v2.storage lib/io-v2/storage.tri base,native_math,io_v2 +native io_v2_text io.v2.text lib/io-v2/text.tri base,native_math,io_v2 diff --git a/bootstrap/compiler-source-map.v1 b/bootstrap/compiler-source-map.v1 @@ -1,13 +1,13 @@ arboricx.source-map.v1 native source_index source.index lib/source/index.tri prelude -native source_map source.map lib/source/map.tri prelude,io,source_object,source_module,source_resolve,source_build +native source_map source.map lib/source/map.tri prelude,compiler_io,source_object,source_module,source_resolve,source_build native source_unit source.unit lib/source/unit.tri prelude,parser,source_order,source_compile,source_program,source_resolve,source_ast,source_stable,source_index native prelude prelude lib/prelude.tri base,bytes,conversions -native io io lib/io.tri base -native source_object source.object lib/source/object.tri prelude,io,native_math -native source_module source.module lib/source/module.tri prelude,io,source_object,source_unit,source_resolve,source_lexer,parser +native compiler_io compiler.io lib/source/io-v2.tri base,io_v2,io_v2_storage,io_v2_text +native source_object source.object lib/source/object.tri prelude,compiler_io,native_math +native source_module source.module lib/source/module.tri prelude,compiler_io,source_object,source_unit,source_resolve,source_lexer,parser native source_resolve source.resolve lib/source/resolve.tri prelude,parser,source_parser,source_ast,source_lexer,source_core,source_equations,source_pattern -native source_build source.build lib/source/build.tri prelude,io,source_object,source_module,source_lock,source_bundle,source_resolve,source_unit +native source_build source.build lib/source/build.tri prelude,compiler_io,source_object,source_module,source_lock,source_bundle,source_resolve,source_unit native parser parser lib/parser.tri prelude native source_order source.order lib/source/order.tri prelude,parser,graph,source_program,source_resolve,source_ast,source_equations,source_index native source_compile source.compile lib/source/compile.tri prelude,parser,source_resolve,source_core @@ -19,11 +19,14 @@ native conversions conversions lib/conversions.tri base,native_math native source_lexer source.lexer lib/source/lexer.tri prelude,parser,lexer,native_math native source_parser source.parser lib/source/parser.tri prelude,parser,source_lexer,source_ast,source_equations native source_core source.core lib/source/core.tri prelude -native source_lock source.lock lib/source/lock.tri prelude,io,source_module,source_object,source_resolve -native source_bundle source.bundle lib/source/bundle.tri prelude,io,source_object,source_module,source_resolve +native source_lock source.lock lib/source/lock.tri prelude,compiler_io,source_module,source_object,source_resolve +native source_bundle source.bundle lib/source/bundle.tri prelude,compiler_io,source_object,source_module,source_resolve native graph graph lib/graph.tri base native lexer lexer lib/lexer.tri base,parser native source_pattern source.pattern lib/source/pattern.tri prelude,parser,source_core native source_equations source.equations lib/source/equations.tri prelude,parser,source_lexer,source_ast,source_pattern,source_core native source_stable source.stable lib/source/stable.tri prelude,parser,source_core,source_ast,source_compile,source_resolve,source_program,source_order native native_math math.native lib/math/native.tri - +native io_v2 io.v2 lib/io-v2.tri base,native_math +native io_v2_storage io.v2.storage lib/io-v2/storage.tri base,native_math,io_v2 +native io_v2_text io.v2.text lib/io-v2/text.tri base,native_math,io_v2 diff --git a/bootstrap/rebuild/bootstrap-kit.json b/bootstrap/rebuild/bootstrap-kit.json @@ -1,18 +1,19 @@ { - "schema": "tricu.bootstrap-kit.v1", + "schema": "tricu.bootstrap-kit.v2", "backend": "stable", - "accepted_source": "1da4fab351c3c04d2543cea54667f099074e95cb", - "source_map_sha256": "14e5f01337df8e6b9cec8c88f08e9aed29d5c8fb9f37ae86a618953d260f413f", + "accepted_source": "f438bd9bf85c92642c5a12023232d5ef414612c2", + "source_map_sha256": "cf9cded6cdf08c2c5206140c8cb9ea6c56fe68dfd4f884322cb02b398f36ec28", "artifacts": { "compiler": { "file": "rebuilt-compiler.arboricx", "export": "sourceMapBuildStable", - "sha256": "7f625f6290facf67a389d6d4e8a045bae8f4ab4f53ea42203f1334b27e4bcf95" + "sha256": "55fbbc19ad0945311ae4c42ec529bebef8b06f47a9ab9cad52da21e8010e9076" }, "adapter": { "file": "io-adapter.arboricx", "export": "main", - "sha256": "60907c66516bdac8d240188252a6e337ac7adfa642bcf171ab83d86ec4fbd9d5" + "sha256": "e798596a882b150f38feafaf4b9aa6b1821e4cae3cf1b1b254ac9546bfbeffda" } - } + }, + "io_version": 2 } diff --git a/bootstrap/rebuild/io-adapter.arboricx b/bootstrap/rebuild/io-adapter.arboricx Binary files differ. diff --git a/bootstrap/rebuild/rebuilt-compiler.arboricx b/bootstrap/rebuild/rebuilt-compiler.arboricx Binary files differ. diff --git a/bootstrap/stable-compiler-source-map.v1 b/bootstrap/stable-compiler-source-map.v1 @@ -1,13 +1,13 @@ arboricx.source-map.v1 native source_index source.index lib/source/index.tri prelude -native source_map source.map lib/source/map.tri prelude,io,source_object,source_module,source_resolve,source_build +native source_map source.map lib/source/map.tri prelude,compiler_io,source_object,source_module,source_resolve,source_build native source_unit source.unit lib/source/unit.tri prelude,parser,source_order,source_compile,source_program,source_resolve,source_ast,source_stable,source_index native prelude prelude lib/prelude.tri base,bytes,conversions -native io io lib/io.tri base -native source_object source.object lib/source/object.tri prelude,io,native_math -native source_module source.module lib/source/module.tri prelude,io,source_object,source_unit,source_resolve,source_lexer,parser +native compiler_io compiler.io lib/source/io-v2.tri base,io_v2,io_v2_storage,io_v2_text +native source_object source.object lib/source/object.tri prelude,compiler_io,native_math +native source_module source.module lib/source/module.tri prelude,compiler_io,source_object,source_unit,source_resolve,source_lexer,parser native source_resolve source.resolve lib/source/resolve.tri prelude,parser,source_parser,source_ast,source_lexer,source_core,source_equations,source_pattern -native source_build source.build lib/source/build.tri prelude,io,source_object,source_module,source_lock,source_bundle,source_resolve,source_unit +native source_build source.build lib/source/build.tri prelude,compiler_io,source_object,source_module,source_lock,source_bundle,source_resolve,source_unit native parser parser lib/parser.tri prelude native source_order source.order lib/source/order.tri prelude,parser,graph,source_program,source_resolve,source_ast,source_equations,source_index native source_compile source.compile lib/source/compile.tri prelude,parser,source_resolve,source_core @@ -19,11 +19,14 @@ native conversions conversions lib/conversions.tri base,native_math native source_lexer source.lexer lib/source/lexer.tri prelude,parser,lexer,native_math native source_parser source.parser lib/source/parser.tri prelude,parser,source_lexer,source_ast,source_equations native source_core source.core lib/source/core.tri prelude -native source_lock source.lock lib/source/lock.tri prelude,io,source_module,source_object,source_resolve -native source_bundle source.bundle lib/source/bundle.tri prelude,io,source_object,source_module,source_resolve +native source_lock source.lock lib/source/lock.tri prelude,compiler_io,source_module,source_object,source_resolve +native source_bundle source.bundle lib/source/bundle.tri prelude,compiler_io,source_object,source_module,source_resolve native graph graph lib/graph.tri base native lexer lexer lib/lexer.tri base,parser native source_pattern source.pattern lib/source/pattern.tri prelude,parser,source_core native source_equations source.equations lib/source/equations.tri prelude,parser,source_lexer,source_ast,source_pattern,source_core native source_stable source.stable lib/source/stable.tri prelude,parser,source_core,source_ast,source_compile,source_resolve,source_program,source_order native native_math math.native lib/math/native.tri - +native io_v2 io.v2 lib/io-v2.tri base,native_math +native io_v2_storage io.v2.storage lib/io-v2/storage.tri base,native_math,io_v2 +native io_v2_text io.v2.text lib/io-v2/text.tri base,native_math,io_v2 diff --git a/bootstrap/stable-compiler.json b/bootstrap/stable-compiler.json @@ -1,13 +1,13 @@ { - "schema": "tricu.shipped-stable-compiler.v1", + "schema": "tricu.shipped-stable-compiler.v2", "backend": "stable", "bundle": "bootstrap/build-stable.arboricx", - "sha256": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e", + "sha256": "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4", "export": "sourceBuildGraph", "published_fixture_export": "source_build.sourceBuildGraphStable", - "accepted_source": "1da4fab351c3c04d2543cea54667f099074e95cb", + "accepted_source": "f438bd9bf85c92642c5a12023232d5ef414612c2", "source_map": "bootstrap/stable-compiler-source-map.v1", - "source_map_sha256": "14e5f01337df8e6b9cec8c88f08e9aed29d5c8fb9f37ae86a618953d260f413f", + "source_map_sha256": "cf9cded6cdf08c2c5206140c8cb9ea6c56fe68dfd4f884322cb02b398f36ec28", "sources": [ { "alias": "source_index", @@ -18,8 +18,8 @@ { "alias": "source_map", "path": "lib/source/map.tri", - "snapshot": "test/fixtures/reference-source/72638c3d2ec6f47598cba67c730249e8381c67cd5ba6437c4d24711e206afced.tri", - "sha256": "7bccdf109e4a4e84471c0c4d45705cb2d517062510d12079ce8439b61f095c26" + "snapshot": "test/fixtures/reference-source/efa6213780eb641f7ec9ae6006d105a8110232ac06b455c335a5552324e58591.tri", + "sha256": "7c1613558c38c424afb3aee5a2cd7b9e2f30c8302d5305abd32d541d096f9ada" }, { "alias": "source_unit", @@ -34,22 +34,22 @@ "sha256": "f04dabc6162ecb4c07d7df844078f2da31daec080656806bfe54f7be96ffa747" }, { - "alias": "io", - "path": "lib/io.tri", - "snapshot": "test/fixtures/reference-source/c4ec206590d25d581b9a96f242c22936d8d0b2e9903331ce50b30ad7de254bd6.tri", - "sha256": "1fd5bf8f19d0729d4df86775772e2ec00d4aff42e7483bd71b7eafb906d5857e" + "alias": "compiler_io", + "path": "lib/source/io-v2.tri", + "snapshot": "test/fixtures/reference-source/575f7c3c8834bd2780f444a0078e72366fe2f0de6c4e00b4c3722e4da8bb9693.tri", + "sha256": "f92b2523dc96206db8f335e78fff157091ba870afaaecfc3724159e7f3ed8a0f" }, { "alias": "source_object", "path": "lib/source/object.tri", - "snapshot": "test/fixtures/reference-source/79cd7f19d3de74be38bac823d8674f6b74327a4ae12bb0cf405119b51fbd9ea5.tri", - "sha256": "42245c52c513ec7dca241de6a8384f084c29defd1fc5ecbef2753fed51988e0e" + "snapshot": "test/fixtures/reference-source/0f98a415e9bcdd223cd27eba9b501e91fe6b3778a2d5ea59218815081ab3d767.tri", + "sha256": "2a35eaa72b136d5914df200673fea8ad2923e916ed411cf9e3f267b69bed08be" }, { "alias": "source_module", "path": "lib/source/module.tri", - "snapshot": "test/fixtures/reference-source/fb609a75a12659d2a05a63681c0cf96ef606045b4aebe044eac077c8cd23fbf2.tri", - "sha256": "d5fcf84a953068c86390c50ef7c018e045dc11e17521893c835ea976a6ec9964" + "snapshot": "test/fixtures/reference-source/49be76ef3607cedcafd4e6a599b90a7f0601b5d030e1431011e879342c9b0e91.tri", + "sha256": "1727ea831a54b97c8dbfb63007c7645662b13f2c3828c7d4ce6b18dbc86a70c1" }, { "alias": "source_resolve", @@ -60,8 +60,8 @@ { "alias": "source_build", "path": "lib/source/build.tri", - "snapshot": "test/fixtures/reference-source/5c24cc853df42e433916b1528fc8cd56334bd393606f54e7b12efb6a7069be0d.tri", - "sha256": "2ebbe730e40c91a854d0c4ff61bf037adf334f76e7da0ab2558ee386b5d8d951" + "snapshot": "test/fixtures/reference-source/3bfd3a8ea35a6316e1ef9cc53ac2418a9dbd6115c69d129a1b2de81c87877e7a.tri", + "sha256": "c395e37889780f8ba1b59b08710beee2146d5ae7120a640ee5327fc0c165f78e" }, { "alias": "parser", @@ -84,8 +84,8 @@ { "alias": "source_program", "path": "lib/source/program.tri", - "snapshot": "test/fixtures/reference-source/7a99fcc8da4c502ff67bc723b3ef317409a84f608c607cfa99331dd30245845a.tri", - "sha256": "c3e7ba64fa923003a199ed9aabe373cb0087691656a116e9a7d82ca176fdbd47" + "snapshot": "test/fixtures/reference-source/297b2195345b9bf4c49857aacd48545604157a9a0f7b3cb5d8ec7759e2e41e83.tri", + "sha256": "b5aecc97a425bf58b69a8fa9437fdf40cba2b5c44d183a309ff0d90ad49d5c6b" }, { "alias": "source_ast", @@ -132,20 +132,20 @@ { "alias": "source_lock", "path": "lib/source/lock.tri", - "snapshot": "test/fixtures/reference-source/31ffd58166bf861e82da0a90594cfb9c6c12e19baa51d5166fe0e4414d24af94.tri", - "sha256": "4c156008baae6d4f86a07ba805d7e50539d36f66ad34170a80be1e69fd85fe3a" + "snapshot": "test/fixtures/reference-source/52c3f1b1f0d29975e3932850451dc8e3cee93ac475dac869d8e9eba7838bd830.tri", + "sha256": "fa7fd529761523a9ca738d7634849043895c2b38bd8733c2d8c0422b99ae683d" }, { "alias": "source_bundle", "path": "lib/source/bundle.tri", - "snapshot": "test/fixtures/reference-source/3fedb41000f22609b0bdab8fb222175d1347202eceabe7bdb983bc28b3258627.tri", - "sha256": "92ac8da8a65a01b14d7ab21a5b111675528198180c4b201a9d5dbbc47e02cfe6" + "snapshot": "test/fixtures/reference-source/2373335a22f1a6c7d00c7dd612ec8498deb8502eaf48bd6e8aa9f9407a1bfc13.tri", + "sha256": "dae796778a24f441d137332ae745177618cfa65d05b986800867a8b23b79c1a4" }, { "alias": "graph", "path": "lib/graph.tri", - "snapshot": "test/fixtures/reference-source/bc8622d006e357b1045f2cb3c775a8a58045610ee6f4a617858762d37947b5ec.tri", - "sha256": "1c63908d2463f36f84b178cabf8faf4183325666f1961ba99b21b4957d973d18" + "snapshot": "test/fixtures/reference-source/e98fc7955feb673b86ac9176a211136493d6247b011cb80995fb53464028f976.tri", + "sha256": "523153c5842623fd8277cb11c54ad6ef4a1f2c08e403fbafc1b40fbe10d1c109" }, { "alias": "lexer", @@ -168,26 +168,44 @@ { "alias": "source_stable", "path": "lib/source/stable.tri", - "snapshot": "test/fixtures/reference-source/b407662fec9436bfb7cc06eed2c14961174c6fa19863413ffc74d58ef72c5431.tri", - "sha256": "608f42975a2509dad402fecf83cf32515e96d7e2975b20f7d416482e194c2104" + "snapshot": "test/fixtures/reference-source/0d38a5a7511d4a08f648dfa28beaa312f9139bf9639f7779f2f83ac45134f439.tri", + "sha256": "7500a4b2e13cd64c18d061af47ee5c6203b72d138079cc545c2cf1efd53fac4f" }, { "alias": "native_math", "path": "lib/math/native.tri", "snapshot": "test/fixtures/reference-source/e9d0b87564d13695bef695c552e9707710d9147877f9d9dc5282c7335664cc57.tri", "sha256": "339e671e543ba04264b8e3014ee983124b48b09347caf32b9cae16a883088524" + }, + { + "alias": "io_v2", + "path": "lib/io-v2.tri", + "snapshot": "test/fixtures/reference-source/8b2e1cd6ef5acc8454a690875d2e8415869892f84c3fb5198c18fc8e665d0f32.tri", + "sha256": "a7780e7fca707a2535df0a4075db88496c6176e0f663cde0589b25b9554d5ad5" + }, + { + "alias": "io_v2_storage", + "path": "lib/io-v2/storage.tri", + "snapshot": "test/fixtures/reference-source/64e2248dbd4a05fc78b9d92cd775bb111951af95e384b91000e7f7aa32410013.tri", + "sha256": "921d60c39190fab48ad35096cd7a02fc21a143929ee179808ecb00b083cd3074" + }, + { + "alias": "io_v2_text", + "path": "lib/io-v2/text.tri", + "snapshot": "test/fixtures/reference-source/1ca2682e6a35d949196ff3b1d2aabed47af57e7ef43b8e32b2b7b19ca004f64f.tri", + "sha256": "678d6544e5618117fcc24b04932b6aacc0d669614922606cc7944e7001ea5ddd" } ], "acceptance": { "fresh_generations": 2, - "matching_manifests": 28, - "distinct_complete_trees": 903, - "matching_bundle_sha256": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e", + "matching_manifests": 31, + "matching_bundle_sha256": "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4", "matching_artifacts": { - "rebuilt-compiler.arboricx": "7f625f6290facf67a389d6d4e8a045bae8f4ab4f53ea42203f1334b27e4bcf95", - "build.arboricx": "186a08c9f0372cb6f8a82ce80640e230ca56c7a18db334033fbcf123cfc4f18d", - "build-stable.arboricx": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e" + "rebuilt-compiler.arboricx": "55fbbc19ad0945311ae4c42ec529bebef8b06f47a9ab9cad52da21e8010e9076", + "build.arboricx": "971d2a18c9af5cfb1b9b4e22f69dcd48987d130855e3fcfa0267824290716788", + "build-stable.arboricx": "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4" } }, - "limits": "Fresh stable pair with complete closure audits and generation-two smokes. Checksums bind bytes, not publisher identity; verification does not query Git." + "limits": "Fresh stable pair with complete closure audits and generation-two smokes. Checksums bind bytes, not publisher identity; verification does not query Git.", + "io_version": 2 } diff --git a/demos/arboricx-inspection/main.tri b/demos/arboricx-inspection/main.tri @@ -1,55 +1,27 @@ !import arboricx_app as App -!import arboricx_server as Registry -!import io * -!import socket as Socket +!import io_v2 as IO +!import handlers as H +!import handler_artifacts as Artifacts !export main !export startupAction -!export initializeThenServe -!export initializeStore -!export ensureRootDirectory -!export createRootDirectory +!export startupBody !export storeRoot -!export finishStartup -!export afterInitialization -!export afterRootDirectory -!export afterCreateDirectory +!export createRootDirectory +!export initializeStore +!export createView !export initializeView -!export serveView -!export serveRegistry -!export createSocket -!export afterSocketResult --- Preserve the actual application's executable tree, rather than rebuilding an --- observationally equivalent main. This bundle adds an inspection vocabulary. +-- Keep the actual application's executable tree, including its scoped startup. main = App.main +payload (t _ value) = value +payload _ = t +startupAction = payload (payload main) +startupBody = payload startupAction --- Ordinary named values recover these exact trees in the startup spine. +-- These are separately materialized views. The startup callback above remains +-- opaque: inspection never applies it to invent an owner or follow its effects. storeRoot = "./store" -createRootDirectory = createDirectory storeRoot -ensureRootDirectory = Registry.ensureDir storeRoot -initializeStore = Registry.ensureStore storeRoot - --- Structural projections, NOT applications of the extracted continuations. --- These helpers assume the current v1 envelope/Bind layouts. The acceptance --- harness checks their roles against the actual compiled server. -rightPayload (t _ (t _ value)) = value -rightPayload _ = t -leftPayload (t _ (t value _)) = value -leftPayload _ = t - -startupAction = rightPayload main -initializeThenServe = leftPayload startupAction -finishStartup = rightPayload startupAction --- Project from the actual main, not a separately recompiled equivalent wrapper. -afterInitialization = rightPayload initializeThenServe -afterRootDirectory = rightPayload initializeStore -afterCreateDirectory = rightPayload ensureRootDirectory - --- Separate materialized views: useful for inspection, not evidence that the --- main inspector followed or executed its opaque continuations. Running these --- exports as IO would perform effects; inspection does not. -initializeView = io initializeStore -serveRegistry = Registry.arboricxServer storeRoot "0.0.0.0" 8080 -serveView = io serveRegistry -createSocket = Socket.socket -afterSocketResult = rightPayload serveRegistry +createRootDirectory = IO.createDirectory storeRoot +initializeStore = H.runIOV2 (Artifacts.prepareFilesystemV2 storeRoot) +createView = IO.io createRootDirectory +initializeView = IO.io initializeStore diff --git a/demos/arboricx-inspection/tricu.workspace b/demos/arboricx-inspection/tricu.workspace @@ -1,11 +1,20 @@ tricu.workspace.v1 arboricx_app ../../tricu-apps/arboricxServer.tri +arboricx_handlers ../../lib/arboricx/handlers.tri arboricx_server ../../lib/arboricx/server.tri +arboricx_streaming_v2 ../../lib/arboricx/streaming-v2.tri base ../../lib/base.tri bytes ../../lib/bytes.tri conversions ../../lib/conversions.tri +handler_artifacts ../../lib/handlers/artifacts.tri +handler_log ../../lib/handlers/log.tri +handlers ../../lib/handlers.tri http ../../lib/http.tri io ../../lib/io.tri +io_v2 ../../lib/io-v2.tri +io_v2_artifacts ../../lib/io-v2/artifacts.tri +io_v2_http ../../lib/io-v2/http-transport.tri +io_v2_storage ../../lib/io-v2/storage.tri native_math ../../lib/math/native.tri parser ../../lib/parser.tri prelude ../../lib/prelude.tri diff --git a/demos/handlers/artifact-get.tri b/demos/handlers/artifact-get.tri @@ -0,0 +1,35 @@ +!import base as B +!import handlers as H +!import handler_log as Log +!import handler_artifacts as Artifacts +!import http as HTTP +!export getBundle +!export main + +-- The application chooses HTTP policy; the artifact service returns domain +-- Results. Read failures deliberately do not reveal filesystem paths to clients. +status ((t t), _) = 200 +status (t, (["invalid-digest" message], t)) = 400 +status _ = 404 +response ((t t), (bytes, t)) = HTTP.response 200 "application/vnd.arboricx.bundle" bytes +response (t, (["invalid-digest" message], t)) = HTTP.badRequestResponse message +response _ = HTTP.errorResponse 404 "Artifact unavailable\n" + +getBundle digest = H.label "bundle-get" <| do H.bind + reply <- Artifacts.load digest + logged <- Log.info [digest (status reply)] + case logged of + ((t t), _) = H.pure (response reply) + (t, (message, _)) = H.fail "application-failure" "logging" "emit" message + _ = H.fail "application-failure" "logging" "emit" "invalid logger reply" + +-- Substitute Artifacts.withFilesystem root for this scripted handler to read +-- real objects, then run with H.runIO inside an IO.io envelope. Host read grants +-- are still required. Capture remains an ordinary composable wrapper. +exampleStore "load" _ = H.pure (B.ok [0 255 65] t) +exampleStore operation payload = H.forward "artifacts" operation payload +main = getBundle "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + |> Log.withContext [["request" 7]] + |> H.handle "artifacts" exampleStore + |> Log.capture + |> H.runPure diff --git a/demos/handlers/main.tri b/demos/handlers/main.tri @@ -0,0 +1,13 @@ +!import base as B +!import handlers as H +!import handler_log as Log +!export main + +-- Application code knows neither where logs go nor who supplies request context. +application = do H.bind + Log.info "starting" + Log.info "finished" + H.pure 42 + +-- Pure: Result [42 events]. Put capture outside context so it sees enrichment. +main = Log.withContext [["request" 7]] application |> Log.capture |> H.runPure diff --git a/demos/interaction-inspection.tri b/demos/interaction-inspection.tri @@ -1,5 +1,5 @@ --- A materialized IO tree for read-only bundle inspection. Compiling and --- inspecting this module does not read settings.txt. Running it with --io does. +-- A materialized historical v1 tree for read-only bundle inspection. +-- Executing its settings.txt read requires a retained v1 runtime. !export main !export afterRead diff --git a/demos/interactionTrees.tri b/demos/interactionTrees.tri @@ -1,58 +1,25 @@ -!import prelude * -!import io * -!export * +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export asyncDemo --- Interaction Tree Effect Runtime --- --- The IO system is an interaction-tree effect runtime interpreted by a --- small-step machine with a cooperative scheduler. Primitive actions --- (putStr, readFile, writeFile, ...) are tagged nodes in an interaction --- tree. Sequencing is performed by the single generic `bind` constructor. --- --- pure x -- lift a pure value into IO --- bind action k -- run action, then apply k to its result --- thenIO a b -- run a, discard its result, then run b --- mapIO action f -- run action, then apply f to its result inside pure --- --- The runtime supports several effects beyond basic IO: --- ask -- read the current environment --- local f action -- run action with environment transformed by f --- get -- read the current mutable state --- put s -- replace the mutable state --- fork action -- spawn a concurrent task, returning a handle --- await handle -- wait for a forked task to complete --- yield -- yield control to the scheduler --- sleep ms -- suspend current task for N milliseconds --- --- File operations return a Result tree (see lib/base.tri): --- ok value -- pair true (pair value t) --- err msg -- pair false (pair msg t) --- --- Use onReadFile / onWriteFile for convenient branching. --- --- See demos/interactionTrees/ for smaller focused examples. - --- Cooperative async demo. --- fork runs an action in the background. --- sleep suspends the current task for N milliseconds. --- await waits for a forked task and returns its value. --- --- Here the child sleeps for 2 s while the parent prints immediately. --- The parent's message appears first, proving interleaving. - -asyncDemo = ( - bind (fork - (bind (sleep 2000) (_ : - bind (putStrLn "2000ms done sleeping!") (_ : - pure "child2000 done")))) - (handle2000 : - bind (fork - (bind (sleep 5000) (_ : - bind (putStrLn "5000ms done sleeping!") (_ : - pure "child5000 done")))) - (handle5000 : - bind (putStrLn "Parent first!") (_ : - bind (await handle5000) (_ : - await handle2000))))) - -main = io asyncDemo +-- Explicit IO v2 sequencing, scoped children and consumed join tickets. Sleep +-- permits other work, but promises neither exact wakeup time nor task ordering. +-- Only the parent writes stdout: concurrent writers need explicit coordination, +-- not an assumption that yield/sleep makes a multi-write message atomic. +line text = IO.require IO.stdout (writer : Text.writeText writer (B.append text "\n")) +waitFor [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" value] = IO.pure value + ["cancelled" reason _] = IO.cancelled reason + ["failed" problem cleanup] = IO.fail [problem cleanup] + _ = IO.fail "Invalid task outcome") +waitFor _ = IO.fail "Invalid spawn reply" +worker milliseconds message = IO.require (IO.sleepMilliseconds milliseconds) (_ : IO.pure message) +asyncDemo = IO.scope (_ : do IO.require + first <- IO.spawn 0 1 [] (_ : worker 2000 "2000ms done sleeping!") + second <- IO.spawn 0 1 [] (_ : worker 5000 "5000ms done sleeping!") + line "Parent first!" + IO.bind (waitFor first) (message : IO.require (line message) (_ : + IO.bind (waitFor second) (message : IO.require (line message) (_ : IO.pure "child2000 done"))))) +main = IO.io asyncDemo diff --git a/demos/interactionTrees/arboricxServer.tri b/demos/interactionTrees/arboricxServer.tri @@ -1,24 +1,17 @@ -!import prelude * -!import io * -!import arboricx_server * -!export * - --- Arboricx HTTP registry server demo. --- Run with --allow-write /tmp/store --allow-read /tmp/store --- --- Endpoints: --- GET /_arboricx/package/:name/:version -> exact release record --- GET /_arboricx/health -> "OK" --- POST /_arboricx/bundle -> upload bundle, returns hash --- GET /_arboricx/bundle/hash/:h -> download bundle by hash --- --- Example usage: --- curl http://localhost:9050/_arboricx/health --- curl -X POST --data-binary @mybundle.arboricx http://localhost:9050/_arboricx/bundle --- curl http://localhost:9050/_arboricx/bundle/hash/<hash> - -main = io (do bind - putStrLn "Starting Arboricx server on 127.0.0.1:9050" - do onOk_ - ensureStore "/tmp/store" - arboricxServer "/tmp/store" "127.0.0.1" 9050) +!import base as B +!import conversions as C +!import io_v2 as IO +!import arboricx_streaming_v2 as Server +!import arboricx_handlers as Registry +!export main +!export server +-- Native streaming registry with selectable address and connection count. +-- Source-owned ./store, 1-MiB body/object budget and bounded FIFO16 admission. +port ["ipv4" _ value] = value +port _ = 0 +ready listener = do IO.require + address <- IO.localAddress listener + writer <- IO.stdout + IO.require (IO.writeAll writer (B.append (C.showNumber (port address)) [10])) IO.pure +server root address count = IO.io (Server.serveCount root 1048576 Registry.stdoutLogV2 address 16 count ready) +main = IO.io (Server.serve "./store" 1048576 Registry.stdoutLogV2 (IO.ipv4 [127 0 0 1] 9050) 16 ready) diff --git a/demos/interactionTrees/echoServer.tri b/demos/interactionTrees/echoServer.tri @@ -1,29 +1,25 @@ -!import prelude * -!import io * -!import socket * -!export * - --- Main accept+echo loop. Recursion via y. -echoLoop = y (self : server : - withAccepted_ server - (err : - bind (putStrLn (append "accept error: " err)) (_ : - self server)) - (clientSock addr : - bind (putStrLn (append "client from " addr)) (_ : - onResult_ (recv clientSock 4096) - (err : - bind (closeSocket clientSock) (_ : - self server)) - (msg : - bind (send clientSock msg) (_ : - bind (closeSocket clientSock) (_ : - self server)))))) - -main = io ( - onOk_ socket (server : - onOk_ (bindSocket server "127.0.0.1" 0) (_ : - onOk_ (listen server 5) (_ : - onOk_ (getSocketName server) (port : - bind (putStrLn (append "Echo server listening on port " (showNumber port))) (_ : - echoLoop server)))))) +!import base as B +!import conversions as C +!import native_math as N +!import io_v2 as IO +!export main +!export server +-- A deliberately sequential, bounded-stream echo demonstration. A stalled peer +-- holds the single connection slot; there is no idle timeout or availability SLA. +port ["ipv4" _ value] = value +port _ = 0 +ready listener = do IO.require + address <- IO.localAddress listener + output <- IO.stdout + IO.require (IO.writeAll output (B.append (C.showNumber (port address)) [10])) IO.pure +once listener = IO.scope (owner : IO.require (IO.acceptTCP listener owner) + (client : IO.require (IO.copyStream client client) (_ : IO.pure t))) +serve 0 _ = IO.pure t +serve count listener = IO.bind (once listener) (_ : serve (N.predecessor count) listener) +forever listener = IO.bind (once listener) (_ : forever listener) +server address count = IO.io (IO.scope (owner : do IO.require + listener <- IO.listenTCP owner address 5 + IO.bind (ready listener) (_ : serve count listener))) +main = IO.io (IO.scope (owner : do IO.require + listener <- IO.listenTCP owner (IO.ipv4 [127 0 0 1] 0) 5 + IO.bind (ready listener) (_ : forever listener))) diff --git a/demos/interactionTrees/environment.tri b/demos/interactionTrees/environment.tri @@ -1,20 +1,20 @@ -!import prelude * -!import io * -!export * - --- Environment effects: ask and local. --- ask reads the current environment value. --- local f action runs action with the env transformed by f. --- --- The CLI starts with an empty (Leaf) environment. This demo uses --- local to inject a real string so that ask returns something readable. - -main = io <| - (bind - local (_ : "sandbox") - (bind ask (env : - bind (putStrLn (append "working in env: " env)) (_ : - pure "inside-done")))) - (outside : - bind (putStrLn (append "local returned: " outside)) (_ : - pure t)) +!import base as B +!import handlers as H +!import io_v2 as IO +!import io_v2_text as Text +!export main +-- Lexical application configuration, NOT the OS environment or a sandbox. +ask = H.request "configuration" "ask" t +reader value "ask" t = H.pure value +reader _ operation argument = H.forward "configuration" operation argument +line message = H.liftIO (IO.require IO.stdout (writer : + IO.require (Text.writeText writer (B.append message "\n")) IO.pure)) +inside = do H.bind + value <- ask + line (B.append "working in env: " value) + H.pure "inside-done" +main = IO.io (H.runIOV2 (H.handle "configuration" (reader "outer") (do H.bind + value <- H.handle "configuration" (reader "demo") inside + line (B.append "local returned: " value) + outside <- ask + line (B.append "outside env: " outside)))) diff --git a/demos/interactionTrees/forkAwait.tri b/demos/interactionTrees/forkAwait.tri @@ -1,18 +1,18 @@ -!import prelude * -!import io * -!export * - --- Basic fork and await. --- fork spawns a concurrent task and returns a handle. --- await blocks until the task completes and returns its value. - -worker = msg : - bind (putStrLn (append "working: " msg)) (_ : - pure (append msg "-result")) - -main = io <| - (bind (fork (worker "job1")) (h1 : - bind (fork (worker "job2")) (h2 : - bind (await h1) (r1 : - bind (await h2) (r2 : - putStrLn (append "Got " (append r1 (append " and " r2)))))))) +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export worker +-- Structured children return work records; the parent serializes their output. +-- Task completion does not imply deterministic scheduling or atomic shared writes. +worker message = IO.require IO.yield (_ : IO.pure [(B.append "working: " message) (B.append message "-result")]) +consume writer [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" [message value]] = IO.require (Text.writeText writer (B.append message "\n")) (_ : IO.pure value) + _ = IO.fail outcome) +consume _ _ = IO.fail "Invalid spawn reply" +main = IO.io (IO.scope (_ : do IO.require + first <- IO.spawn 0 1 [] (_ : worker "job1") + second <- IO.spawn 0 1 [] (_ : worker "job2") + writer <- IO.stdout + IO.bind (consume writer first) (a : IO.bind (consume writer second) (b : + IO.require (Text.writeText writer (B.append "Got " (B.append a (B.append " and " (B.append b "\n"))))) IO.pure)))) diff --git a/demos/interactionTrees/getLineAsync.tri b/demos/interactionTrees/getLineAsync.tri @@ -1,27 +1,22 @@ -!import prelude * -!import io * -!export * --- Manual test for async getLine --- --- Run with: --- nix run .# -- eval -f demos/async-getline-test.tri --io --- --- Expected behaviour: --- 1. You immediately see: --- Please enter your first name: --- (this printed before you typed anything) --- (this second line also printed before you typed anything) --- 2. You type your name and press Enter. --- 3. You see: --- Hello, <name>! +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main - -main = io <| - bind (fork getLine) (h : - bind (putStr "Please enter your first name: ") (_ : - bind (putStr "\n(this printed before you typed anything)\n") (_ : - bind (putStr "\n(this second line also printed before you typed anything)\n") (_ : - bind (await h) (name : - bind (putStr "Hello, ") (_ : - bind (putStr name) (_ : - putStr "!\n"))))))) +-- One child reads a bounded line; only the parent writes. Prompt availability +-- does not require input first. EOF is not an empty name, CR is not stripped, +-- and neither sleep nor scheduler timing is used to coordinate the prompt. +name reader = IO.require (Text.readLine (Text.lineCursor reader) 4096) (reply : case reply of + [["line" text _] _] = IO.pure (B.append "Hello, " (B.append text "!\n")) + [["eof"] _] = IO.pure "End of input.\n" + _ = IO.fail "Invalid line reply") +main = IO.io (IO.scope (_ : do IO.require + reader <- IO.stdin + writer <- IO.stdout + spawned <- IO.spawn 0 1 [] (_ : name reader) + Text.writeText writer "Please enter your first name: \n(this printed before you typed anything)\n(this second line also printed before you typed anything)\n" + case spawned of + [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" greeting] = IO.require (Text.writeText writer greeting) IO.pure + _ = IO.fail outcome) + _ = IO.fail "Invalid name task")) diff --git a/demos/interactionTrees/greet.tri b/demos/interactionTrees/greet.tri @@ -1,10 +1,6 @@ -!import prelude * -!import io * -!export * - --- Greet and return a pure value. --- putStrLn writes to stdout; pure lifts "done" into IO. - -main = io <| - bind (putStrLn (append "Hello, " "tricu")) - (_ : pure "") +!import io_v2 as IO +!import io_v2_text as Text +!export main +-- Explicit v2 output, with ordinary write failure checked before success. +main = IO.io (IO.require IO.stdout (writer : + IO.require (Text.writeText writer "Hello, tricu\n") (_ : IO.pure ""))) diff --git a/demos/interactionTrees/httpServer.tri b/demos/interactionTrees/httpServer.tri @@ -1,17 +1,31 @@ -!import prelude * -!import io * -!import socket * -!import http * -!export * - -myRouter = (method path headers body : - matchBool - (okResponse (append "Hello from " (append path "\n"))) - (errorResponse 405 "Method not allowed\n") - (strEq? method "GET")) - -main = io ( - onOk_ socket (server : - onOk_ (bindSocket server "127.0.0.1" 9050) (_ : - onOk_ (listen server 5) (_ : - serveForever server (httpHandler myRouter))))) +!import base as B +!import conversions as C +!import native_math as N +!import io_v2 as IO +!import io_v2_http as Transport +!import http as HTTP +!export main +!export server +-- Sequential example, not the registry's bounded concurrent admission policy. +router "GET" path _ _ = IO.pure (HTTP.okResponse (B.append "Hello from " (B.append path "\n"))) +router _ _ _ _ = IO.pure (HTTP.errorResponse 405 "Method not allowed\n") +port ["ipv4" _ value] = value +port _ = 0 +ready listener = do IO.require + address <- IO.localAddress listener + output <- IO.stdout + IO.require (IO.writeAll output (B.append (C.showNumber (port address)) [10])) IO.pure +connection client = IO.bind (Transport.recvHeaders client) (reply : case reply of + ((t t), (bytes, t)) = Transport.handle router client (HTTP.parseRequestLine bytes) + (t, (431, t)) = Transport.respond client "" (HTTP.errorResponse 431 "Request Header Fields Too Large\n") + _ = Transport.respond client "" (HTTP.badRequestResponse "Bad Request\n")) +once listener = IO.scope (owner : IO.require (IO.acceptTCP listener owner) connection) +serve 0 _ = IO.pure t +serve count listener = IO.bind (once listener) (_ : serve (N.predecessor count) listener) +forever listener = IO.bind (once listener) (_ : forever listener) +server address count = IO.io (IO.scope (owner : do IO.require + listener <- IO.listenTCP owner address 5 + IO.bind (ready listener) (_ : serve count listener))) +main = IO.io (IO.scope (owner : do IO.require + listener <- IO.listenTCP owner (IO.ipv4 [127 0 0 1] 9050) 5 + IO.bind (ready listener) (_ : forever listener))) diff --git a/demos/interactionTrees/safeRead.tri b/demos/interactionTrees/safeRead.tri @@ -1,16 +1,15 @@ -!import prelude * -!import io * -!export * - --- readFile returns a Result. matchResult branches on ok / err. --- Run with --allow-read PATH or --unsafe-io. - -safeRead = (path : - bind (readFile path) - (result : - matchResult - (err rest : pure "ERROR: Unable to read file") - (contents rest : pure contents) - result)) - -main = io (safeRead "demos/interactionTrees/greet.tri") +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export run +!export safeRead +-- Ordinary read failures are application values; cleanup faults still fail the +-- scope. Generic UTF-8 (including initial BOM), with an explicit 1-MiB budget. +safeRead path = IO.bind (IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + Text.readText reader 1048576)) (reply : case reply of + ((t t), (text, t)) = IO.pure text + (t, (_, t)) = IO.pure "ERROR: Unable to read file" + _ = IO.fail "Invalid file reply") +main = IO.io (safeRead "demos/interactionTrees/greet.tri") +run path = IO.io (safeRead path) diff --git a/demos/interactionTrees/shout.tri b/demos/interactionTrees/shout.tri @@ -1,23 +1,16 @@ -!import prelude * -!import io * -!export * - --- Transform an IO result. --- mapIO applies a pure function to the value produced by an action. --- Run with --allow-read PATH or --unsafe-io. - -safeRead = (path : - bind (readFile path) - (result : - matchResult - (err rest : pure "missing") - (contents rest : pure contents) - result)) - -shout = (path : - mapIO (safeRead path) - (text : append text "!!!")) - -main = io (bind - (shout "demos/interactionTrees/greet.tri") - (text : putStrLn text)) +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export run +!export shout +safeRead path = IO.bind (IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + Text.readText reader 1048576)) (reply : case reply of + ((t t), (text, t)) = IO.pure text + (t, (_, t)) = IO.pure "missing" + _ = IO.fail "Invalid file reply") +shout path = IO.bind (safeRead path) (text : IO.pure (B.append text "!!!")) +main = IO.io (IO.bind (shout "demos/interactionTrees/greet.tri") (text : + IO.require IO.stdout (writer : IO.require (Text.writeText writer (B.append text "\n")) IO.pure))) +run path = IO.io (shout path) diff --git a/demos/interactionTrees/state.tri b/demos/interactionTrees/state.tri @@ -1,22 +1,13 @@ -!import prelude * -!import io * -!export * - --- Mutable state via get and put. --- get reads the current state. --- put replaces the state. --- --- The CLI starts with an empty (Leaf) state. This demo puts --- readable strings and prints them back out. - -main = io <| - bind (put "idle") (_ : - bind get (s1 : - bind (putStrLn (append "state: " s1)) (_ : - bind (put "running") (_ : - bind get (s2 : - bind (putStrLn (append "state: " s2)) (_ : - bind (put "done") (_ : - bind get (s3 : - bind (putStrLn (append "state: " s3)) (_ : - pure t))))))))) +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export runState +-- A sequential state machine passes its state explicitly. There is no implicit +-- host get/put slot and no claim of a mutable cell shared across child tasks. +line writer state = Text.writeText writer (B.append "state: " (B.append state "\n")) +runState writer "idle" = IO.require (line writer "idle") (_ : runState writer "running") +runState writer "running" = IO.require (line writer "running") (_ : runState writer "done") +runState writer "done" = IO.require (line writer "done") (_ : IO.pure t) +runState _ _ = IO.fail "Unknown state" +main = IO.io (IO.require IO.stdout (writer : runState writer "idle")) diff --git a/demos/interactionTrees/writeThenRead.tri b/demos/interactionTrees/writeThenRead.tri @@ -1,20 +1,20 @@ -!import prelude * -!import io * -!export * - --- Write a file, then read it back. --- thenIO discards the writeFile Result and continues. --- Run with --unsafe-io (needs both read and write permissions). - -writeThenRead = (path text : - thenIO - (writeFile path text) - (readFile path)) - -main = io <| - (bind (writeThenRead "/tmp/tricu-demo.txt" "hello from tricu") - (result : - matchResult - (err rest : putStrLn "error") - (contents rest : putStrLn contents) - result)) +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export run +!export writeThenRead +-- Check write/close success before opening a reader; do not discard the write +-- Result. Select the output path explicitly, rather than clobbering shared /tmp. +writeThenRead path text = IO.scope (owner : do IO.onOk + writer <- IO.openWriter owner path + Text.writeText writer text + IO.close writer + reader <- IO.openReader owner path + Text.readText reader 1048576) +main = IO.io (IO.require (IO.environmentVariable "TRICU_DEMO_OUTPUT") (selection : case selection of + ["value" path] = IO.require (writeThenRead path "hello from tricu") (text : + IO.require IO.stdout (writer : IO.require (Text.writeText writer (B.append text "\n")) IO.pure)) + ["missing"] = IO.fail "Set TRICU_DEMO_OUTPUT to an explicit output path" + _ = IO.fail "Invalid environment reply")) +run path text = IO.io (writeThenRead path text) diff --git a/demos/interactionTrees/yield.tri b/demos/interactionTrees/yield.tri @@ -1,33 +1,35 @@ -!import prelude * -!import io * -!export * +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!import handlers as H +!import handler_log as Log +!export main +!export chatter --- Cooperative scheduling with yield. --- yield returns control to the scheduler so other tasks can run. --- --- Two tasks print alternately because each yields after every line. - ---chatter = (name n : --- bind (putStrLn (append name " says 1")) (_ : --- bind yield (_ : --- bind (putStrLn (append name " says 2")) (_ : --- bind yield (_ : --- bind (putStrLn (append name " says 3")) (_ : --- pure n)))))) - -chatter = name n : bind <| - putStrLn (append name " says 1") (_ : - bind yield (_ : - bind (putStrLn (append name " says 2")) (_ : - bind yield (_ : - bind (putStrLn (append name " says 3")) (_ : - pure n))))) - - -main = io <| - bind (fork (chatter "A" "doneA")) (ha : - bind (fork (chatter "B" "doneB")) (hb : - bind yield (_ : - bind (await ha) (a : - bind (await hb) (b : - putStrLn (append "Finished: " (append a (append " " b)))))))) +-- Yield offers scheduling opportunities, never guaranteed alternation. Each +-- child installs its own collection; the parent alone writes the observed events. +-- No implicit shared state slot, inherited collector or concurrent stdout writes. +chatter name result = H.runIOV2 (Log.capture (do H.bind + Log.info (B.append name " says 1") + H.liftIO (IO.require IO.yield IO.pure) + Log.info (B.append name " says 2") + H.liftIO (IO.require IO.yield IO.pure) + Log.info (B.append name " says 3") + H.pure result)) +printEvents _ [] = IO.pure t +printEvents writer [["info" _ message] | rest] = IO.require + (Text.writeText writer (B.append message "\n")) (_ : printEvents writer rest) +printEvents _ _ = IO.fail "Invalid child log event" +consume writer [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" [value events]] = IO.bind (printEvents writer events) (_ : IO.pure value) + ["cancelled" reason _] = IO.cancelled reason + ["failed" problem cleanup] = IO.fail [problem cleanup] + _ = IO.fail "Invalid task outcome") +consume _ _ = IO.fail "Invalid spawn reply" +main = IO.io (IO.scope (_ : do IO.require + first <- IO.spawn 0 1 [] (_ : chatter "A" "doneA") + second <- IO.spawn 0 1 [] (_ : chatter "B" "doneB") + IO.yield + writer <- IO.stdout + IO.bind (consume writer first) (a : IO.bind (consume writer second) (b : + IO.require (Text.writeText writer (B.append "Finished: " (B.append a (B.append " " (B.append b "\n"))))) (_ : IO.pure t))))) diff --git a/demos/io-v2/clock.tri b/demos/io-v2/clock.tri @@ -0,0 +1,32 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export elapsed +!export yielded +!export invalidSleep +!export cancelledWait +!export scopeWait +elapsed milliseconds = IO.io (do IO.require + before <- IO.monotonicNanoseconds + IO.sleepMilliseconds milliseconds + after <- IO.monotonicNanoseconds + IO.pure (N.subtract after before)) +yielded = IO.io IO.yield +invalidSleep value = IO.io (IO.sleepMilliseconds value) +cancelledWait = IO.io (IO.scope (_ : IO.require + (IO.spawn 1 1 [] (_ : IO.require (IO.sleepMilliseconds 9223372036854775807) (_ : IO.pure 99))) + (spawned : case spawned of + [task ticket] = do IO.require + IO.sleepMilliseconds 20 + IO.cancel task "timer-stop" + outcome <- IO.join ticket + IO.pure outcome + _ = IO.fail "Invalid timer spawn reply"))) +-- Normal scope exit waits for its child even without a retained result ticket. +scopeWait = IO.io (do IO.require + before <- IO.monotonicNanoseconds + IO.bind (IO.scope (_ : IO.require + (IO.spawn 0 0 [] (_ : IO.require (IO.sleepMilliseconds 20) (_ : IO.pure t))) + (_ : IO.pure t))) (_ : do IO.require + after <- IO.monotonicNanoseconds + IO.pure (N.subtract after before))) diff --git a/demos/io-v2/context.tri b/demos/io-v2/context.tri @@ -0,0 +1,5 @@ +!import io_v2 as IO +!export directory +!export variable +directory = IO.io IO.workingDirectory +variable name = IO.io (IO.environmentVariable name) diff --git a/demos/io-v2/copy.tri b/demos/io-v2/copy.tri @@ -0,0 +1,78 @@ +!import base as B +!import io_v2 as IO +!import handlers as H +!export cancelledCopy +!export copy +!export handledCopy +!export borrowedCopy +!export transferredCopy +!export joinedOnExitCopy +!export handledChildCopy + +pump reader writer = IO.copyStream reader writer + +copy source destination = IO.io <| IO.scope (owner : do IO.onOk + reader <- IO.openReader owner source + writer <- IO.openWriter owner destination + pump reader writer) + +-- A captured service supplies the implementation, while the caller requests +-- policy-level work. The host scope, not handler removal, owns both resources. +service reader writer "run" _ = H.liftIO (pump reader writer) +service _ _ operation argument = H.forward "copy" operation argument +handledCopy source destination = IO.io <| IO.scope (owner : do IO.onOk + reader <- IO.openReader owner source + writer <- IO.openWriter owner destination + H.runIO (H.handle "copy" (service reader writer) (H.request "copy" "run" t))) + + +joinCopy spawned = case spawned of + [task ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" value] = IO.pure value + ["failed" diagnostic cleanup] = IO.fail [diagnostic cleanup] + ["cancelled" reason cleanup] = IO.fail [reason cleanup] + _ = IO.fail "Invalid task outcome") + _ = IO.fail "Invalid spawn reply" + +borrowedCopy source destination = IO.io <| IO.scope (owner : do IO.require + reader <- IO.openReader owner source + writer <- IO.openWriter owner destination + spawned <- IO.spawn 0 1 [] (_ : pump reader writer) + joinCopy spawned) + +transferredPump [reader writer] = pump reader writer +transferredPump _ = IO.fail "Invalid transferred handles" +transferredCopy source destination = IO.io <| IO.scope (owner : do IO.require + reader <- IO.openReader owner source + writer <- IO.openWriter owner destination + spawned <- IO.spawn 0 1 [reader writer] transferredPump + joinCopy spawned) + +-- No ticket and no explicit join: scope return still waits for complete copying. +joinedOnExitCopy source destination = IO.io <| IO.scope (owner : do IO.require + reader <- IO.openReader owner source + writer <- IO.openWriter owner destination + IO.spawn 0 0 [] (_ : pump reader writer) + IO.pure (B.ok t t)) + +handledChildCopy source destination = IO.io <| IO.scope (owner : do IO.require + reader <- IO.openReader owner source + writer <- IO.openWriter owner destination + H.runIOV2 (H.handle "copy" (service reader writer) (do H.bind + reply <- H.spawnV2 0 1 [] (_ : H.request "copy" "run" t) + H.liftIO (IO.require (IO.pure reply) joinCopy)))) + +-- Cancellation after one acknowledged chunk is not rollback. The supervisor +-- observes the outcome only after the transferred handles have been released. +cancelPump [reader writer] = IO.require (IO.readSome reader 4096) (reply : case reply of + ("eof", t) = IO.cancelled "requested" + ("data", bytes) = IO.require (IO.writeAll writer bytes) (_ : IO.cancelled "requested") + _ = IO.fail "Invalid read reply") +cancelPump _ = IO.fail "Invalid transferred handles" +cancelledCopy source destination = IO.io <| IO.scope (owner : do IO.require + reader <- IO.openReader owner source + writer <- IO.openWriter owner destination + spawned <- IO.spawn 1 1 [reader writer] cancelPump + case spawned of + [task ticket] = IO.require (IO.join ticket) IO.pure + _ = IO.fail "Invalid spawn reply") diff --git a/demos/io-v2/directory.tri b/demos/io-v2/directory.tri @@ -0,0 +1,46 @@ +!import io_v2 as IO +!import base as B +!import native_math as N +!export inventory +!export count +!export initialize +!export removeFile +!export removeDirectory +!export rename +!export metadata + +-- Caller-owned accumulation: inventory intentionally retains all entries, count +-- consumes each entry without retaining names. Neither assumes enumeration order. +walk directory root entries = IO.require (IO.nextDirectory directory) (reply : case reply of + ("eof", t) = IO.pure entries + ("entry", name) = IO.require (IO.lstatPath (B.append root (B.append "/" name))) + (status : walk directory root (B.pair [name status] entries)) + _ = IO.fail "Invalid directory reply") +inventory root = IO.io <| IO.scope (owner : IO.require (IO.openDirectory owner root) + (directory : walk directory root [])) + +-- Force the small counter before the next host request; lazy successor chains +-- would retain pending arithmetic even though directory names are discarded. +forceCounter t next = next t +forceCounter (t bit rest) next = case bit of + t = forceCounter rest next + (t t) = forceCounter rest next + _ = IO.fail "Invalid counter" +forceCounter _ _ = IO.fail "Invalid counter" +countLoop directory total = forceCounter total (_ : IO.require (IO.nextDirectory directory) (reply : case reply of + ("eof", t) = IO.pure total + ("entry", _) = countLoop directory (N.successor total) + _ = IO.fail "Invalid directory reply")) +count root = IO.io <| IO.scope (owner : IO.require (IO.openDirectory owner root) + (directory : countLoop directory 0)) + +-- Ordered nonrecursive initialization like the compiler's store/cache callers. +initialize root = IO.io <| IO.onOk (IO.ensureDirectory root) (_ : + IO.ensureDirectory (B.append root "/cache")) +removeFile path = IO.io (IO.removeFile path) +removeDirectory path = IO.io (IO.removeDirectory path) +rename source destination = IO.io (IO.renamePath source destination) +metadata path = IO.io <| do IO.bind + followed <- IO.statPath path + unfollowed <- IO.lstatPath path + IO.pure [followed unfollowed] diff --git a/demos/io-v2/indexed-read.tri b/demos/io-v2/indexed-read.tri @@ -0,0 +1,16 @@ +!import io_v2 as IO +!export read +!export probe + +-- No store layout or publication policy is native. Supply an exact byte frame, +-- expected indexed-object digest, private-spool directory, and disk-size budget. +read path directory limit size digest = IO.io <| IO.scope (owner : do IO.require + reader <- IO.openReader owner path + IO.readIndexedTree reader directory limit size digest) + +-- Preserve ordinary failures and observe consumption without retrying a frame. +probe path directory limit size digest = IO.io <| IO.scope (owner : do IO.require + reader <- IO.openReader owner path + IO.bind (IO.readIndexedTree reader directory limit size digest) (decoded : do IO.require + position <- IO.filePosition reader + IO.pure [decoded position])) diff --git a/demos/io-v2/indexed-write.tri b/demos/io-v2/indexed-write.tri @@ -0,0 +1,44 @@ +!import io_v2 as IO +!import base as B +!export hash +!export write +!export publish +!export roundTrip + +hash limit tree = IO.io (IO.hashIndexedTree limit tree) + +-- In-place transport, deliberately not atomic replacement. Failed preflight does +-- not touch this already-open writer; opening an absent update file creates it. +write path limit digest tree = IO.io <| IO.scope (owner : do IO.onOk + writer <- IO.openUpdate owner path + IO.writeIndexedTree writer limit digest tree) + +-- Source owns naming/publication and exact winner verification. "exists" means +-- byte-verified at observation time, not crash durability or future immutability. +publish directory limit tree = IO.io <| IO.scope (owner : do IO.onOk + info <- IO.hashIndexedTree limit tree + case info of + [size digest] = do IO.onOk + stage <- IO.openStage owner directory + IO.writeIndexedTree stage limit digest tree + IO.sealStage stage + state <- IO.publishNew stage (B.append directory (B.append "/" digest)) + case state of + "published" = IO.pure (B.ok [state size digest] t) + "exists" = do IO.onOk + reader <- IO.openReader owner (B.append directory (B.append "/" digest)) + IO.verifyIndexedTree reader limit tree + end <- IO.readSome reader 1 + case end of + ("eof", t) = IO.pure (B.ok [state size digest] t) + _ = IO.pure (B.err ["invalid-data" "indexed-file/verify" "trailing bytes" "unknown"] t) + _ = IO.fail "Invalid publication reply" + _ = IO.fail "Invalid indexed hash reply") + +-- A shared DAG enters and leaves without ever becoming an expanded source byte +-- list. This fixture expects canonical input; the writer checks its digest again. +roundTrip input output directory limit size digest = IO.io <| IO.scope (owner : do IO.onOk + reader <- IO.openReader owner input + tree <- IO.readIndexedTree reader directory limit size digest + writer <- IO.openWriter owner output + IO.writeIndexedTree writer limit digest tree) diff --git a/demos/io-v2/lines.tri b/demos/io-v2/lines.tri @@ -0,0 +1,50 @@ +!import io_v2 as IO +!import io_v2_text as Text +!import base as B +!import native_math as N +!export collect +!export count +!export first +!export stdinLines +!export cancelled +!export closed + +-- Collection is only a test/demo convenience; count discards each line instead. +walk cursor limit events = IO.bind (Text.readLine cursor limit) (reply : case reply of + ((t t), ([event next], t)) = case event of + ["eof"] = IO.pure (B.ok (B.reverse events) t) + _ = walk next limit (B.pair event events) + (t, (cause, t)) = IO.pure (B.err [(B.reverse events) cause] t) + _ = IO.fail "Invalid line Result") +force t next = next t +force (t bit rest) next = case bit of + t = force rest next + (t t) = force rest next + _ = IO.fail "Invalid count" +force _ _ = IO.fail "Invalid count" +countLoop cursor limit n = force n (_ : IO.require (Text.readLine cursor limit) (reply : case reply of + [["eof"] _] = IO.pure n + [_ next] = case N.add n 1 of + number = countLoop next limit number + _ = IO.fail "Invalid line Result")) +collect path limit = IO.io (IO.scope (owner : IO.require (IO.openReader owner path) + (reader : walk (Text.lineCursor reader) limit []))) +count path limit = IO.io (IO.scope (owner : IO.require (IO.openReader owner path) + (reader : countLoop (Text.lineCursor reader) limit 0))) +first path limit = IO.io (IO.scope (owner : IO.require (IO.openReader owner path) + (reader : IO.bind (Text.readLine (Text.lineCursor reader) limit) (reply : case reply of + ((t t), ([event _], t)) = IO.pure (B.ok event t) + _ = IO.pure reply)))) +stdinLines limit = IO.io (IO.require IO.stdin (reader : walk (Text.lineCursor reader) limit [])) +cancelled = IO.io (IO.scope (_ : do IO.require + reader <- IO.stdin + spawned <- IO.spawn 1 1 [] (_ : Text.readLine (Text.lineCursor reader) 4096) + case spawned of + [task ticket] = do IO.require + IO.sleepMilliseconds 50 + IO.cancel task "line-stop" + outcome <- IO.join ticket + IO.pure outcome + _ = IO.fail "Invalid line task")) +closed path limit = IO.io (IO.bind (IO.scope (owner : IO.require (IO.openReader owner path) + (reader : IO.pure (Text.lineCursor reader)))) (cursor : Text.readLine cursor limit)) diff --git a/demos/io-v2/objects.tri b/demos/io-v2/objects.tri @@ -0,0 +1,14 @@ +!import io_v2 as IO +!import io_v2_objects as O +!export put +!export get +!export copy +!export putCategory + +put root limit tree = IO.io (O.putTree root limit tree) +get root limit digest = IO.io (O.getTree root limit digest) +copy source destination limit digest = IO.io <| IO.onOk (O.getTree source limit digest) + (tree : O.putTree destination limit tree) +putCategory root limit tree = IO.io <| IO.bind (O.putTree root limit tree) (reply : case reply of + (t, ([category _ _ _], t)) = IO.pure category + _ = IO.fail "Expected operation failure") diff --git a/demos/io-v2/positioned.tri b/demos/io-v2/positioned.tri @@ -0,0 +1,39 @@ +!import io_v2 as IO +!export patch +!export append +!export appendGuards +!export badOffset + +-- Bounded edits and inspection, not atomic publication. Scope owns the file; +-- every operation uses the same logical cursor, including read/write switches. +patch path offset bytes size = IO.io <| IO.scope (owner : do IO.require + file <- IO.openUpdate owner path + original <- IO.fileSize file + IO.seekFile file offset + start <- IO.filePosition file + IO.writeAll file bytes + after <- IO.filePosition file + IO.resizeFile file size + afterResize <- IO.filePosition file + resized <- IO.fileSize file + IO.seekFile file 0 + prefix <- IO.readSome file 4096 + IO.pure [original start after afterResize resized prefix]) + +append path bytes = IO.io <| IO.scope (owner : do IO.require + file <- IO.openAppender owner path + before <- IO.fileSize file + IO.writeAll file bytes + after <- IO.fileSize file + IO.pure [before after]) + +appendGuards path = IO.io <| IO.scope (owner : IO.require (IO.openAppender owner path) + (file : do IO.bind + seek <- IO.seekFile file 0 + position <- IO.filePosition file + resized <- IO.resizeFile file 0 + read <- IO.readSome file 1 + IO.pure [seek position resized read])) + +badOffset path offset = IO.io <| IO.scope (owner : IO.require (IO.openUpdate owner path) + (file : IO.seekFile file offset)) diff --git a/demos/io-v2/process.tri b/demos/io-v2/process.tri @@ -0,0 +1,38 @@ +!import io_v2 as IO +!export pipeline +!export normalExit +!export raw + +-- Child stdout/stderr are drained concurrently while the parent pumps stdin. +-- All three streams use bounded chunks; output is written to files, not collected. +-- Executable and argv are caller supplied; using a shell requires naming one. +copy reader writer = IO.require (IO.copyStream reader writer) IO.pure +completed ticket = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" value] = IO.pure value + _ = IO.fail outcome) +waitCopy [_ ticket] = completed ticket +waitCopy _ = IO.fail "Invalid copy task reply" +pump owner source destination errors spawned = case spawned of + [task ticket input output error] = do IO.require + reader <- IO.openReader owner source + writer <- IO.openWriter owner destination + errorWriter <- IO.openWriter owner errors + stdoutTask <- IO.spawn 0 1 [] (_ : copy output writer) + stderrTask <- IO.spawn 0 1 [] (_ : copy error errorWriter) + IO.bind (copy reader input) (_ : do IO.require + IO.close input + IO.bind (waitCopy stdoutTask) (_ : IO.bind (waitCopy stderrTask) (_ : completed ticket))) + _ = IO.fail "Invalid process reply" +pipeline [executable arguments directory environment grace source destination errors] = IO.io (IO.scope (owner : + IO.require (IO.spawnProcess executable arguments directory environment grace) + (pump owner source destination errors))) +pipeline _ = IO.io (IO.fail "Invalid pipeline arguments") +normalExit [executable arguments] = IO.io (IO.scope (_ : + IO.require (IO.spawnProcess executable arguments ["inherit"] ["inherit"] 20) + (spawned : case spawned of + [_ _ input _ _] = IO.require (IO.close input) (_ : IO.pure 42) + _ = IO.fail "Invalid process reply"))) +normalExit _ = IO.io (IO.fail "Invalid process arguments") +raw [executable arguments directory environment grace] = IO.io + (IO.spawnProcess executable arguments directory environment grace) +raw _ = IO.io (IO.fail "Invalid raw arguments") diff --git a/demos/io-v2/publish.tri b/demos/io-v2/publish.tri @@ -0,0 +1,76 @@ +!import base as B +!import io_v2 as IO +!import io_v2_artifacts as Artifacts +!import handlers as H +!export failedAfterFull +!export invalidChunk +!export badDigest +!export nestedProducer +!export forkedProducer +!export publish +!export cancelled +!export earlySuccess +!export ignoresWriteFailure +!export emptyChunk +!export overrideWrite +!export rejectedDestination +!export malformedDestination + +pump reader = H.bind (H.liftIO (IO.readSome reader 4096)) (reply : case reply of + ((t t), (("eof", t), t)) = H.pure (B.ok t t) + ((t t), (("data", bytes), t)) = H.bind (Artifacts.write bytes) (written : case written of + ((t t), (t, t)) = pump reader + _ = H.pure written) + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "reader" "read" "Invalid read reply") + +publish source directory expected = IO.io <| IO.scope (owner : IO.require + (IO.openReader owner source) (reader : + H.runIOV2 (Artifacts.publish directory expected (H.label "file-producer" (pump reader))))) + +cancelled directory = IO.io <| H.runIOV2 (Artifacts.publish directory 3 (do H.bind + Artifacts.write [97 98 99] + H.liftIO (IO.cancelled "producer cancelled"))) +earlySuccess directory = IO.io <| H.runIOV2 (Artifacts.publish directory 3 (H.pure (B.ok t t))) +ignoresWriteFailure directory = IO.io <| H.runIOV2 (Artifacts.publish directory 1 (do H.bind + Artifacts.write [97 98 99] + H.pure (B.ok t t))) +emptyChunk directory = IO.io <| H.runIOV2 (Artifacts.publish directory 0 (do H.bind + Artifacts.write [] + H.pure (B.ok t t))) +acknowledge _ _ = H.pure (B.ok t t) +overrideWrite directory = IO.io <| H.runIOV2 (Artifacts.publish directory 3 + (H.handle "artifact-stream" acknowledge (do H.bind + Artifacts.write [97 98 99] + H.pure (B.ok t t)))) + +failedAfterFull directory = IO.io <| H.runIOV2 (Artifacts.publish directory 3 (do H.bind + Artifacts.write [97 98 99] + H.pure (B.err "Producer failed" t))) +invalidChunk directory = IO.io <| H.runIOV2 (Artifacts.publish directory 1 (do H.bind + Artifacts.write [256] + H.pure (B.ok t t))) +corruptDigest "run" (10, ("digest", (1, ("finish", _)))) = H.pure (B.ok "../escape" t) +corruptDigest operation argument = H.forward "host.io" operation argument +observe computation = IO.io <| IO.require + (IO.spawn 1 1 [] (_ : H.runIOV2 computation)) + (spawned : case spawned of + [task ticket] = IO.require (IO.join ticket) IO.pure + _ = IO.fail "Invalid spawn reply") +badDigest directory = observe (H.handle "host.io" corruptDigest + (Artifacts.publish directory 3 (do H.bind + Artifacts.write [97 98 99] + H.pure (B.ok t t)))) + +observeBoundary directory producer = observe (H.label "outer" + (Artifacts.publish directory 0 (H.label "inner" producer))) +nestedProducer directory = observeBoundary directory (H.scopeV2 (_ : H.pure (B.ok t t))) +forkedProducer directory = observeBoundary directory (H.spawnV2 0 0 [] (_ : H.pure (B.ok t t))) +rejectedDestination directory = IO.io (H.runIOV2 (Artifacts.publishWith directory 3 + (_ : H.pure (B.err "Destination rejected" t)) (do H.bind + Artifacts.write [97 98 99] + H.pure (B.ok t t)))) +malformedDestination directory = observe (Artifacts.publishWith directory 3 + (_ : H.pure 0) (do H.bind + Artifacts.write [97 98 99] + H.pure (B.ok t t))) diff --git a/demos/io-v2/registry.tri b/demos/io-v2/registry.tri @@ -0,0 +1,41 @@ +!import base as B +!import conversions as C +!import io_v2 as IO +!import arboricx_handlers as Registry +!import arboricx_streaming_v2 as Server +!import handlers as H +!export main +!export server +!export limited +!export badLogger +!export listen +!export cancelledServer +port ["ipv4" _ value] = value +port _ = 0 +ready listener = do IO.require + address <- IO.localAddress listener + writer <- IO.stdout + IO.require (IO.writeAll writer (B.append (C.showNumber (port address)) [10])) IO.pure +quiet _ = H.pure (B.ok t t) +bad _ = H.fail "test" "logger" "write" "private logger detail" +server root count window = IO.io (Server.serveCount root 1048576 quiet + (IO.ipv4 [127 0 0 1] 0) window count ready) +badLogger root count window = IO.io (Server.serveCount root 1048576 bad + (IO.ipv4 [127 0 0 1] 0) window count ready) +listen root address = IO.io (Server.serve root 1048576 Registry.stdoutLogV2 address 16 ready) +main = listen "./store" (IO.ipv4 [0 0 0 0] 8080) +cancelledServer root count window = IO.io (IO.scope (_ : IO.require + (IO.spawn 1 1 [] (_ : Server.serve root 1048576 quiet (IO.ipv4 [127 0 0 1] 0) window ready)) + (spawned : case spawned of + [task ticket] = do IO.require + input <- IO.stdin + IO.readSome input 1 + IO.cancel task "test shutdown" + outcome <- IO.join ticket + case outcome of + ["cancelled" "test shutdown" []] = IO.pure t + _ = IO.fail "Unexpected server cancellation outcome" + _ = IO.fail "Invalid controlled server spawn"))) + +limited root count window = IO.io (Server.serveCount root 4 quiet + (IO.ipv4 [127 0 0 1] 0) window count ready) diff --git a/demos/io-v2/streams.tri b/demos/io-v2/streams.tri @@ -0,0 +1,31 @@ +!import base as B +!import io_v2 as IO +!import handlers as H +!export main +!export handled +!export child +!export diagnostic +!export closeDenied +!export transferDenied +!export broken + +-- Run stored exports with --io --no-result: stdout belongs to the pipeline. +stream = do IO.require + input <- IO.stdin + output <- IO.stdout + IO.require (IO.copyStream input output) (_ : IO.pure t) +main = IO.io stream +handled = IO.io <| H.runIOV2 (H.liftIO stream) +-- Root admission closes on return, but the attached child may first borrow +-- inherited streams afterwards. Parent completion waits for the entire copy. +child = IO.io <| IO.require (IO.spawn 0 0 [] (_ : stream)) (_ : IO.pure t) +diagnostic = IO.io <| IO.require IO.stderr + (output : IO.require (IO.writeAll output [0 255 13 10]) (_ : IO.pure t)) +closeDenied = IO.io <| IO.require IO.stdout IO.close +transferDenied = IO.io <| IO.require IO.stdout + (output : IO.spawn 0 0 [output] (_ : IO.fail "must not start")) +broken = IO.io <| IO.require IO.stdout (output : IO.bind (IO.writeAll output [97]) + (reply : case reply of + (t, (_, t)) = IO.require IO.stderr + (diagnostic : IO.require (IO.writeAll diagnostic [102 97 105 108 101 100]) (_ : IO.pure t)) + _ = IO.fail "Expected broken pipe")) diff --git a/demos/io-v2/tcp.tri b/demos/io-v2/tcp.tri @@ -0,0 +1,74 @@ +!import io_v2 as IO +!import base as B +!import native_math as N +!import conversions as C +!import handlers as H +!export server +!export handledServer +!export supervisedHandledServer +!export client +!export namedClient +!export resolve +!export candidates +!export connectResult +!export invalidAddress + +port ["ipv4" _ value] = value +port ["ipv6" _ _ value] = value +port _ = 0 +announce listener = do IO.require + address <- IO.localAddress listener + output <- IO.stdout + IO.writeAll output (B.append (C.showNumber (port address)) [10]) +echo socket = IO.require (IO.copyStream socket socket) (_ : + IO.require (IO.shutdownWrite socket) (_ : IO.pure t)) +worker [socket] = echo socket +worker _ = IO.fail "Invalid connection handoff" +serve 0 _ _ = IO.pure t +serve count owner listener = IO.require (IO.acceptTCP listener owner) (socket : + IO.require (IO.spawn 0 0 [socket] worker) (_ : serve (N.predecessor count) owner listener)) +server address count = IO.io <| IO.scope (owner : do IO.require + listener <- IO.listenTCP owner address 128 + announce listener + IO.bind (serve count owner listener) (_ : IO.pure t)) + +-- Child routing is inherited through H.spawnV2, not raw IO.spawn. The banner +-- binding lives in source and is deliberately not a native networking service. +requireH action next = H.bind (H.liftIO (IO.require action IO.pure)) next +banner _ _ = H.pure [66 0 255 10] +handledWorker [socket] = H.bind (H.request "banner" "bytes" t) (bytes : + requireH (IO.writeAll socket bytes) (_ : H.liftIO (echo socket))) +handledWorker _ = H.liftIO (IO.fail "Invalid connection handoff") +handledServe _ 0 _ _ = H.pure t +handledServe supervised count owner listener = requireH (IO.acceptTCP listener owner) (socket : + H.bind (H.spawnV2 supervised 0 [socket] handledWorker) (reply : case reply of + ((t t), (_, t)) = handledServe supervised (N.predecessor count) owner listener + _ = H.liftIO (IO.fail "Failed connection spawn"))) +handledServer address count = handledServerWith 0 address count +-- Explicit containment probe: discarded child failures do not stop admission. +-- A production service should choose its own logging/outcome-observation policy. +supervisedHandledServer address count = handledServerWith 1 address count +handledServerWith supervised address count = IO.io <| H.runIOV2 + (H.handle "banner" banner (H.scopeV2 (owner : + requireH (IO.listenTCP owner address 128) (listener : + requireH (announce listener) (_ : handledServe supervised count owner listener))))) + +-- Read and write proceed concurrently to avoid duplex backpressure deadlock. +-- The source/destination files must differ, including aliases; output truncates. +client address source destination = IO.io <| IO.scope (owner : + IO.require (IO.connectTCP owner address) (socket : clientStreams owner socket source destination)) +namedClient host family port source destination = IO.io <| IO.scope (owner : + IO.require (IO.resolveTCP host family port 64) (resolved : case resolved of + [addresses _] = IO.require (IO.connectFirstTCP owner addresses) + (socket : clientStreams owner socket source destination) + _ = IO.fail "Invalid resolver reply")) +resolve host family port limit = IO.io (IO.resolveTCP host family port limit) +candidates addresses = IO.io <| IO.scope (owner : IO.connectFirstTCP owner addresses) +clientStreams owner socket source destination = do IO.require + input <- IO.openReader owner source + output <- IO.openWriter owner destination + IO.spawn 0 0 [] (_ : IO.require (IO.copyStream input socket) (_ : + IO.require (IO.shutdownWrite socket) (_ : IO.pure t))) + IO.require (IO.copyStream socket output) (_ : IO.pure t) +connectResult address = IO.io <| IO.scope (owner : IO.connectTCP owner address) +invalidAddress = connectResult ["ipv4" [127 0 0 256] 80] diff --git a/demos/io-v2/text.tri b/demos/io-v2/text.tri @@ -0,0 +1,24 @@ +!import io_v2 as IO +!import io_v2_text as Text +!export encode +!export decode +!export source +!export write +!export roundTrip + +encode limit text = IO.io (Text.encodeText limit text) +decode final bytes = IO.io (IO.decodeUTF8 final bytes) +source path limit = IO.io <| IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + Text.readSource reader limit) +write path text = IO.io <| IO.scope (owner : do IO.onOk + writer <- IO.openWriter owner path + Text.writeText writer text) +roundTrip input output limit = IO.io <| IO.scope (owner : do IO.onOk + reader <- IO.openReader owner input + received <- Text.readSource reader limit + case received of + (_, chars) = do IO.onOk + writer <- IO.openWriter owner output + Text.writeText writer chars + _ = IO.fail "Invalid source result") diff --git a/demos/package-inspector-v2/main.tri b/demos/package-inspector-v2/main.tri @@ -0,0 +1,18 @@ +!import foundation as B +!import fileio as IO +!import inspection as Inspect +!export main +-- Candidate offline bindings, not imports from the source workspace. Native v2 +-- has OS process authority; --allow-read/--unsafe-io are not accepted. +-- Read once within an explicit byte budget: hash and report use that same value. +main path = IO.io (IO.bind (IO.readBytes path 1048576) (reply : B.matchResult + (problem _ : IO.abortIO ["Read failed" problem]) + (bytes _ : B.matchResult + (code _ : IO.abortIO ["Invalid Arboricx bundle" (Inspect.describeError code) code]) + (summary _ : IO.bind (IO.sha256Hex bytes 1048576) (digestReply : B.matchResult + (problem _ : IO.abortIO ["Hash failed" problem]) + (digest _ : IO.putStr (B.append "arboricx.inspect.v1\nbundle-sha256\t" + (B.append digest (B.append "\n" (Inspect.renderSummary summary))))) + digestReply)) + (Inspect.inspectBundle bytes)) + reply)) diff --git a/demos/package-inspector-v2/tricu.project b/demos/package-inspector-v2/tricu.project @@ -0,0 +1,4 @@ +arboricx.project.v1 +foundation home tricu-base 0.2.0-io-v2 +fileio home tricu-file-io 0.2.0-io-v2 +inspection home arboricx-inspection 0.2.0-io-v2 diff --git a/demos/package-inspector/main.tri b/demos/package-inspector/main.tri @@ -2,22 +2,17 @@ !import fileio as IO !import inspection as Inspect !export main - --- These three modules are generated offline package bindings, not library source. --- main accepts a path through `arboricx run --arg PATH --io --allow-read PATH`. --- Select --no-result so the final IO value adds no bytes to the report. --- Read once: the digest and report always describe the same byte snapshot. -main path = (IO.io (IO.bind (IO.readFile path) - (reply : B.matchResult - (message _ : IO.abortIO (B.append "Read failed: " message)) - (bytes _ : B.matchResult - (code _ : IO.abortIO (B.append "Invalid Arboricx bundle: " (B.append (Inspect.describeError code) - (B.append " (code " (B.append (B.showNumber code) ")"))))) - (summary _ : IO.bind (IO.sha256Hex bytes) - (digestReply : B.matchResult - (message _ : IO.abortIO (B.append "Hash failed: " message)) - (digest _ : IO.putStr (B.append "arboricx.inspect.v1\nbundle-sha256\t" - (B.append digest (B.append "\n" (Inspect.renderSummary summary))))) - digestReply)) - (Inspect.inspectBundle bytes)) - reply))) +-- foundation, fileio and inspection are offline package bindings. +-- Run with --io --no-result; native file access uses OS process authority. +-- Read once within an explicit byte budget: hash and report use that same value. +main path = IO.io (IO.bind (IO.readBytes path 1048576) (reply : B.matchResult + (problem _ : IO.abortIO ["Read failed" problem]) + (bytes _ : B.matchResult + (code _ : IO.abortIO ["Invalid Arboricx bundle" (Inspect.describeError code) code]) + (summary _ : IO.bind (IO.sha256Hex bytes 1048576) (digestReply : B.matchResult + (problem _ : IO.abortIO ["Hash failed" problem]) + (digest _ : IO.putStr (B.append "arboricx.inspect.v1\nbundle-sha256\t" + (B.append digest (B.append "\n" (Inspect.renderSummary summary))))) + digestReply)) + (Inspect.inspectBundle bytes)) + reply)) diff --git a/demos/package-inspector/tricu.project b/demos/package-inspector/tricu.project @@ -1,4 +1,4 @@ arboricx.project.v1 -foundation home tricu-base 0.1.0 -fileio home tricu-file-io 0.1.0 -inspection home arboricx-inspection 0.1.0 +foundation home tricu-base 0.5.0-io-v2 +fileio home tricu-file-io 0.5.0-io-v2 +inspection home arboricx-inspection 0.5.0-io-v2 diff --git a/demos/runArboricxBundle.tri b/demos/runArboricxBundle.tri @@ -1,26 +1,20 @@ -!import prelude * -!import io * -!import arboricx * -!export * - --- Read an Arboricx bundle from disk and execute it. --- This demo loads test/fixtures/id.arboricx and applies the --- default export to the string "hi". The id bundle simply --- returns its argument, so the expected output is: --- hi --- --- Run with --allow-read test/fixtures/id.arboricx or --unsafe-io. - -runBundle = (path arg : - bind (readFile path) - (result : - matchResult - (err rest : putStrLn "ERROR: Could not read bundle file") - (bundleBytes rest : - matchResult - (err rest : putStrLn "ERROR: Could not execute bundle") - (value rest : putStrLn value) - (runArboricx bundleBytes arg)) - result)) - -main = io (runBundle "test/fixtures/id.arboricx" "hi") +!import base as B +!import io_v2 as IO +!import io_v2_storage as Storage +!import io_v2_text as Text +!import arboricx as Bundle +!export main +!export run +!export runBundle +-- Whole-bundle source decoding is explicitly bounded, not a streaming decoder. +-- Selecting/executing this program is authority to execute the selected bundle; +-- bundle validation alone is not authentication or a computation-resource quota. +-- Container remainders are internal directory cursors, not unconsumed input; +-- bytes outside declared sections are permitted by the portable reader profile. +runBundle path argument = IO.bind (Storage.readBytes path 1048576) (read : case read of + ((t t), (bytes, t)) = case Bundle.runArboricx bytes argument of + ((t t), (value, _)) = IO.require IO.stdout (writer : IO.require (Text.writeText writer (B.append value "\n")) IO.pure) + _ = IO.fail "Could not validate/execute bundle" + _ = IO.fail "Could not read bundle file") +main = IO.io (runBundle "test/fixtures/id.arboricx" "hi") +run path argument = IO.io (runBundle path argument) diff --git a/docs/build-and-run.md b/docs/build-and-run.md @@ -20,7 +20,7 @@ compiled tricu program, included at `bootstrap/build-stable.arboricx`. The executable automatically selects that packaged stable compiler. It locates its installed data independently of the caller's working directory, authenticates the bundle, and imports it indexed into the selected store. Missing or corrupt -package data is an error, never a switch to the Haskell recovery syntax. +package data terminates compilation with an error. The compiler's bytes and source provenance are recorded in `bootstrap/stable-compiler.json`. `python3 test/shipped_compiler.py` verifies the @@ -82,8 +82,11 @@ to indexed storage, preserving shared branches. Importing native library dependencies is a different operation; see [modules](modules.md.html) and the [artifact checks](tri-tools.md.html#host-artifact-checks) before choosing flat storage. -For programs with effects, select `--io` and grant the application its own file -permissions. Use trusted store directories. See [execution options](tri-tools.md.html#io-and-permissions). +For IO programs, select `--io`. Current runtimes execute IO v2 with OS process +authority and reject v1 permission-grant arguments. Use trusted store directories; +path confinement requires external isolation. See the +[structured IO contract](../test/io-v2/README.md) and +[execution options](tri-tools.md.html#io-and-permissions). `WORK` is temporary and contains all objects created by this example. Keep it if you want to reuse them, or remove it when finished. @@ -102,8 +105,8 @@ exported programs. The `arboricxServer` flake output builds the image `arboricx-server:latest`. It compiles `tricu-apps/arboricxServer.tri` directly using the repository `tricu.workspace` source locations and packaged stable compiler at build time. Startup runs the compiled -manifest from a read-only Nix store; it does not compile source, search -`tricu.workspace`, or write a compiler cache into registry storage. +manifest directly from a read-only Nix store. Compilation and workspace +resolution finish during the image build; registry storage holds runtime data. ```sh nix build .#arboricxServerTest --no-link @@ -124,6 +127,10 @@ The process listens on `0.0.0.0:8080` inside the container. The example publishe only host loopback; put a protected reverse proxy in front of it for remote access. The bind mount must be writable by the selected UID/GID. Preserve and back up `registry-store` across replacements; do not mount over `/nix/store`. +The default runner uses IO v2 with OS process authority. Each connection owns its +socket and upload resources; admission waits for the oldest child at a window of +16 connections. A stalled oldest connection can delay later requests, including +health checks. Uploads process bounded chunks; downloads retain whole values. Storage initialization and socket bind/listen failures exit nonzero. Monitor the HTTP health endpoint externally; the minimal image has no curl-based healthcheck. @@ -135,12 +142,12 @@ use [administrative publication](modules.md.html#exact-version-packages-from-tru to populate them in the mounted root. Uploads store opaque bytes: hashing is not bundle validation or authorization. -**Deployment limits:** this is a research registry, not a hardened public upload -service. It has no built-in authentication, TLS, quotas, or request deadlines. -Protect uploads at the proxy and apply connection/time/rate and storage limits. +**Deployment requirements:** this research registry requires an external layer +for authentication, TLS, quotas and request deadlines. Protect uploads at the +proxy and apply connection/time/rate and storage limits. The current HTTP parser caps headers at 64 KiB and bodies at 1 MiB. The runner -uses one Haskell capability and a 2 GiB managed-heap limit, not a total-process -memory guarantee; set container resource limits appropriate to the workload. +uses one Haskell capability and a 2 GiB managed-heap limit. Set container resource +limits for total process memory appropriate to the workload. Retain the previous image and a storage backup for rollback. For native operation, build `.#arboricxServerRunner` and run @@ -148,7 +155,12 @@ For native operation, build `.#arboricxServerRunner` and run same fixed port. `.#arboricxServerBundle` exposes `server.arboricx`, `manifest`, and the indexed `runtime` store. The opt-in `arboricxServerTest` exercises the actual runner: startup failures, health, binary uploads/downloads, invalid paths, -package lookup, and persistence across restart. It is not a container-engine test. +package lookup, and persistence across restart. It also checks failed logging after +publication, fragmented uploads, early EOF and cleanup, FIFO admission, and repeated +64-KiB/1-MiB upload/download workloads. Reports under `retention/` record allocation, +GC-sampled live heap and RSS; upload guards remain128MiB RSS and16MiB live heap. +Large downloads retain substantial whole-value cost. The checks run the native +entrypoint directly. Container-engine integration requires a separate check. ## Explicit compiler selection @@ -164,7 +176,10 @@ result/bin/tricu eval "$WORK/hello.tri" --store "$WORK/application" \ Use the manifest hash printed on the last import line, not a mutable alias or individual export hash. `--tool-store` requires `--toolchain` and otherwise defaults -to the application store. An explicit selection never falls back to package data. +to the application store. An explicit selection never falls back to package data. The packaged compiler and execution default use IO v2 with OS process authority. +A retained v1 compiler must run with its matching retained v1 runtime; +the current host rejects `--toolchain-io-version 1`. +No failure-based protocol fallback is performed. For retained source/compiler/dependency inputs and an independent offline replay, see [provenance capture, verify and rebuild](modules.md.html#retained-build-inputs-and-reproduction). @@ -186,3 +201,19 @@ multiline blocks, imports, loading files, and session operations; see between reference tests, candidate checks, and compiler acceptance. - [Compiler rebuilding](compiler-rebuilding.md.html): for changing or recovering the compiler. + +## Historical registry runners + +The v1 handler, chunked-upload and body-probe runner outputs are retired from +current Nix packages. Their source and executable evidence belong to the frozen +rollback toolchains; no current runner silently selects or emulates those hosts. +Use the default IO v2 runner and its acceptance gate above. The explicit +`arboricxStreamingV2Runner` and `arboricxStreamingV2Test` outputs exercise the same +supported composition and gates under their descriptive names. + +The current tests retain startup, restart, binary/path/package, malformed framing, +early-EOF cleanup, FIFO admission, and failed-logging-after-publication controls. +Paths resolve under OS process authority, including symlink traversal. An object +can remain published after logging fails. See +[body retention measurements](validation.md.html#body-ingestion-and-retention-probes) +for bounded upload checks and whole-value download limits. diff --git a/docs/compiler-rebuilding.md b/docs/compiler-rebuilding.md @@ -49,7 +49,9 @@ A full `source_map` generation produces: - `rebuilt-compiler.arboricx`: map-builder for the next generation. - `build-stable.arboricx`: stable application compiler, exporting `sourceBuildGraph`. - `build.arboricx`: retained optimized application compiler under the same ABI. -- `io-adapter.arboricx`: inherited standard adapter. +- `io-adapter.arboricx`: adapter actually used to execute this generation. +- `produced-io-adapter.arboricx`: selected adapter for executing the produced cohort + (new generation records); it can differ at an IO-version transition. Use `--backend stable` explicitly for new generations. The runner defaults to stable; historical records without a backend mean optimized. Generation @@ -73,13 +75,76 @@ checks source consistency, independently audits the store, enforces publication and whole-job deadlines, and records completion status. Zig also applies an address-space ceiling, CPU ceiling, and an 8 MiB native stack. -Both execution routes grant broad file IO to trusted compiler jobs. Use the -isolated workflow below to restrict filesystem access. Small-closure validation +IO v1 routes grant broad file IO to trusted compiler jobs. Native Haskell and Zig +IO v2 use OS process authority without legacy grants. The Zig executor calls the +same v2 interpreter as its CLI through the explicit owned-ABI entrypoint. Use OS isolation to restrict filesystem access. Small-closure validation uses `test/cross_host_generation.py`, documented in the [Zig runtime guide](../ext/zig/README.md#small-compiler-probe). Ordinary pair acceptance and retention toolchain roles use Haskell generations; the cross-host verifier compares Haskell and Zig generations. +### Explicit IO framing and transitions + +New generation records bind `execution_io_version`, `produced_io_version` and +`produced_adapter_sha256`. The first describes the selected running worker; the +second describes the resulting compiler cohort. Callers declare both values +explicitly. Old records +without these fields describe the isolated historical v1 path. Partial metadata, +unknown versions and Boolean/string JSON substitutes for integers are rejected. + +Selection uses the kit's declared version or the previous generation's produced +version. The previous produced adapter is authenticated and captured before +execution; historical generations use their recorded input adapter. An explicit +transition worker can override execution framing with `--execution-io-version 1|2` +and a matching `--adapter`. Kit metadata cannot be overridden by a conflicting +flag. Never retry another interpreter after failure. + +`--produced-io-version 1|2` defaults to the execution version. Changing it requires +an explicit `--produced-adapter`. For example, a retained v1 kit can compile a v2 +source projection while selecting a previously prepared v2 adapter for its output: + +```sh +python3 test/bootstrap_generations.py "$FIRST" --bootstrap-kit "$V1_KIT" \ + --tricu "$TRICU" --source-root "$FROZEN_SOURCE" \ + --map "$FROZEN_SOURCE/bootstrap/compiler-source-map.io-v2.v1" \ + --produced-io-version 2 --produced-adapter "$V2_ADAPTER" --backend stable --fresh +``` + +Acceptance of the selected cohort requires full pair verification and retention. +Both current source maps select `compiler_io`, the compiler storage facade, +plus its v2 dependencies. Compiler source imports this facade explicitly; general +application `io` is independent. The packaged compiler uses v2. Input limits +and source-owned storage semantics are described in the +[compiler projection controls](../test/io-v2/README.md#source-storage-and-isolated-compiler-projection). + +Reproduction requires the previous produced version and exact produced adapter. +Pair verification binds generation one's produced version/adapter to generation +two's execution and reproduced output, instead of requiring the *input* adapters +of a transition pair to match. It still requires the first rebuilt compiler, +identical frozen inputs, complete manifest reproduction and all three identical +compiler bundles. New records also check invocation authority flags. V2 Haskell +execution has no `--unsafe-io` or read/write grants. The correctness smoke and +retention probes select the recorded produced version explicitly. + +An opt-in preparation/control builds a v2 map-builder and adapter using the retained +compiler, writes a versioned kit, stages the full v2 map and builds only +`native_math`. It compares the complete manifest with a v1 execution control and +rejects conflicting versions, missing transition adapters and unsupported hosts: + +```sh +python3 test/io_v2_generation.py "$TRICU" --directory /tmp/new-v2-generation-probe +``` + +This probe covers preparation and one module. Full pair acceptance requires source +freezing, complete reproduction, smokes and rolling retention. Accepted native v2 pairs support +[recovery archives](#recovery-archives); seed-only v2 archives remain rejected. The +cross-host generation experiment accepts explicitly selected v2 seeds and maps. +Zig native executes the v2 compiler/file profile, but bounded workflow smokes +are not full cross-host fixed-point acceptance. Never relabel a +v2 kit as v1 to bypass that boundary. Application `provenance` capture/rebuild uses a separate +[versioned descriptor and explicit trust gate](modules.md.html#retained-build-inputs-and-reproduction), +not these generation records. + ### Isolated cross-runtime fixed-point reproduction The Linux workflow builds two Haskell generations, then uses the second compiler @@ -96,7 +161,27 @@ nix develop -c python3 test/bootstrap_cross_host.py \ --tricu "$TRICU" --zig-library "$ZIG/lib/libarboricx.so" ``` -Select exact Nix runtime paths and a new output directory. The source commit +The default remains the historical v1 experiment. Select a v2 cohort explicitly: + +```sh +# An authenticated caller-selected v2 kit is mandatory; never relabel a v1 kit. +nix develop -c python3 test/bootstrap_cross_host.py \ + --source-commit COMMITTED_SOURCE --directory "$SCRATCH/cross-runtime-v2-new" \ + --io-version 2 --bootstrap-kit "$V2_KIT" \ + --tricu "$TRICU" --zig-library "$ZIG/lib/libarboricx.so" +``` + +Use the same selectors with `--probe` first. The seed manifest and both verified +payloads are copied into `seed-kit`; later jobs never reopen the external kit. +Version2 selects the committed `bootstrap/compiler-source-map.io-v2.v1` map. +Derived kits carry the generation's **produced** framing and authenticated +produced adapter, independently of its execution framing. Continuation requires +the same selected version, frozen seed, sources and runtimes. These selectors +enable an experiment; they do not constitute acceptance or change defaults. + +Select exact Nix runtime paths and a new output directory. Python itself must +resolve inside `/nix/store` (as in `nix develop`); a profile symlink outside the +mounted tree cannot be executed inside the sandbox. The source commit supplies the frozen map, sources, kit and inner generation runner. Each job uses a fresh store in a filesystem sandbox containing its source snapshot, selected compiler/adapter kit, Nix dependencies and writable work directory. Haskell @@ -157,8 +242,10 @@ python3 test/bootstrap_generations.py "$FIRST" --bootstrap-kit "$KIT" \ --capabilities 8 --memory-mib 65536 --wall-seconds 7200 ``` -The JSON manifest has schema `tricu.bootstrap-kit.v1`, backend `stable`, and an -`artifacts` object with exactly two roles: +The historical JSON manifest has schema `tricu.bootstrap-kit.v1` (IO v1). New +explicit kits use `tricu.bootstrap-kit.v2` with required integer `io_version` +(1 or 2). A v1 schema cannot declare v2 execution. Both have backend `stable` and +an `artifacts` object with exactly two roles: | Role | `file` | `export` | | --- | --- | --- | @@ -324,6 +411,36 @@ concurrent writers. Interrupted builds remain incomplete. Resumption helps diagnosis and recovery; the acceptance pair requires fresh stores. The map-builder checkpoints completed modules as whole units. +## Preparing a source-mapped compiler kit + +Use this route to prepare a changed worker/adapter without flattening retained +compiler support or mixing v1 constructors with a v2 IO facade: + +```sh +python3 test/prepare_compiler_kit.py "$TRICU" "$NEW_PREPARATION" \ + --compiler-bundle "$APPLICATION_COMPILER" --compiler-io-version 2 \ + --io-version 2 --map bootstrap/compiler-source-map.io-v2.v1 \ + --source-root "$SOURCE" --backend stable +python3 test/io_v2_generation.py "$TRICU" \ + --kit "$NEW_PREPARATION/kit" --directory "$NEW_SCOPED_CHECK" +``` + +The compiler's execution version and the produced kit's version are separate, +explicit selections. An older v1 application compiler uses +`--compiler-io-version 1`; this does not change the requested output version. +For v1 output, select its source map and `--io-version 1`. Version selection is +caller-declared, never inferred from successful execution or tree shape. + +Preparation captures the mapped source bytes and compiler bundle into a new +workspace, retains namespaces and normal native module resolution, and compiles +both the worker and its six-argument adapter. It records source/map/compiler/runtime +identities and ordinary whole-process metrics. Limits default to 8 GiB and 1800 +seconds; select explicit larger limits within the 64-GiB/two-hour guard if needed. +There is no interpreter fallback. A successfully prepared kit is **not** a compiler +generation or acceptance result. The scoped check exercises real generation +boundaries and complete `native_math` output against a v1 control; full frozen-input +pair acceptance, application smokes and retention remain separate requirements. + ## Transitioning older inputs An older closure may lack the selected entrypoint, current native headers, or @@ -339,6 +456,11 @@ python3 test/bootstrap_generations.py "$PREVIOUS" "$NEXT" --tricu "$TRICU" \ --capabilities 8 --memory-mib 65536 --wall-seconds 7200 ``` +This legacy flat bridge requires v1 retained support and produces v1 actions; it +rejects a v2 support generation before writing output. Its application compiler may +be explicitly selected with `--compiler-io-version 2`, but that does not convert +the bridge output to v2. Use the source-mapped kit route above for v2 output. + Worker preparation selects only the requested backend and its reachable local definitions. It audits every transported support export. Inspect `--plan-only` before preparing a retained flat compiler; its source compiler export can exceed @@ -439,7 +561,28 @@ selected standard adapters remain inside the protected generations. The support preparation must contain the complete selected candidate cohort in one directory. The command reproduces current support bundles, metadata, receipts and full native manifests through the existing publisher, then checks -source freshness. Published fixtures remain unchanged. `relocate_patterns` +source freshness. By default this means the working checkout. An optional +`support.source` registry path selects a protected frozen reference source tree +instead. It must match **all current fixture source pins and compiler-map +membership**, and the publisher still reproduces all current support artifacts, +receipts and full manifests exactly. It is not permission to retain stale support +outputs. Reports distinguish frozen-reference matching from working-tree freshness +and list unpublished working-source differences; those differences remain publication +obligations, not covered behavior. + +A reference-only tree can be materialized without rewriting fixtures: + +```sh +python3 test/fixture_sources.py materialize --source-root "$SCRATCH/support/source" +``` + +This authenticates recorded snapshots before writing a new directory and reconstructs +compiler-map membership/dependencies from those snapshots. It is not a historical +Git checkout or a compiler-generation acceptance claim. Include that directory in +`protected` and set `support.source` to its scratch-relative path. Source drift, +symlink escapes and a mismatched selection block the cleanup gate. + +Published fixtures remain unchanged. `relocate_patterns` selects the current V3 publication procedure. Protect any other preparation or store used by active work before planning. The operator must check external users and stop uncoordinated jobs before setting the two confirmation fields. @@ -478,7 +621,8 @@ command. Use trusted local filesystems and keep adversarial writers or mounts outside the workflow. Before deletion, `bwrap` hides the entire scratch root, exposing only registered -protected directories and the writable checkpoint evidence directory. Each +protected directories and the writable checkpoint evidence directory. The sandbox sets its temporary-directory variables to its fresh `/tmp`, rather +than inheriting paths hidden by that mount. Each selected compiler passes independent pair audits, CLI compilation/execution, diagnostics and integrity controls, correctness smokes, and a fresh scoped `native_math` map-builder/adapter job with complete-manifest comparison. Support @@ -496,6 +640,13 @@ tooling evolves, but an in-flight delete must use its reviewed harness. Report an incomplete checkpoint with its blockers and outstanding bytes. Perform rotations at each checkpoint, while sufficient disk space remains. +If an enclosing PID namespace exposes a mismatched procfs, namespace setup can +fail before a validation process starts. Do not count that launch as a check or +weaken isolation. Run the same reviewed apply command inside a matching PID/proc +namespace, using `unshare --user --map-root-user --mount --pid --fork +--kill-child --mount-proc -- python3 ...`, and recheck completion. Preserve failed +launch evidence; only the completed resumed postchecks satisfy retention. + Safety controls run in the fast Nix check without compiling tricu programs: ```sh @@ -515,8 +666,19 @@ python3 test/bootstrap_archive.py \ For a verified pair, also supply `--first-generation "$FIRST"` and `--acceptance "$PAIR_ACCEPTANCE"`, with `--generation` selecting generation two. -Current fixture fingerprints must match current mapped source. The selected seed's -source must match its recorded inputs exactly. +The selected seed's source must match its recorded inputs exactly. Fixture bundles +and their reference source snapshots are authenticated separately; accepted-pair +transfers record working-source differences rather than claiming those edits have +published fixture coverage. Seed-only transfers still require current compiler +fixture sources because they construct tools from that fixture. + +V2 recovery requires an independently accepted pair. The selected adapter must be +the second generation's authenticated **produced** adapter, not an earlier transition +input adapter. `tricu.bootstrap-recovery.v2` records compiler IO version 2; its +application probes explicitly select that version and run with OS process authority, +without v1 grants or interpreter fallback. The selected generation source map is +retained in `tools/source-map.v1`. Follow that map when rebuilding; do not substitute +the current checkout's map. V1 transfers retain their historical runtime default. The transfer includes full generation stores and provenance, matching source, current tracked working files, reachable Git history/refs, a tracked-change patch, @@ -551,17 +713,17 @@ transfers. ```sh python3 test/bootstrap_generation_cli.py "$PREVIOUS" --backend stable --adapter "$ADAPTER" -python3 test/verified_tree_loading.py "$TRICU" "$PREVIOUS" \ - "$DIAGNOSTIC_GENERATION" --compiler-bundle "$APPLICATION_COMPILER" \ - --directory "$PROBE_DIRECTORY" --publish --imports \ - --memory-mib 32768 --seconds 600 +python3 test/io_v2_indexed.py "$TRICU" --directory "$PROBE_DIRECTORY/read" +python3 test/io_v2_indexed_write.py "$TRICU" --directory "$PROBE_DIRECTORY/write" +python3 test/io_v2_compiler_store.py "$TRICU" --directory "$PROBE_DIRECTORY/compiler-store" ``` Use new writable diagnostic directories. The CLI probe checks cold/warm/change, interruption, corruption, backend selection, and receipt options. An older -closure can use an explicit `--worker-bundle`. The transport probe compiles an -adapter through the selected portable toolchain and checks unit exports and -loading. Its checkpoints support diagnosis; ordinary generation completion +closure can use an explicit `--worker-bundle`. The v2 transport probes use independent canonical-byte, corruption, bounded +transfer and compiler-store checks. The older v1 source-splicing probes are +retired and reject before writes; their historical results remain historical. +These focused checks support diagnosis; ordinary generation completion requires the generation runner and acceptance workflow above. See [Storage](storage.md.html#verified-native-tree-transport) for the transport contract. diff --git a/docs/compiler.md b/docs/compiler.md @@ -40,8 +40,8 @@ repetition. Binary and HTTP consumers share those primitives with the compiler. Stable module compilation shares a compiler-private persistent index between name analysis and external-name resolution. `lib/source/index.tri` indexes the -Leaf/Stem/Fork preorder of finite tree keys; it does not hash identifiers or -inspect stored executable values. Construction preserves first-binding precedence, +Leaf/Stem/Fork preorder of finite tree keys and retains stored executable values +opaquely. Construction preserves first-binding precedence, and insertion of a compiled local creates a new index version. Index callers supply proper binding lists and finite keys. @@ -91,12 +91,12 @@ delayed fixed point with lexical captures. The stable backend prunes a binder-independent application only when it is certified constructor-only: its function is a known Leaf or Stem and its argument is a represented value or another certified construction. Opaque -variables may be stored but are not known callable shapes. Fork applications, +variables may be stored as opaque values. Fork applications, unknown calls, and constructors containing unproved computations remain residual. There is no general independent-expression or eta shortcut. Constructor shape and the minimum free index are cached on private residual -nodes and updated during abstraction/shifting; they do not enter output trees -or diagnostic locations. Inspectable function representations can differ from +nodes and updated during abstraction/shifting; output trees and diagnostic +locations retain their public representations. Inspectable function representations can differ from older stable compilers even when application results agree. The retained optimized backend prunes binder-independent subtrees into constant @@ -164,12 +164,12 @@ protocol. `resolveGateAst` and `sourceLowerEquations` retain optimized-backend lowering, including its branch-construction safeguards. Factoring can change compiled function representations; intensional tree -observations remain compiler-dependent. It does not change evaluator rules, -data encodings, or the equality implementation. The +observations remain compiler-dependent. Evaluator rules, data encodings, and +the equality implementation remain unchanged. The [comparison probe](validation.md.html#pattern-lowering-comparison-opt-in) checks results, diagnostics, demand, code-generation targets, and costs using explicit -compiler identities. Source-level checks do not substitute for compiler artifact -acceptance. +compiler identities. Compiler artifact acceptance requires the separate +reproduction gates. ## Located representations @@ -235,7 +235,7 @@ Whole-interface and selected-export loads authenticate the manifest once. The streaming flat-bundle packer remains separate: it must not reconstruct expanded trees just to reuse the executable loader. -`PortableCompiler` supervises tool selection, loading, deadlines, restricted IO +`PortableCompiler` supervises tool selection, loading, deadlines, IO framing and publication verification for file compilation and interactive sessions. It owns no source semantics. Its failures are IO errors; CLI and REPL boundaries choose how to report them rather than library code terminating the process. @@ -247,7 +247,7 @@ kernel rules without an explicit application/thunk node. Pending reductions live in Haskell thunks. Constructor application returns an outer Stem/Fork without forcing its children; constant application ignores its discarded argument; triage inspects the discriminator only as needed. This is demand-driven, -call-by-need execution, not a separate full-normalization pass on every call. +call-by-need execution. Indexed loading reconstructs shared inert subtrees. Distribution reuses its argument, but sharing a function tree does not establish a global application @@ -262,8 +262,8 @@ physical sharing but cannot serialize unevaluated Haskell computations or infinite trees. Stored `--profile` follows normal rendering demand; the explicit `--profile-full-result` option additionally forces the complete result using a traversal-local physical-node memo (`TreeObservation.forceTreeShared`). Nodes are -marked complete only after their children, so a cycle is not accepted as a finite -normal form. Infinite inputs still require external resource limits. +marked complete only after their children, preserving the requirement for a +finite normal form. Infinite inputs still require external resource limits. The `Evaluation demand boundaries` tests in `test/Spec.hs` use poisoned host subtrees for positive/negative demand controls, check distribution sharing, and @@ -335,7 +335,7 @@ input calls `done`; a Fork supplies its head and state to `step`. `Recur` advanc on the original tail. Step indices are state 0, head 1, then outer binders; inserted self/tail bindings use binder-aware remapping. `done` can read state 0 but has no other free outer binders. `Recur` is meaningful only inside a step. -These are trusted-IR conventions, not validation of arbitrary user trees. +These conventions require trusted compiler-built IR. ### Authored conditional and list forms @@ -379,8 +379,8 @@ non-cons tails. Formatter replacement fragments remain test inputs, separate from the production formatter. Checks must force complete compiled trees before application, then independently -publish and execute them. Finite application agreement does not establish equal -function representations. Timeouts bound observations, not termination proofs. +publish and execute them. Compare function representations independently of +application results. Record timeouts as bounded observations. ### Preserve non-strict arguments @@ -406,8 +406,7 @@ scalar source. Native overlays use the captured original octets directly; atomic writes. Legacy header rewrites retain their existing line handling. General `readFile` remains binary. Malformed UTF-8 and an initial BOM are rejected. No Unicode normalization occurs; -positions count scalars, not bytes or display cells. Existing explicit-scalar -lexer fixtures check character handling, not file decoding; the opt-in +positions count scalars. Explicit-scalar lexer fixtures check character handling; +the opt-in `test/native_source_utf8.py` checks the file boundary with current consumers. -The included compiler has not yet been republished with this candidate boundary. See [source characters](language.md.html#source-characters-and-tokens). diff --git a/docs/contracts.md b/docs/contracts.md @@ -27,8 +27,8 @@ contractErr = (msg : (rest : err msg rest)) Contract callers normally supply `t` as `rest`. Success contains the checked value, which may have been transformed; failure contains an arbitrary diagnostic -tree, often a string. This is a programming convention, not a host-enforced type: -a user-written contract that returns another shape violates the protocol. +tree, often a string. Library callers enforce this protocol by inspecting the returned Result. +A user-written contract that returns another shape violates the protocol. ## 2. Checking and handling Results @@ -73,8 +73,7 @@ fiveOrZero = (matchResult (withContract nat? 5)) ``` -`checkedSum [1 2 3]` returns `ok 6 t`; `fiveOrZero` is `5`. A failed check does -not inherently abort the host or raise an interaction-tree exception. +`checkedSum [1 2 3]` returns `ok 6 t`; `fiveOrZero` is `5`. The caller chooses how to handle a failed check. The convenience function `check contract value` extracts either the checked value or the diagnostic from `withContract`. It discards the success/failure @@ -88,8 +87,8 @@ tag, so use the Result-returning APIs when that distinction matters. positive? = guardC "expected positive integer" (n : gte? n 1) ``` -Do not pass a Boolean predicate directly to `checkContract` or `checkM`. For -example, `even?` returns a Boolean; `evenC?` from `intensional` returns a Result. +Adapt Boolean predicates with `guardC` before passing them to `checkContract` or +`checkM`. For example, `even?` returns a Boolean; `evenC?` from `intensional` returns a Result. The current library includes: @@ -105,21 +104,20 @@ The current library includes: Examples of contracts include `nat?`, `nonZero?`, `bool?`, and `anyC` in `base`, and `evenC?` and `sortedList?` in `intensional`. Natural-number contracts use -tricu's canonical compact-number encoding, not a host integer type: bits must be +tricu's canonical compact-number encoding: bits must be exactly Leaf or Stem Leaf, and redundant high zero bits are rejected rather than normalized. This agrees with zero tests and `native_math`'s input domain. `listOf` and `nonEmptyListOf` reject improper tails with `err "not a list" rest`; the latter rejects an empty list with `err "empty list" rest`. `pairOf` rejects non-Forks with `err "not a pair" rest`. Element failures short-circuit without -inspecting subsequent elements. Well-formed supplied contracts are still required; -these helpers cannot make an arbitrary contract obey the Result protocol. +inspecting subsequent elements. Composition requires each supplied contract to obey the Result protocol. List checking retains transformed values. In particular, `listOf (fnContract argC resC)` retains the checked proxies; callers still use each proxy's explicit Result/remainder calling protocol. Building a checked list incurs allocation even for `anyC`. -Remainders are not parser state threaded between checks: `andC` calls its second +`andC` calls its second contract with the original remainder, `orC` retries the original value/remainder, and `bindC` supplies its continuation the checked value, original value and original remainder. Those delegated final Results are returned as supplied; @@ -131,8 +129,7 @@ Structural contracts in `intensional` distinguish arbitrary trees from numbers: `nonEmptyTree?` accepts any non-Leaf, and `fullTree?` permits only Leaves/Forks. Parity, power-of-two, and ASCII checks first validate natural-number shape. `sortedList? c` checks/transforms elements with `c`, then checks natural-valued -ascending order on that resulting list; it does not treat comparison failures as -truthy success. These are finite-tree checks, not termination guarantees. +ascending order on that resulting list using exact successful comparisons. These checks require the demanded tree structure to terminate. ### Arithmetic and decimal boundaries @@ -148,12 +145,12 @@ produce `err "non-zero" t`; malformed dividends produce the natural-number error first. Successful `divMod a b` is the pair `t quotient remainder`, with `a = quotient*b + remainder` and `0 <= remainder < b`. Use it instead of doing the same division twice. Annotations return plain success values and in-band -failure Results, as elsewhere; not host exceptions. +failure Results, as elsewhere. -Raw `native_math` does not validate inputs. Its `divMod a 0` is explicitly the -degenerate pair `(0,a)` (quotient 0, remainder a), **not valid Euclidean division**; -the checked facade rejects that case before calling it. Do not use the raw -kernel's total zero convention as a boundary check. `decimal value digit` means +Raw `native_math` requires canonical-natural inputs from its caller. Its +`divMod a 0` convention returns `(0,a)` (quotient 0, remainder a). Euclidean +division requires a positive divisor, which the checked facade enforces before +calling the kernel. `decimal value digit` means `10*value + digit`, with a caller precondition of `0 <= digit <= 9`. `conversions.showNumber` validates its natural input and emits canonical ASCII @@ -173,8 +170,8 @@ identityResult = (bindResult (fnContract nat? nat? (x : x) t) (proxy _ : proxy 7 t)) ``` -`identityResult` is `ok 7 t`. Constructing the proxy does not prove anything -about unobserved calls or the internals of `f`. +`identityResult` is `ok 7 t`. The proxy checks each observed call at its argument +and result boundaries; the implementation of `f` remains opaque. `fn2` returns a curried proxy through the same Result protocol. Each stage must be unwrapped before applying the next argument: @@ -226,17 +223,15 @@ recoveredHalf = (runM (handleM "contract" `runM (halfM 8)` returns `ok 4 t`; `recoveredHalf` returns `ok 0 t`. `runM` interprets this pure `pureE`/`bindE`/`exceptE` representation and returns a -Result. An unhandled exception becomes `err value t`; its tag and continuation -are not retained in that Result. It is not a general IO interpreter and does -not return residual host effects. See +Result. An unhandled exception becomes `err value t`, retaining its diagnostic +value. This runner handles the pure effects protocol. See [`demos/contractEffects.tri`](../demos/contractEffects.tri) for `do bindM`, handler replacement and resumption examples. ## 6. Source annotations `.tri` annotations are frontend sugar for inserting calls to `withContract` and -handling their Results. Contract expressions are ordinary source expressions, -not names in a separate type language. +handling their Results. Contract expressions are ordinary source expressions. ```tri idNat x@nat? =@nat? x @@ -246,10 +241,9 @@ addChecked @nat? @nat? =@nat? add - `x@contract` checks an argument before entering the body. - `=@contract` checks the returned value. -- A successful annotated call returns the accepted **plain value**, not an - outer `ok`. Failure returns `err diagnostic t`. -- Unannotated binders and a missing result annotation add **no check**. They do - not insert an implicit `anyC` application. +- A successful annotated call returns the accepted **plain value**. Failure + returns `err diagnostic t`. +- Checks are inserted only for explicitly annotated arguments/results. - Phantom annotations supply contracts without term binders, wrapping a local raw implementation. Mixing phantom and named binders is unsupported. - Compound annotations containing application must be parenthesized. @@ -260,7 +254,7 @@ before the final binder produces an absorbing function for the next argument before returning the failure Result. Annotation lowering belongs to the portable source compiler in -`lib/source/program.tri`; there is no Haskell source desugarer. The accepted profile is described +`lib/source/program.tri`. The accepted profile is described in [the source profile](language.md.html). ## 7. Modules and content-addressed storage @@ -287,29 +281,222 @@ Result convention. Runtime checks complement tests and execution budgets. `lib/arboricx/server.tri` exports `bundleHashC`: exactly 64 lowercase ASCII hexadecimal characters, preserving the checked value and remainder. Its predicate rejects malformed list/numeral trees as well as malformed strings. -It examines at most 64 characters and one final list constructor; an oversized -suffix is not traversed. This validates a path component, not the integrity or -format of the stored bundle, and does not replace filesystem permissions. - -`getBundleByHash` applies `checkContract` before constructing its file-read -action. It explicitly translates contract failures into the HTTP convention: -`err 400 diagnostic`, rather than propagating a contract remainder as an HTTP -message. Read failures remain `err 404 message`; success is `ok bytes t`. -`serveBundleHash` handles those statuses, so both download routes share the -same guard. Direct callers of `getBundleByHash` also receive the guard. - -The host/unit tests record requested effects with a small interpreter: rejected -identifiers must request **zero reads**, not merely return an error after a -failed read. They also check byte membership, malformed trees, oversized suffix -non-evaluation, accepted-value/remainder preservation, missing objects, and -unchanged health/landing routes. This is a boundary convention, not a private -capability: code can still deliberately call `readFile` or path helpers itself. - -`putBundle` computes the digest itself, checks the same hash contract before -filesystem IO, then publishes with `atomicWriteBytes`. There is no public -unchecked hash-taking writer. It preserves the existing replace-on-upload policy; -the hash contract does not validate bundle contents or supply general symlink -containment. Root is trusted configuration, and host permissions remain necessary. - -See [registry pipeline validation](validation.md.html#registry-server-pipelines-opt-in) -for current artifact checks and their limits. +It examines at most 64 characters and one final list constructor, leaving an +oversized suffix opaque. This validates the spelling of a path component. + +`handler_artifacts.loadFrom` applies the contract before issuing a storage +request. Its filesystem adapter also checks raw requests before constructing +paths. Rejected digests return `err ["invalid-digest" message] t`; the HTTP +consumer maps this to status 400 and other load failures to a redacted 404. +Successful loads authenticate the bytes against the requested SHA-256 name. + +Publication validates canonical octets, computes the digest, stages the bytes, +and verifies/synchronizes immutable winners before acknowledging success. +Existing corrupt winners remain intact for diagnosis. Stored bytes are opaque; +applications supply executable-bundle validation when required. Roots are trusted +configuration and path access follows OS process authority. + +Independent handler and storage tests check zero requested reads for invalid +digests, canonical character/numeral shapes, oversized suffix laziness, +accepted-value/remainder preservation, byte authentication, corrupt winners, +publication races and ordinary failures. See +[handler validation](validation.md.html#scoped-handler-library-opt-in) and the +[IO storage controls](../test/io-v2/README.md). + +## 10. Scoped request handlers + +The native workspace modules `handlers`, `handler_log`, `handler_artifacts`, and +`handler_testing` provide request/response services. They implement a source-owned interaction-tree protocol, with explicit bridges +to host IO. Browser use requires publication of the selected source cohort. + +### Checked continuations + +`H.checkWith contract value rejected malformed next` applies `checkContract` +once and selects a continuation: + +| Contract reply | Continuation argument | +| --- | --- | +| `ok checked t` | `next checked` | +| `err diagnostic remainder` | `rejected diagnostic` | +| Any other shape | `malformed value`, using the original value | + +The selected continuation determines the returned computation or value. The +checked value can be a transformation or higher-order proxy. Rejection preserves +existing callers' treatment of the diagnostic independently of its remainder. +Unselected continuations and unused payloads remain lazy. + +`H.checkRequest service operation contract argument next` supplies the standard +request policy: rejection produces `invalid-request`, and a malformed contract +reply produces `invalid-request-contract`. Both carry the service and operation; +interpretation supplies the surrounding diagnostic labels. Logging's sink adapter +uses this helper to pass the checked event directly to its sink. + +`H.checkedRequest service operation inputC outputC argument` checks the argument, +issues the request with the checked value, then checks the delivered reply in +its continuation. Reply failures use `invalid-response` and +`invalid-response-contract`. Successful service Results remain ordinary values +inside the handler computation. + +Artifact loading uses `checkWith` to select its domain-specific policy: rejected +digests produce an ordinary `invalid-digest` Result, while malformed contract +replies produce a handler failure. Artifact publishing retains its own diagnostic +categories. These helpers share checking and continuation selection while keeping +those policies explicit at each boundary. + +### Application code and composition + +```tricu +!import handlers as H +!import handler_log as Log +!export main + +application = do H.bind + Log.info "starting" + Log.info "finished" + H.pure 42 + +main = Log.withContext [["request" 7]] application + |> Log.capture + |> H.runPure +``` + +The result is `ok [42 events] t`, with two events. A log event is +`[level contextFields payload]`: fields and payload are trees, with formatting chosen by the sink. `withContext` prepends outer context fields, preserving order +and duplicate keys. It checks that its fields form a proper list. `Log.info` and +`Log.warn` are conveniences over `Log.emit level payload`. + +`Log.withSink sink computation` installs a function receiving the event and +returning a handler computation whose value is an ordinary Result: `ok t t` or +`err diagnostic t`. `Log.discard` installs a successful no-op sink. Log operations +return those Results for applications to inspect or deliberately ignore. + +`Log.capture computation` is a composable boundary transformer returning +`[value events]` on normal completion, including when `value` is an ordinary +failure Result. It forwards other operations to outer handlers. A terminal +interpreter failure propagates, discarding the accumulated events. Persist +events through a sink when they must survive a failed computation. Capture belongs outside context decoration +if it should observe the decorated events. An inner sink consumes requests before +an outer capture can see them, and an inner capture similarly hides events from +an outer sink or capture. + +See [the runnable logging example](../demos/handlers/main.tri) and +[the artifact GET consumer](../demos/handlers/artifact-get.tri). + +### Requests, scopes, and forwarding + +`H.request service operation argument` constructs a request. Names are ordinary +trees; use readable service/operation strings for diagnostics. `H.handle service +handler computation` installs a handler whose callback receives `operation` and +`argument` and returns a computation yielding one reply. + +The nearest matching service handler owns the request. Its own computation runs +in the environment **outside its installation scope**, excluding itself and any +intervening inner handlers. The caller resumes in its original environment. +`H.forward service operation argument` is an explicit spelling of another request +from that outer environment. Outside a handler callback, `forward` uses the +current request environment. Forward unknown operations to outer handlers. + +This permits contextual overrides and stable service identity together. Wrapping +a callback's body in its original service handler pins that service while leaving +other services contextual. Merely returning a function or inert action from a +scope leaves it to use the environment at its eventual execution. Handler order +is significant: changing wrapper order can change routing. If service-handler work should inherit a logging context, +install that context **outside** the service handler. + +`H.label name computation` adds an execution label for diagnostics. Labels are +outer-to-inner and are restored on scope exit. Handler-generated work retains the +request's execution labels even though its service environment is the handler's +outer environment. Labels supply diagnostic context. + +### Results, contracts, and execution + +`H.checkedRequest service operation inputContract outputContract argument` checks +the argument before requesting and the reply before returning. Checks use +`checkContract` directly; transformed success values are retained. Successful contracts must return an empty remainder. Rejected or +malformed contracts terminate with a named protocol diagnostic. + +`H.runPure computation` returns an **execution Result**. `ok value t` says that +interpretation completed. A service can return `err message t` as its ordinary value; the outer result is then +`ok (err message t) t`. Unhandled requests, invalid protocol responses, and +`H.fail kind service operation detail` terminate with: + +```text +err [kind service operation detail labels] t +``` + +A missing handler uses kind `"unhandled-request"`. An invalid response uses +`"invalid-response"`. `fail` terminates the computation with its diagnostic. + +`H.liftIO hostAction` requests the named `"host.io"` / `"run"` bridge. `runPure` +rejects it like any other unhandled request. `H.runIO computation` is an alias +for `H.runIOV2`: it returns a host IO action yielding the successful computation +value. Handler failures become terminal v2 failures. Wrap the action with +`IO.io` at an executable entrypoint. Install handlers for other named services. +The host enforces resource ownership under OS process authority. + +`H.scopeV2 body` and `H.spawnV2 supervised keep handles body` preserve the lexical +handler environment and labels in their callbacks. Each callback starts with an +empty continuation stack. Their execution uses the host +[scope/task lifecycle](../test/io-v2/README.md); pure interpretation reports an +invalid view for these boundaries. + +### Artifact storage + +`Artifacts.load digest` and `Artifacts.publish bytes` address the default +`"artifacts"` service. `loadFrom service digest` and `publishTo service bytes` +select explicit roles, useful for source/destination stores. + +`Artifacts.withFilesystemV2 root limit computation` installs the default service; +`withFilesystemForV2 service root limit computation` installs a named one. The +byte limit applies to storage operations. Paths and publication use the artifact +store's hash/shard layout, staging and authenticated winner checks. +`Artifacts.prepareFilesystemV2 root` prepares and synchronizes the directory +layout at application startup and returns an ordinary Result. Run it explicitly +before serving requests; path access follows OS process authority. + +Loads return `ok bytes t` or `err diagnostic t`. Invalid digests return +`err ["invalid-digest" message] t` before requesting storage. The filesystem +adapter rechecks raw load requests before constructing paths. Publication checks +canonical octets and returns `ok digest t` or an ordinary failure Result. Replies +are checked, including byte-list contents and canonical digest spelling. These +checks traverse their data, with costs depending on the demanded contents. + +Domain Results use an empty remainder. HTTP mapping belongs to the consumer. +The example GET consumer maps +invalid digests to 400 and other load failures to a redacted 404, records a +completion event, and explicitly treats logger failure as an application error. +The default source registry composes these adapters. Publication stores opaque +bytes; callers validate executable-bundle formats when required. A later failure +can leave a published object visible. + +### Scripted services and low-level interpretation + +`Test.expect service operation argument reply` constructs one script row. +`Test.run script computation` interprets residual requests using exact matching +and returns `[executionResult chronologicalTrace unusedScript]`. The trace +includes attempted requests that exhaust or mismatch the script. Unused rows on +successful completion are an error. Early failures retain their trace and unused +rows. Reply contracts run after each scripted reply, preserving the service's +response checks. + +`H.view computation` exposes the normalized prefix for alternative interpreters: +`(100, value)`, `(103, completeDiagnostic)`, or +`(106, [event labels replyContinuation])`, where `event` is +`[service operation argument]`. The continuation returns another normalized view. +Scope and spawn views additionally use `(107, [labels body next])` and +`(108, [supervised keep handles labels body next])`. Bodies and continuations +remain opaque until explicitly applied. +`H.collect service operation argumentContract reply computation` is the lower- +level boundary transformer used by log capture. It validates and collects matching +arguments, replies with the supplied value, and relays other requests outward. +Install collection inside each scoped or child body; crossing those boundaries +produces a `state-boundary` diagnostic. + +The implementation uses linear handler lookup and a two-stage interpreter. +Service names and closures are inspectable routing values. Host authority and +resource lifetimes come from the IO runtime; applications choose retry and +persistence policies. Explicit value-based services remain useful where stable +identity is the primary requirement. + +Run the independent source and filesystem checks described in +[Testing](validation.md.html#scoped-handler-library-opt-in). diff --git a/docs/host-abi.md b/docs/host-abi.md @@ -28,7 +28,8 @@ pair tag payload The `tag` says how the host should interpret `payload`. -The payload is always the canonical/raw Tree Calculus representation for that type. The ABI envelope tags the payload; it does not replace or recursively wrap canonical Tree Calculus data. +The ABI envelope tags the outer payload, whose contents retain the canonical/raw +Tree Calculus representation for the selected type. ## Tags @@ -116,7 +117,7 @@ These predicates validate canonical tree encodings. Important ambiguity note: -Tree Calculus encodings are not globally disjoint. For example, `t` is also `false`, `0`, and `[]`. Typed runners intentionally interpret values according to the requested type. +Tree Calculus encodings overlap. For example, `t` is also `false`, `0`, and `[]`. Typed runners intentionally interpret values according to the requested type. ## Error behavior @@ -146,14 +147,13 @@ if `resultCharacters` is string-like, otherwise: err errHostCodecFailed rest ``` -The typed runner preserves the execution remainder on codec failure; -it does not replace it with the rejected payload. Invalid dispatch tags and +The typed runner preserves the execution remainder on codec failure. Invalid dispatch tags and improper argument-list spines are rejected before bundle parsing/application, with code 14 and an empty remainder. The raw tree constructor remains unchecked; typed execution checks the selected payload interpretation. -String payloads are **not UTF-8 byte streams**. Wire manifest text uses validated -UTF-8 octets, whereas the host string envelope carries Unicode scalar values. +The host string envelope carries Unicode scalar values. Wire manifest text uses +validated UTF-8 octets. ## Execution wrappers The base self-hosted Arboricx runners are defined in `lib/arboricx/arboricx.tri`: @@ -231,47 +231,37 @@ The reference tests cover these invariants: 7. A typed runner returns `errHostCodecFailed` when validation fails. 8. Named-export typed runners select the requested export before wrapping. -## Native source IO boundary - -These are IO action tags, separate from the result-envelope tags above: - -| Constructor | IO tag | Result payload | -| --- | --- | --- | -| `readSourceFile path` | 37 | Pair of exact file byte list and decoded Unicode scalar list. | -| `encodeSourceText characters` | 38 | UTF-8 byte list; noncanonical naturals, non-scalars and improper lists fail. | - -Both return ordinary IO Results with an empty remainder. Reading uses the same -read permission checks as `readFile` (tag 20), which remains an octet reader. -Source decoding is strict UTF-8 and rejects an initial BOM with a path-bearing -diagnostic. Embedded U+FEFF is an ordinary scalar, not stripped. Encoding does -not normalize text or translate newlines; it performs no filesystem IO. - -These actions support the current-source native compiler candidate. Retained -compiler bundles keep their recorded behavior until integrated publication. - -## Haskell filesystem permission boundary - -Read/write grants are canonical path prefixes with directory-component boundaries. -Checks resolve existing symlinked ancestors even when the final filename does not -yet exist. Empty grants do not authorize paths; resolution errors deny access. -Unrestricted read/write flags explicitly bypass the corresponding checks. - -This is not a race-free filesystem sandbox: checking a pathname and subsequently -opening it are separate operations. Use trusted directories and do not allow -untrusted concurrent mutation of directory entries or symlinks. - -## Haskell IO run ownership - -A host IO run owns its asynchronous OS workers and open socket registry. Root -completion and `abortIO` end the run; they do not leave detached workers running. -Normal return, failure and external cancellation cancel and join outstanding OS -workers before closing remaining registered sockets. Explicit socket close still -invalidates that handle immediately. Task handles and socket handles are local -to the run and cannot transfer resource ownership to another invocation. - -Cancellation does not execute pending tricu continuations or language-level -cleanup actions, and it cannot roll back completed effects or partial writes. -Worker cancellation waits for host cleanup; an uninterruptible OS operation can -delay termination beyond a requested timeout. This is not a hard process-kill -deadline. Socket operations report operational IO errors as Results, but host -asynchronous cancellation is not converted into an ordinary Result. +## Structured host IO + +The typed value envelopes above are independent of the IO execution protocol. +Current hosts execute only the canonical `tricuIO` envelope with version **2**. +Unsupported versions reject before action payloads are demanded. Historical +version-1 trees can be inspected as data; execution requires a retained runtime. + +`io` reexports `io_v2`. The protocol supplies sequencing, scope-owned resources, +child tasks, consumable joins, supervision and cancellation. Named/versioned +facilities provide binary streams, codecs and backend-specific OS operations. +Native execution uses OS process authority and explicit resource ownership. The [IO v2 contract](../test/io-v2/README.md) specifies +wire shapes, ordinary Results, terminal outcomes, cancellation and cleanup. + +Native Haskell supports filesystem, TCP, process/context and clock facilities. +Zig supports the compiler/file profile; browser hosts support core/task and +terminal operations. Query capabilities for the selected backend +before choosing a facility. + +## Source bytes and publication + +`compiler_io` is the source-owned compiler policy over IO v2. Whole-source reads +preserve exact raw octets and decode strict Unicode scalars, rejecting malformed +UTF-8 and an initial BOM without normalizing text or newlines. Encoding U+FEFF +is valid; source-file policy rejects only an initial BOM. Whole-source operations +retain their values and enforce a 64-MiB source/metadata budget. Heap usage +requires a separate bound. + +Indexed tree transport authenticates bytes and preserves sharing. Source-level +storage helpers own sharding, expected-digest checks, no-replace publication and +winner verification. File/directory sync follows host filesystem guarantees; +a post-publication failure can leave a valid object visible. Scope cleanup +releases live resources while the runtime is executing. Process death requires +external recovery, and paths resolve under OS process authority. See [storage](storage.md.html) and the +[IO validation contract](../test/io-v2/README.md). diff --git a/docs/language-features.md b/docs/language-features.md @@ -26,14 +26,14 @@ separate optimized backend. | Traverse or scan | Direct self-recursive functions; `let rec` / `where rec`; ordered equations | Plain locals remain nonrecursive; no mutual groups. | | Inspect a tree or positional structure | Constructor, pair, and list patterns in arguments or `case` | Pairs match Forks; lists match empty, cons, or exact-list shapes. | | Keep the original value while inspecting it | `whole{pattern}` | Captures are equation/arm-local. | -| Compare against an existing value | `^name`, including a qualified name | Exact tree equality, not a predicate or expression guard. | +| Compare against an existing value | `^name`, including a qualified name | Exact tree equality. | | Name intermediate work | `let` / `where`, including local equation groups | Plain groups are sequential/nonrecursive; `rec` marks one self-recursive function. | | Sequence computations | `do bindOperator` | Operator is explicit; its continuation receives one bound value. | | Make an application pipeline readable | `value |> function`, `function <| value` | Both pipe chains associate left-to-right. | | Branch without pattern equations | Library eliminators and lazy continuations | Handler order/truthiness depends on the actual helper. | | Compose failure-aware work | Result/Maybe helpers, parser combinators, IO helpers | They have different continuation/remainder protocols. | -| Check a runtime boundary | Contracts and argument/result annotations | Runtime checks, not types or automatic host exceptions. | -| Inspect code as data | Tree constructors, `triage`, structural equality/inspection | Equal application results do not imply equal function trees. | +| Check a runtime boundary | Contracts and argument/result annotations | Runtime checks with explicit failure handling. | +| Inspect code as data | Tree constructors, `triage`, structural equality/inspection | Functions with equal application results can have different trees. | | Organize/share code | Explicit native imports, namespaces, exports, and reexports | No implicit prelude or registry lookup. | ## Functions and tree values @@ -41,7 +41,7 @@ separate optimized backend. `name x y = body` defines a curried function; `(x y : body)` is a lambda. Functions can capture lexical bindings, be passed as arguments, and be returned. Application `f x y` means `(f x) y`; helper names such as `pair`, `true`, `if` -and `bind` are library bindings, not additional kernel primitives. +and `bind` are library bindings. The kernel shapes are `t` (Leaf), `t child` (Stem), and `t left right` (Fork). `triage` and structural equality make these representations observable, including @@ -84,7 +84,7 @@ Evidence: [pattern corpus](../test/source_equations.py), `let name args = value in body` and `body where name args = value` bind names lexically. Indented local blocks can contain multiple equation groups: each -sees outer bindings and earlier groups, not itself or later groups. Local names +sees only outer bindings and earlier groups. Local names can shadow outer names. Native top-level names cannot collide with imports. Application is left-associative and binds tighter than pipes. `f <| x` and @@ -95,7 +95,7 @@ Continue an application on lines indented beyond its owning definition or statement. The first operand may follow `=` on the next line; later operands can align with it. Same-column siblings and dedents end the expression. Extra indentation does not group arguments: use parentheses for a nested application. -Use spaces, not tabs, for source layout. Lists are whitespace-separated atoms: +Use spaces for source layout. Lists are whitespace-separated atoms: `[a b]` has two items; `[(f x)]` has one application item. Commas are not list separators. Pair-pattern syntax is not a general tuple-expression constructor: construct pairs with `pair left right` (imported) or `t left right`. @@ -121,17 +121,17 @@ Statements align at one indented column; deeper lines may continue an expression A binding scopes only over later statements. A non-final expression discards its result; the final statement must be an expression. A one-expression block returns that expression without invoking the operator. There is no implicit `pure` or -`return`. `<-` currently accepts one name, not a destructuring pattern. +`return`. `<-` accepts a single name. Choose the operator according to its actual protocol: | Operator | Continuation / behavior | | --- | --- | -| `bind` from `io` | One action result; constructs host-interpreted IO sequencing. Does not itself unwrap a Result. | +| `bind` from `io` | Passes one action result intact to its continuation; constructs host-interpreted IO sequencing. | | `onOk_` from `io` | Unwraps successful action Results to one value; propagates failures; deliberately drops the success remainder. | | `bindM` / `bindE` from `base` | One value in the pure interaction-tree layer; use `runM` to interpret it. Not a host IO interpreter. | | `maybeBind` from `base` | One successful Maybe value; propagates `nothing`. | -| `bindResult` from `base` | Calls its continuation with **value and remainder**; not directly interchangeable with a one-value `do` operator. | +| `bindResult` from `base` | Calls its continuation with **value and remainder**; one-value `do` requires an adapter. | | `parserBind` from `parser` | Continuation receives a parsed value and returns the next parser; parser machinery threads stream/progress. | If a Result pipeline needs `do`, choose explicitly whether its bound value @@ -166,7 +166,7 @@ Evidence: [do AST lowering](../lib/source/ast.tri), and [implementation](../lib/patterns.tri) for their callback conventions. - `native_math` provides direct compact-natural arithmetic/comparison and byte classification (`digit`, `letter`, `printable`, `inRange`, etc.). Its inputs - must be canonical naturals; it does not validate arbitrary trees. Compare + must be canonical naturals. Compare costs when choosing an arithmetic implementation. Current libraries share this binary engine with base arithmetic, add `divMod` and exponentiation by squaring, and format decimal numbers by repeated division by @@ -179,20 +179,65 @@ Evidence: [do AST lowering](../lib/source/ast.tri), starting name in it, as supplied by `graphSort`; it is not a cycle detector for arbitrary graphs. Sorting uses linear membership/removal scans. - `lexer` supplies reusable positioned scans and first-match dispatch. Positions - count source characters (LF advances line), not UTF-8 bytes or display cells. + count source characters; LF advances the line. Callers validate character/position domains. Quoted scans start after an opening delimiter; emit/resume adapters assume an outer prefix was already consumed. These helpers do not silently introduce backtracking after a selected failure. - `parser` already supplies reply diagnostics, consumed/committed flags, sequencing, alternatives, labels, repetition, and rollback. A parser reply is not merely an HTTP Result; preserve its full protocol when adapting it. -- `io` includes Result-aware handlers, cleanup helpers, state/environment and - concurrency actions. Current-source `bracket` requires Result-returning - acquisition: failures skip use/release; success supplies the resource value. - `finally` preserves the action's ordinary result and ignores the cleanup result. - Neither guarantees cleanup after abort, divergence or host exceptions. - These are explicit interaction trees; real effects need - the Haskell IO driver and permissions. Consult [host ABI](host-abi.md.html). +- `io` reexports `io_v2`: Result-aware sequencing, scoped files, bounded binary + chunks, child tasks, consumable joins, supervision, cancellation and ownership handoff, plus + staged publication and independent incremental SHA-256 contexts. Borrowed + `stdin`/`stdout`/`stderr` and source `copyStream` compose bounded binary pipelines + without closing inherited process handles. File readers/writers support absolute + positioning and size queries; update, append and resize support bounded edits + with explicit access modes and a shared cursor lease (not atomic multi-call + transactions). Path metadata, nonrecursive directory initialization/removal, + rename/unlink and scoped one-entry directory iteration support store workflows; + path resolution and filename encoding follow the host. Numeric + IPv4/IPv6 TCP listeners and duplex streams support scoped handoff, independent + read/write leases and write half-close. Explicit bounded host resolution and source + candidate fallback are separate from connecting; TLS and deadlines are not implied. + Authenticated `readIndexedTree` preserves DAG sharing using bounded transfer and + a scoped disk snapshot before decoding; callers select framing and disk budgets, + while decoded graphs still occupy memory proportional to their nodes. Canonical + indexed hashing/writing and exact-byte comparison avoid expanded-tree transport; + source code still owns publication and whole-file verification policy. + File and directory synchronization compose with `io_v2_objects`, which performs + source-owned sharding, no-replace publication, exact winner verification and + synchronization before returning object identities. Store roots must preexist; + synchronization is subject to host filesystem guarantees. Stateless bounded + UTF-8 codecs preserve strict scalar/byte semantics; `io_v2_text` composes text + writing and whole-source reads that preserve raw bytes and reject an initial BOM. + Whole-source conveniences retain their complete values in memory. + Its explicit single-consumer line cursor preserves bounded read-ahead and strict + incremental UTF-8, with a caller-selected encoded-byte limit per line. LF/EOF + framing is explicit; CR and BOM remain generic text content. Thread the returned + cursor forward and retire it on failure. Callers serialize composite operations. + `io_v2_storage` composes byte hashing, budgeted whole-file reads, immutable winner + verification and mutable alias replacement. Its source-only compiler projection + is selected as `compiler_io` by current compiler store callers; + see the isolated compiler procedure in the IO validation guide. + `io_v2_artifacts` + composes exact-length artifact publication in source, with verification of existing + winners. `IO.capabilities` reports implemented named/versioned facilities and + limits; `IO.inspect` classifies one action header without executing it or forcing + payloads/callbacks (using the chosen import alias for `IO`). These use OS process authority. `handlers.scopeV2`, `spawnV2` + and `runIOV2` inherit lexical services without sharing continuation frames. + Stateful collection must be installed inside the child explicitly. See the + [implemented contract and validation](../test/io-v2/README.md) for outcome shapes, + cancellation limits, unsupported facilities and backend boundaries. +- `handlers` supplies named request/response scopes and pure/host-IO runners. + `handler_log` supplies context, sinks and capture; `handler_artifacts` supplies + artifact storage; `handler_testing` supplies exact scripts. Service Results + remain ordinary computation values. `runPure` wraps completion in an execution + Result; `runIO`/`runIOV2` yield successful values and map handler failures to + terminal IO failures. Host scopes own resource cleanup. See + [scoped handlers](contracts.md.html#10-scoped-request-handlers). + `handler_upload` selects the scoped v2 producer/staged-publication composition; + see the [registry runner](build-and-run.md.html#deploy-the-arboricx-registry-linux). + Downloads retain whole values; producer uploads consume bounded chunks. - Contract helpers include `checkContract`, `withContract`, `guardC`, collection/higher-order combinators, and `x@contract` / `=@contract` annotations. Contracts and Boolean predicates have different protocols. Annotated success @@ -205,7 +250,7 @@ Evidence: [do AST lowering](../lib/source/ast.tri), Use `!import prelude *` for an explicit open import, `!import name` for qualified access, or `!import name as Alias` for a chosen qualifier. Full manifest hashes can be imported with an explicit alias or `*`. `!export name` selects a local -binding; `!export *` exports local definitions, **not imports**. +binding; `!export *` exports local definitions only. `!export * from REF` explicitly reexports and acquires that dependency. The CLI discovers source locations through the nearest `tricu.workspace`; it diff --git a/docs/language.md b/docs/language.md @@ -14,32 +14,29 @@ by the Tree Calculus rules. Applying a function is distinct from constructing Functions can receive, return, and inspect other functions. Structural equality observes their compiled trees; function representation depends on the compiler -and lowering backend. Equal results on chosen inputs do not imply equal trees. +and lowering backend. Functions with equal results on chosen inputs can have +different compiled trees. ## Evaluation and observation -Execution is non-strict: an application need not evaluate an unused argument, -and observing a constructor need not evaluate its children. Local bindings do -not themselves force their values. Pattern selection demands only the structure +Execution is non-strict: unused arguments, constructor children and locally +bound values remain lazy until demanded. Pattern selection demands only the structure needed by reached tests; an ignored subject or sibling and an unselected body remain unevaluated. A function can consume a finite prefix of a recursively produced tree without consuming the rest. -The Arboricx execution contract names this `normal-order`. This does not mean -that every invocation first produces a complete normal form. The observer -matters: inspecting a root constructor, decoding a number, rendering a complete +The Arboricx execution contract names this `normal-order`. The observer +determines demand: inspecting a root constructor, decoding a number, rendering a complete tree, and publishing an executable can demand different amounts of work. -Serialization requires a complete finite tree in normal form; an infinite or -partially defined runtime value is not itself a portable bundle. A finite function -tree that produces such a value when applied can be bundled. Normalizing the -function's representation does not guarantee eager-compatible execution: applying -it to an argument creates new computations whose termination can depend on the -evaluation strategy. Compilation includes normalization, with the construction +Serialization requires a complete finite tree in normal form. A finite function +tree can be bundled even when applying it produces an infinite or partially +defined value. Application creates new computations whose termination can depend +on the evaluation strategy. Compilation includes normalization, with the construction limits described below. The Haskell host implements demand through lazy fields and call-by-need sharing. -Physical pointer identity, exact reduction counts, and time/space bounds are not -source-language guarantees. Sharing a stored subtree is also distinct from +Physical pointer identity, exact reduction counts, and time/space bounds depend +on the implementation and workload. Sharing a stored subtree is also distinct from memoizing all applications of that subtree. See [compiler architecture](compiler.md.html) for implementation and regression-test boundaries, and [profiling](tri-tools.md.html#profiling) for ordinary observation versus explicit @@ -49,7 +46,7 @@ full-result forcing. The lexer consumes a list of Unicode scalar characters. Native source readers strictly decode UTF-8, rejecting malformed, overlong, truncated, surrogate and -out-of-range sequences. An initial UTF-8 BOM is rejected, not stripped. No +out-of-range sequences. An initial UTF-8 BOM is rejected. No normalization or newline conversion occurs. Strings and comments may contain Unicode scalars; identifier syntax remains ASCII. The browser compiler supplies character lists directly. @@ -67,8 +64,7 @@ character lists directly. The lexer produces located tokens. Spans are half-open; positions contain a one-based line and column plus a zero-based absolute character offset. LF moves -to column one of the next line. Columns count characters, rather than display -cells or encoded UTF-8 bytes. +to column one of the next line. Columns count characters. Unterminated strings and block comments use diagnostic code `5`. Invalid escapes use `6`, with the remainder beginning at the offending escape. Unsupported @@ -88,8 +84,7 @@ Lists contain whitespace-separated atoms. `[a b]` contains two elements; `f <| x` and `x |> f` both mean `f x`. Application binds tighter than pipes, and pipe chains associate left-to-right: `f <| g <| x` means `(f g) x`. -Application continuation is relative to the owning definition or statement, -not the first operand's column. A later line continues the expression when its +Application continuation is relative to the owning definition or statement. A later line continues the expression when its first token is deeper than that owner. These definitions are equivalent: ```tricu @@ -108,18 +103,17 @@ main = ``` Same-column siblings and dedents end the expression. Local-block definitions, -`case` arms and `do` statements establish their own boundaries. Extra indentation -does not implicitly group arguments: application remains left-associative. -Blank lines and comments do not change the boundary. +`case` arms and `do` statements establish their own boundaries. Application +remains left-associative across indented continuation. Blank lines and comments +preserve the boundary. Parentheses explicitly group expressions and allow newline continuation without an indentation requirement; a local/case/do block inside them still has its own -layout boundary. Inside brackets, newlines separate list atoms, not application -operands. Original token spans are preserved. +layout boundary. Inside brackets, newlines separate list atoms. Original token spans are preserved. A pipe operator or `where` on a later line may align with its owner or be deeper, but cannot cross a dedent out of an inner block. For example, a `where` dedented -past the last case arm belongs to the enclosing case expression, not that arm. +past the last case arm belongs to the enclosing case expression. A pipe operand beginning on a later line must be deeper than the owner unless explicitly grouped. Standalone expression parsing still does not join bare newline-separated operands outside parentheses. @@ -148,7 +142,7 @@ an indented application. ### Local bindings `let name args = value in body` and `body where name args = value` bind names -lexically. A plain binding scopes over the body, not its own value. Within a +lexically. A plain binding scopes over the body; its value sees the outer scope. Within a local block, each group sees outer bindings and earlier groups. The enclosing expression sees all groups. diff --git a/docs/modules.md b/docs/modules.md @@ -242,9 +242,9 @@ exact version**, separated by tabs, with LF endings and a final newline: ```text arboricx.project.v1 -foundation<TAB>home<TAB>tricu-base<TAB>0.1.0 -fileio<TAB>home<TAB>tricu-file-io<TAB>0.1.0 -inspection<TAB>home<TAB>arboricx-inspection<TAB>0.1.0 +foundation<TAB>home<TAB>tricu-base<TAB>0.5.0-io-v2 +fileio<TAB>home<TAB>tricu-file-io<TAB>0.5.0-io-v2 +inspection<TAB>home<TAB>arboricx-inspection<TAB>0.5.0-io-v2 ``` `<TAB>` above denotes one actual tab. The checked-in [`inspector @@ -258,8 +258,9 @@ projects are valid. Versions are opaque exact labels: `1.x` selects the literal version `1.x`. Each row must contain the header or a dependency declaration. With an explicitly trusted registry configuration whose `home` entry serves those -releases, prepare a fresh workspace (the site releases must be deployed or served -locally first): +releases, prepare a fresh workspace. The current inspector selects the locally +published v2 set produced by `test/library_inspector.py`; existing public v1 +releases remain separate. Serve the selected local registry before resolution: ```sh mkdir -p "$WORK/inspector" @@ -459,6 +460,13 @@ result/bin/tricu provenance capture --file "$WORK/inspector/main.tri" \ --project "$WORK/inspector/tricu.project" --output "$WORK/inspector-inputs" ``` +For an explicitly selected v2 compiler, pass `--toolchain-io-version 2` to **both** +compilation and capture. Capture records a v2 descriptor with explicit compiler IO +framing; it does not infer framing from code or receipts, and does not prove that +the producer selected the right version. Default v1 captures preserve their +historical descriptor bytes and pins. Native receipts and executable identities +are unchanged by this descriptor extension. + Capture prints the input-pack hash. It discovers the native root receipt through the latest cache locator, or accepts an explicit `--receipt HASH` if that locator is absent or another historical build is desired. Every reached source @@ -484,6 +492,13 @@ result/bin/tricu provenance rebuild --pack "$PACK" --pin "$INPUTS_HASH" \ --compile-timeout 900 ``` +Replaying a v2 pack additionally requires `--trust-io-version 2`. This explicitly +authorizes **OS process authority**. +The default trust version is 1; either direction of a version mismatch fails before +creating the rebuild directory. Authorization binds both the compiler hash and +the IO profile. Verification displays the declared version without +executing code. Rebuild selects that exact authorized version, never a fallback. + `verify` is read-only and checks the authenticated descriptor and its referenced closure as data. Its verification scope is that closure; unrelated files alongside it remain unverified. `rebuild` verifies first, requires explicit @@ -527,18 +542,20 @@ the real-library and false-claim acceptance tests. ## First published libraries: an offline bundle inspector -The initial reusable libraries expose the following public interfaces: +The current source interfaces are: | Release name | Authored interface | Exports | | --- | --- | --- | | `tricu-base` | `lib/packages/base.tri` | `append`, `matchResult`, `showNumber` | -| `tricu-file-io` | `lib/packages/io.tri` | `io`, `bind`, `readFile`, `sha256Hex`, `putStr`, `abortIO` | +| `tricu-file-io` | `lib/packages/io.tri` | `io`, `bind`, `readBytes`, `sha256Hex`, `putStr`, `abortIO` | | `arboricx-inspection` | `lib/arboricx/inspect.tri` | `inspectBundle`, `renderSummary`, `describeError` | Their closed executable exports include the required binary, numeric and parser -helpers, so consumers can use the closed exports directly. Grant IO permissions -separately when executing IO trees. Each release uses the ordinary one-module -package profile above. +helpers, so consumers can use the closed exports directly. The current file facade +constructs IO v2: `readBytes path limit` and `sha256Hex bytes limit` take encoded-byte +budgets; native execution uses OS process authority. Each release uses the ordinary +one-module package profile above. Historical published v1 releases retain their +original interfaces and bytes. [`demos/package-inspector/main.tri`](../demos/package-inspector/main.tri) is a real consumer of all three releases. Its `tricu.project` selects `tricu-base` as @@ -553,7 +570,7 @@ module manifest: ```sh result/bin/tricu arboricx run --store "$APP_STORE" \ --module "$INSPECTOR_MANIFEST" --name main --arg "$INPUT_BUNDLE" \ - --io --no-result --allow-read "$INPUT_BUNDLE" + --io --no-result ``` Output is a line-oriented, tab-separated report: diff --git a/docs/parser.md b/docs/parser.md @@ -58,8 +58,8 @@ on failure, but retains the diagnostic (including its location). code `3`, including an iteration that committed without consuming. An unconsumed, uncommitted failure ends repetition successfully; all other failures propagate, preserving prior consumption and commitment. `parserSepBy` and -`parserSepBy1` do not permit a trailing separator: consuming the separator and -then failing to parse the element propagates a consumed failure. `parserSepBy` +`parserSepBy1` require an element after each consumed separator; failure to +parse that element propagates a consumed failure. `parserSepBy` accepts an empty sequence only if its *first element* fails without consuming or committing; a later repetition failure (including zero-width success) is never converted into an empty sequence. @@ -67,8 +67,8 @@ converted into an empty sequence. `parserMergeFlags` is the common reply adapter used by bind and lexer composition; it preserves the selected payload/remainder while combining progress flags. -`parserMatchReply` matches only the status projection. It does not eagerly -destructure the entire reply: a handler may ignore payload, remainder or flags, +`parserMatchReply` matches only the status projection. A handler may ignore +payload, remainder or flags, even when those fields would diverge. Non-Leaf statuses retain the library's existing truthiness. Handlers can therefore preserve opaque or divergent unused fields. diff --git a/docs/storage.md b/docs/storage.md @@ -193,9 +193,13 @@ graph, preserving its receipt format and executable identities. See the workflow](modules.md.html#retained-build-inputs-and-reproduction). A portable input pack contains a `build-inputs` descriptor and a standard CAS -under `store/objects`. The descriptor is authenticated with the domain -`arboricx.build-inputs.v1`; retain/share its full hash independently of its -path. Its canonical LF/tab format records an entry name, the native root +under `store/objects`. IO v1 descriptors retain the historical header/domain +`arboricx.build-inputs.v1`. IO v2 descriptors use the separate header/domain +`arboricx.build-inputs.v2` and require the exact row `compiler-io\t2` immediately +after the compiler manifest row. Header/domain separation binds the execution +profile to the pin; missing, duplicate or other version rows are rejected. V1 +descriptors cannot embed a v2 override. Retain/share the full descriptor hash +independently of its path. Its canonical LF/tab format records an entry name, the native root receipt, a compiler manifest, sorted unit-name/receipt pairs, and optional project metadata pins. The inventory must exactly match the reachable native local-receipt graph; unit paths are derived as `NAME.tri`. diff --git a/docs/tri-tools.md b/docs/tri-tools.md @@ -35,6 +35,22 @@ SHA256. lets applications start with an empty store. Explicit overrides retain their verified executable-object hash as compiler identity in receipts. +`--toolchain-io-version 2` is the current compiler execution protocol and default, +using OS process authority. Version 1 is rejected by the current host; historical +compilers require their retained runtimes. The packaged compiler is authenticated +as v2. Provenance capture/replay requires separate explicit v2 selection and trust +authorization for the retained pack. +Only select trusted compiler code. Startup verification remains inert. + +The supervisor validates the canonical v2 envelope before demanding its action. +Unsupported or malformed framing terminates the invocation. Manifest/receipt +authentication remains required after execution. Retained +[application recipes](modules.md.html#retained-build-inputs-and-reproduction) and +[generation records](compiler-rebuilding.md.html#explicit-io-framing-and-transitions) +carry explicit v2 metadata. V2 application replay additionally requires +`--trust-io-version 2`; historical profiles remain explicitly v1. + + When the file argument is omitted, `eval` requires a complete native module on stdin, including its exported entrypoint. Stdin uses an isolated temporary workspace: use manifest imports, or a file for workspace-relative dependencies. @@ -96,8 +112,8 @@ result/bin/tricu arboricx inspect --file "$WORK/app.arboricx" --name main --as i result/bin/tricu arboricx inspect --file "$WORK/app.arboricx" --name main --as io --json ``` -`--as io` is required and selects the `tricuIO` version 1 envelope and host -action protocol. Pure contract effects use a separate protocol. `--name` +`--as io` is required and selects the `tricuIO` version 1 or 2 envelope and +corresponding host action protocol. Envelope sentinels and versions are canonical. Pure contract effects use a separate protocol. `--name` defaults to `main`. Supply an already materialized IO tree; apply any arguments needed to produce that tree before inspection. Only explicit child actions are followed; continuations and environment-transforming functions remain opaque, @@ -122,9 +138,8 @@ root: tricuIO v1 aliases: "main" root.action.resume: continuation (not applied) aliases: "afterRead" ``` -The demo spells out the IO constructors directly. Reading `settings.txt` occurs -only when the IO tree is executed; compilation and inspection treat its path as -data. +This historical v1 example spells out the constructors directly. Compilation and +inspection treat the path as data. Executing its read requires a retained v1 host. ### Inspect the actual Arboricx server @@ -138,40 +153,35 @@ result/bin/tricu arboricx compile --file demos/arboricx-inspection/main.tri \ --compile-timeout 900 result/bin/tricu arboricx inspect --file "$WORK/server-inspection.arboricx" --as io result/bin/tricu arboricx inspect --file "$WORK/server-inspection.arboricx" \ - --name serveView --as io + --name createView --as io ``` The `main` outline names every currently visible occurrence: ```text -main: tricuIO v1 - startupAction: Bind - initializeThenServe: Bind - initializeStore: Bind - ensureRootDirectory: Bind - createRootDirectory: CreateDirectory - storeRoot: "./store" - afterCreateDirectory: continuation (not applied) - afterRootDirectory: continuation (not applied) - afterInitialization: continuation (not applied) - finishStartup: continuation (not applied) +main: tricuIO v2 + startupAction: Scope + startupBody: scope callback (not applied) +``` + +The two startup aliases are structural projections of the actual application's +materialized tree. The scope callback stays opaque: inspection does not invent an +owner or apply it to discover later operations. Aliases establish exact tree +identity; names convey the author's interpretation. General helpers and +observationally equivalent expressions can have different trees. + +`createView` separately materializes the directory request: + +```text +createView: tricuIO v2 + createRootDirectory: Facility directory@1/create + storeRoot: arguments (not validated; backend availability not inferred) ``` -The outline uses export aliases in place of occurrence paths for readability. -Four configured values match directly: `storeRoot`, `createRootDirectory`, -`ensureRootDirectory`, and `initializeStore`. The remaining labels are -**author-named structural projections** of the materialized action. Projection -extracts existing subtrees as data; all four continuations remain opaque and -unapplied. Aliases establish exact tree identity; their names convey the -author's interpretation. Matching requires exact structural equality with the -specialized tree. General library helpers and observationally equivalent -expressions can have different trees. - -The independently materialized `serveView` exposes `serveRegistry`, its -`createSocket` action, and the opaque `afterSocketResult` continuation. -Bind/listen/accept remain behind that continuation and depend on the returned -socket. `initializeView` offers a shorter view of store initialization. Inspection -reads these trees; running the view exports with `--io` performs their effects. +`initializeView` materializes the complete initialization computation, exposing its +first directory request while leaving continuations opaque. These views supplement +the startup outline; they are not projections through its callback. Running the +view exports with `--io` performs their effects. Run the comparison in a new directory: @@ -199,15 +209,19 @@ have separate addresses. A generated path such as `root.action.resume` identifies an occurrence; multiple occurrences of one tree can execute effects independently. -`--json` emits `tricu.interaction-inspection.v1`: bundle fingerprint, selected -export, supported protocol version, limits, stored-node count, and a depth-first +`--json` emits `tricu.interaction-inspection.v2`: bundle fingerprint, selected +export, `supportedVersions: [1,2]`, limits, stored-node count, and a depth-first `occurrences` array. Each occurrence includes its path, depth, role, tree ID, export aliases, omitted-alias count, display summary, status, and optional reference to a previously described action occurrence. Summaries are display text whose wording may change; retain the artifact for exact payload data. Status is `decoded`, `opaque`, `invalid`, `unknown`, `truncated`, or `reference`. `opaque` marks a value whose internals remain unexamined. -Callability and type validity require separate checks. +Callability and type validity require separate checks. V2 named facility headers show group, +revision and operation, but arguments and scope/task callbacks remain opaque. +A decoded request is not evidence that a particular backend supports it; native +programs can query `io_v2.capabilities` separately. See the +[v2 protocol and inspection contract](../test/io-v2/README.md). | Option | Default | Meaning | | --- | --- | --- | @@ -237,14 +251,14 @@ ignores `--store`. ## IO and permissions -Select `--io` for interaction-tree execution. Grant file access with repeatable -`--allow-read PATH` and `--allow-write PATH` options. `--unsafe-io` grants broad -access and belongs only in trusted, deliberately selected runs. +Select `--io` for structured IO v2 execution. Compilation and application IO use +OS process authority. Current hosts reject `--allow-read`, `--allow-write`, their +all-path variants and `--unsafe-io` before application execution. There is no +runtime path-grant sandbox; use OS isolation when running untrusted programs. -Compilation grants reads to the selected source directory and application store, -and writes to the application store. Application execution uses its own grants. -Path grants authorize access within trusted programs and store directories. -Hostile-filesystem containment requires separate isolation. +Historical IO v1 bundles require a retained v1 runtime. Current runtimes do not +retry another protocol after failure. See [structured IO](../test/io-v2/README.md) +for resource ownership, task settlement and supported facilities. ## Host artifact checks @@ -335,7 +349,7 @@ source locations removed, run: ```sh python3 test/formatter_equivalence.py result/bin/tricu \ --formatter-directory "$FORMAT_DIRECTORY" --directory "$WORK/format-equivalence" \ - lib/tools/format-tri.tri + lib/io-v2.tri lib/io-v2/storage.tri lib/source/io-v2.tri ``` The check writes candidates and evidence to its new output directory and leaves diff --git a/docs/validation.md b/docs/validation.md @@ -134,9 +134,9 @@ nix build .# PYTHONDONTWRITEBYTECODE=1 python3 test/io_dependencies.py result/bin/tricu ``` -This opt-in probe compiles current `io`, `base`, and `native_math` sources with +This opt-in probe compiles current `io_v2`, `base`, and `native_math` sources with only those modules present in a temporary workspace. It checks IO constructor -and conditional results and rejects an explicit import of the absent `patterns` +versioned envelope/facility shapes and rejects an explicit import of the absent `patterns` module. Published compiler/support fixtures remain unchanged. The fast host policy tests separately check that the working compiler map is an exact closure and that IO depends only on `base`. @@ -522,6 +522,42 @@ python3 test/repair_support_fixture.py "$TRICU" "$SUPPORT_CANDIDATES" \ --capabilities 8 --verification-memory-mib 65536 ``` +`--source-root SNAPSHOT` selects explicitly retained authored sources instead of +the checkout; every selected source must still equal its prepared bytes/digest and +remain unchanged through publication. It does not change expected fixture/provider +identities or bypass receipt checks. + +When the original preparation is unavailable or no longer matches published +receipts, a fresh preparation can select a compiler and namespaced provider bundle +without pretending they form a completed generation: + +```sh +python3 test/fixture_sources.py materialize --source-root "$REFERENCE_SOURCE" +python3 test/native_library_migration.py "$TRICU" "$NEW_PREPARATION" \ + --compiler-bundle bootstrap/build-stable.arboricx \ + --compiler-providers test/fixtures/compiler-library.arboricx \ + --source-root "$REFERENCE_SOURCE" --backend stable --library LIBRARY_NAME +``` + +Select every required library explicitly (all 49 for full current-support retention). +Provider modules retain their **complete** interfaces, imported indexed; actual +source compilation produces new receipts. `selection.json` records compiler/provider +bytes, source/map selection, backend and IO version. V2 compiler bundles require +`--compiler-io-version 2`; generation selection instead uses its produced version. +Resume refuses changed selections. This preparation is not generation acceptance. +Only subsequent exact artifact/receipt comparisons can establish reproduction of +existing published support. + +Additional authored providers outside the compiler map/support candidate set may +be selected with repeated `--source-dependency ALIAS=RELATIVE_PATH`. They must be +native modules inside the selected source root; compiler/support alias overrides, +duplicates and path escapes are rejected. Their complete source bytes are staged, +and path/digest selections bind resume. Dependencies must be selected explicitly, +not discovered from an ambient workspace. Preparation and publication recheck +these source bytes against their staged copies. This does not add the providers +to the support cohort or substitute a partial exported interface; native compilation +records the actual dependency manifests/receipts in the preparation store. + The publisher also accepts multiple preparation directories. To replace a prior candidate explicitly, supply its directory first, the new preparation later, and repeat `--replace ALIAS` for each duplicate alias. It checks the retained @@ -537,6 +573,16 @@ records this relocation without changing the immutable origin membership. Use that option when publishing the current 39-module support set; ordinary candidate replacement preserves cohort membership. +The v4 support fixture explicitly adds ten IO-v2/handler providers so migrated +demos remain dependency-closed. Use `--relocate-patterns --add-io-v2-support` with +all49 genuinely rebuilt candidates. Python publication and the independent Haskell +verifier require the exact added aliases/paths, declarations and rebuild receipts; +ordinary v2/v3 membership rules are unchanged. No implicit cohort expansion is +permitted. `--compiler-fixture PATH` can bind a freshly packed compiler candidate +before installing the coordinated compiler/support fixtures; omitted, it uses the +published compiler fixture. Retention reproduction selects the same expansion +with `support.add_io_v2_support: true`. + Current indexed native-manifest reproduction uses indexed import and exact manifest comparison. Reproducing retained flat manifests expands trees: its default audit RSS/heap @@ -589,8 +635,10 @@ bash test/source_map_end_to_end.sh result/bin/tricu "$GENERATION" \ Check each probe's `--help` for its other required prepared artifacts. Equation coverage has independent AST/diagnostic controls; module/map probes use native -artifacts. `verified_tree_loading.py` covers verified transport and bounded -loading/publication measurements, outside the default checks. +artifacts. `io_v2_indexed.py`, `io_v2_indexed_write.py`, and +`io_v2_compiler_store.py` cover authenticated transport, canonical publication and +compiler storage. The old `verified_tree_loading.py` and `native_indexed_store.py` +CLIs reject before writes; the latter retains its independent wire-oracle helpers. Header-only equivalence uses the explicit `--native-header-equivalence` option of `refresh_compiler_fixture.py`. It authenticates exact staged bytes and @@ -996,19 +1044,19 @@ selected budget; proving nontermination requires separate analysis. ### Native source UTF-8 boundary (opt-in focused probe) ```sh -python3 test/native_source_utf8.py "$TRICU" --generation "$PREVIOUS" \ - --compiler "$MATCHING_RETAINED_COMPILER" --directory "$PROBE_DIRECTORY" +python3 test/native_source_utf8.py "$TRICU" \ + --compiler bootstrap/build-stable.arboricx --directory "$PROBE_DIRECTORY" ``` -This transition probe requires a matching retained flat-native compiler and -support closure. Use a Nix-built Haskell host and a new output directory. It -compiles current read/stage and map consumers with that retained compiler and -authenticated support exports. The published frontend is checked separately by +Use a Nix-built current Haskell host and a new output directory. This IO v2 +probe compiles complete workspace modules for read/stage, mapping and lexing +through the explicitly selected portable compiler. It neither splices source +into retained support nor supplies retired permission grants. The published frontend is checked separately by `test/default_frontend.py`, including Unicode source, raw-byte receipts, stdin, and malformed/BOM rejection. It checks exact input/overlay octets, source-domain fingerprints, ASCII parity, multibyte strings/comments, scalar coordinates, and malformed UTF-8/BOM rejection. Host unit tests separately cover binary IO -parity, permissions and invalid encoder scalars. Existing scalar-list fixtures +parity, grant rejection and invalid encoder scalars. Existing scalar-list fixtures cover scalar operations; file decoding requires the byte-level cases above. Limits are 8192 MiB, 900 seconds for compilation, 180 seconds per smoke and 1500 seconds for the whole job. Generation acceptance and fixture publication follow @@ -1154,6 +1202,21 @@ compiler, and verification after all producer files have been removed. Verification snapshots require unchanged files and mtimes. Each child has a 512 MiB GHC heap ceiling and a 30-second deadline. +For a retained-built v2 candidate application compiler, test actual offline replay: + +```sh +python3 test/io_v2_provenance.py result/bin/tricu \ + --compiler-bundle "$V2_CANDIDATE" --directory /tmp/new-v2-provenance +``` + +This captures a dependency graph with raw Unicode source, removes its producer, +rebuilds with packaged compiler data unavailable and checks exact receipts/module +identity and an independent executable result. A fully authenticated false claim +with a poisoned producer cache must still verify as data, then fail fresh replay. +The ordinary `test/provenance.py` controls additionally cover inert v2 verification, +domain-separated pins, strict metadata/CLI versions and authority mismatches that +must fail before output creation. Neither test accepts a compiler generation pair. + ## CLI workspace source locations `nix flake check` includes declaration parsing, ancestor discovery, nearest-file @@ -1292,3 +1355,139 @@ demand. Selected unit/reference artifacts are fingerprinted, not source-freshnes certified by this option; select audited rebuilt artifacts. Finite poison tests are not a general termination-equivalence proof. This check neither accepts a generation nor publishes fixtures. + +## Scoped handler library (opt-in) + +The native source libraries `handlers`, `handler_log`, `handler_artifacts`, and +`handler_testing` are checked with independent complete-tree and HTTP-byte +expectations: + +```sh +nix build .# +PYTHONDONTWRITEBYTECODE=1 python3 test/handler_library.py result/bin/tricu \ + --directory /tmp/tricu-handler-check --benchmark +``` + +Use a new directory. Omit `--benchmark` for just behavior and filesystem +checks. The runner compiles the current library sources using the packaged stable +compiler and retains inputs' digests, per-invocation logs, and `report.json`. +Checks cover lexical handler routing, scope/label restoration, forwarding, +contract transformation and malformed replies, ordinary versus interpreter +failures, nested log capture/context ordering, scripts, artifact-load/publication +contracts, and real OS permission-denial checks on scoped IO v2 operations. Pure interpretation +has a negative control proving it cannot execute the host IO bridge. The test-only +observer in `test/handlers/observe.tri` retains source diagnostics as data for +independent assertions. Production execution uses terminal v2 outcomes. The test +observer threads state explicitly and rejects implicit child-state inheritance. + +The optional benchmark compares complete successful artifact-GET responses and +identical structured log events for direct application calls, the handler +library with capture, and the handler library with an explicit source-state sink. +Each variant runs 1/20 requests in three fresh processes with rotating order. +Measurements include full response/log rendering with small files and warm local +reads. The direct baseline bypasses the additional service contracts; comparison +therefore covers complete application paths. Allocation volume counts cumulative +allocation; peak residency measures simultaneously live memory. + +The earlier API/interpreter comparison remains separately reproducible with +`test/effect_interfaces.py` and `test/effect_server.py`; see their adjacent +fixture README. Tree rewriting supplies a semantic reference. Compiler +reproduction and library publication use their separate acceptance workflows. + +### Obligation-matched handler cost comparison + +The library benchmark above retains a lighter direct application path. For a +same-run comparison with matching contract obligations on both paths, use: + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/handler_costs.py result/bin/tricu \ + --directory /tmp/tricu-handler-costs +``` + +`--checks-only` runs the negative controls without benchmarking. The default +uses five fresh-process samples per cell; `--repeats` accepts 3–10. Full response +and log outputs are checked against independent Python expectations. Reports use +schema `tricu.handler-costs.v1` and explicitly label the primary pair as +`matched-direct` versus `library-sink`; `direct` and `library-capture` +supply additional reference paths. + +The matched direct path uses the same digest, artifact-reply, logging-event and +logging-reply contract bodies as the library. Private contracts are exposed only +by adding export declarations in a temporary compilation workspace; production +source files and contract bodies remain unchanged. Both paths validate context, +retain the adapter's digest/path and content-authentication checks, record logs +through explicit sequential source state, validate +contract success envelopes, and render complete HTTP responses and logs. The +direct application path has no handler environments or request normalization; +both share the handler-backed filesystem adapter. Their remaining difference +includes scope/label bookkeeping and control-flow implementation, not +only lookup instructions. + +Test-only profiles separately omit API digest validation, deep response-byte +validation, context-list validation, event-shape validation, and sink-reply +validation. They never omit the backend digest/path guard or artifact Result +shape/remainder check. Counterexamples verify that omitted checks really abandon +invariants, while full direct/library probes compare diagnostics and attempted +operations on malformed inputs and replies. These profiles are not production +configuration or safe alternative APIs. + +The matrix uses twenty 3-byte responses and five 1-KiB responses, warm local +reads, and rotating variant order. `report.json` includes median and min/max +observation times, allocation volume, source/overlay digests and bundle digest; +`samples.json` retains individual measurements. Marginal check-removal differences +are not additive and include changed control-flow costs. Compare rows within one +report, not absolute timings from older experiments with different payloads, +logging implementations, compilers, or validation obligations. No networking or +concurrent-server throughput is measured. + +### Handler registry composition + +Use `nix build .#arboricxServerTest --no-link` for the actual IO v2 default +runner, including the test-only failed logging sink and common protocol/body +checks. `arboricxStreamingV2Test` exercises the explicit v2 runner through the +same gates. The v1 handler/chunked/body-probe Nix outputs are retired; earlier +measurements and executable rollback remain historical evidence, not current +runtime compatibility. See [the registry runner](build-and-run.md.html#deploy-the-arboricx-registry-linux). + +### Body ingestion and retention probes + +Measure the supported v2 server's source-owned staged uploads and whole-value +downloads. The old discard/count/hash and whole-body v1 comparison runners are +retired; their historical measurements are not relabeled as v2 results. + +```sh +nix build .#arboricxServerRunner -o result-server-runner +PYTHONDONTWRITEBYTECODE=1 python3 test/body_retention.py \ + --runner result-server-runner/bin/arboricx-server \ + --bounded-streaming \ + --directory /tmp/body-retention --sizes 65536 1048576 --repeats 3 +``` + +Run on Linux with port 8080 free. Each phase/size starts a fresh process, with +repeated requests in that process. Download phases use preseeded files rather +than inheriting the heap capacity of an earlier upload. Complete HTTP lengths, +payloads, and publication contents are checked independently. The script bounds +request waits and process RSS, retains partial reports on failure, and marks a +row complete only after its response assertions pass. + +The runner preserves the Nix executable's heap settings and supplies `GHCRTS=-s`. +A normal interrupt requests final RTS statistics; absent statistics remain null. +The report records allocation volume, GC-sampled maximum live heap, sampled RSS, +and RSS immediately and one/three seconds after each response. RSS measurements +validate the child PID namespace; they never substitute zero for missing memory +data. Peak live heap is not post-request live heap, and idle RSS includes retained +allocator capacity. A plateau over a few repetitions is not proof of leak freedom +or long-lived scheduler boundedness. These sequential phase probes do not test +concurrent maximum-size capacity, forced-GC behavior, or graceful shutdown. + +The default phases are `streaming-publish` and `streaming-download`. +`--phases streaming-publish` selects uploads only; `--bounded-streaming` requires +the upload phase's observed peak RSS to remain within128MiB and its GC-sampled +maximum live heap within16MiB. Missing RTS statistics fail that guard rather than +silently passing. Downloads retain whole values and are not covered by those +upload limits. These are workload regression guards, not service guarantees. + +`nix build .#arboricxServerTest --no-link` runs both phases at64KiB and1MiB with +three repetitions, alongside actual HTTP/security/lifecycle controls. This is +end-to-end chunk consumption/publication, not a change of socket receive size +alone. diff --git a/ext/demo-site/README.md b/ext/demo-site/README.md @@ -491,12 +491,11 @@ Expanded output has a 32 MiB byte bound and DAG has 128 MiB. Preflight rejects oversized expansion without constructing the exponentially large string. This complete-tree demand differs deliberately from the retired shallow JSON preview. -Inspection never interprets an IO-shaped result as execution authority. Browser IO is an -explicit profile for existing `tricuIO`, version 1, tags 0/1/2/10/11: -`pure`, `bind`, `abortIO`, `putStr`, `getLine`. `putStr` resumes with `t`; -`getLine` returns a canonical Unicode scalar list. Unsupported actions terminate -the run. Abort reports its diagnostic handle without demanding arbitrary -payloads. This narrower profile is not the full Haskell driver/error protocol. +Inspection never interprets an IO-shaped result as execution authority. The current +Conversation demo uses the structured IO v2 terminal profile described below. +Current adapters execute only IO protocol 2. Historical v1 bundles remain +inspectable data; they require retained runtimes for execution. No JavaScript +reference interpreter supplies IO compatibility. The bundle capability list must currently be empty, matching the existing format/profile; that list does not identify which exports produce IO trees. @@ -683,14 +682,14 @@ the exact pinned package/export. Clicking a definition name starts inspection; o The first source edit queues compilation and loads its compiler on demand. Explicitly selecting the compiler package simply loads that library. Work continues while using Settings or scrolling through the page. Source remains editable during compilation; active evaluation/IO locks editing -until completion or Stop. Page departure, Stop or replacement terminates the -owning Worker. +until completion or Stop. V2 Stop drains its structured session; page departure, +hard Stop of pure work, or replacement terminates the owning Worker. Compilation and program execution have no configurable node, evaluator-frame, work or active-time ceilings. The former optional-limit editors, settings and Worker request fields have been removed, rather than retained as hidden defaults. -Garbage collection remains enabled. Stop and page departure terminate running -Workers; actual browser/OS memory restrictions still apply. A divergent program +Garbage collection remains enabled. Pure Stop and page departure terminate +running Workers; v2 Stop waits for structured settlement; actual browser/OS memory restrictions still apply. A divergent program can run until cancelled, and a large live result can exhaust browser memory. Package loading retains a 60-second deadline covering Worker startup, transfer, @@ -717,11 +716,13 @@ and ordinary-package roles must satisfy the smaller download cap. inputs are bounded decimal strings; decoded naturals are capped at 4,096 bits. - Result previews: up to 64 distinct constructors; internal graph-preview lists: 32 entries with 8-node previews. Decoded text/natural lists: 4,096 entries. Strings/bytes have 4,096-element limits. -- IO: 128 actions; 256 scalars per line, no CR/LF; 16,384 output scalars. +- IO v2: 65,536 interpreted actions; 1–4,096-byte chunks; 256 input-line scalars + plus LF; 16,384 attempted output bytes shared across stdout/stderr. These are + protocol bounds, not pure-evaluation fuel, deadlines or heap quotas. - Main-page deadlines: 60 seconds for package loading/download and 120 seconds for bounded definition export. Program execution and compilation run until - completion, error or cancellation. Stop/reset/replacement terminates the Worker - without executing cleanup/finalizers. Browser timer throttling means loading + completion, error or cancellation. Pure Stop and emergency Worker teardown + do not execute cleanup/finalizers; v2 Stop instead waits for structured cleanup. Browser timer throttling means loading and export deadlines are not hard real-time guarantees. Output bounds apply independently of execution. No application setting promises @@ -1068,13 +1069,12 @@ caller handles valid. `Graph.collect(roots)` is an **explicit lifetime boundary**: list every handle needed afterward. Unreachable unlisted handles become invalid. It consumes fuel and commits the sweep only on success. Compilation lists all saved definition -handles, allowing compiler/input/decoder garbage to go. Compiler IO explicitly -owns its graph: `runIO(..., true)` traces current action + saved continuations -between steps, and may invalidate unrelated caller handles. Its ordinary API -keeps this off by default. No collector runs concurrently during a user pause. -Package sessions enable tracing (initial threshold 100k) and own their IO graph, -retaining application/continuation roots only. Compiler tracing starts at 250k; -compiler IO also lists all saved definitions as additional retained roots. +handles, allowing compiler/input/decoder garbage to go. The reference graph is +pure-only; it is not an IO executor. Native browser adapters retain their own +structured IO session roots, including pending work and completed results until +settlement/decoding. Reference compiler tracing starts at 250k cells; package +reference tracing starts at 100k. These reference settings are not native runtime +memory limits. Stats separate live cells (`nodes`), cumulative cell allocations (`allocations`), peak live cells, collections, reclaimed cells, and collector work. Both browser @@ -1094,8 +1094,11 @@ Linux WebKit harness against a built site. All sixteen current catalogue sources in `demos/` are editable native modules. The six Basic Examples teach one concept at a time; Language reference supplies -an editable syntax sampler. Conversation -imports IO; RGB imports canonical-byte validation. Tree microscope keeps the +an editable syntax sampler. Conversation imports the published IO v2 and text +modules, checks read/write Results, and distinguishes an empty line from EOF. +Send appends LF; End input returns an empty name with an end-of-input message. +Structured Stop retires the prompt and permits rerunning without recompilation. +RGB imports canonical-byte validation. Tree microscope keeps the structural traversal visible but imports successor/Boolean operations. Business examples import base arithmetic and, where needed, Arboricx's Unicode scalar-text validator. Their small record literals describe the demo output schema directly. @@ -1129,7 +1132,9 @@ parser/lexer examples through the real page. `prepare-libraries.py` publishes one complete public interface for every `lib/**/*.tri` source under `tricu-lib-MODULE`, including compiler helpers, experiments, and libraries requiring native host capabilities. It derives coverage -from the source tree. Fixture-backed +from the source tree and uses declared `tricu.workspace` aliases before filename +derivation for non-fixture modules. Fixture/workspace disagreements and alias +collisions fail before publication. Fixture-backed modules retain every authenticated public root; other library modules compile against those exact interfaces with an explicitly pinned stable compiler. @@ -1538,3 +1543,320 @@ Headless background tabs do not reproduce every desktop lifecycle condition. This check does not inject OS suspend, memory pressure, tab unloading, extensions, or production network/deployment changes. A clean run is not proof that an intermittent user-reported failure cannot occur. + +## IO version boundary + +Both production native adapters require bounded, canonical `tricuIO` version 2 +framing before observing the computation. Unsupported/noncanonical versions +reject without retry. `probeIO` recognizes version 2 without inspecting actions +or arguments. Historical v1 trees may be loaded and inspected, not executed. +The reference JS graph supports pure evaluation only. + +V2 uses the shared structured task/resource interpreter, not the old single-read +loop. `IO.capabilities` reports the actual selected backend (`haskell-wasm` or +`zig-wasm`), browser-terminal authority, core operations, implemented named +revision-1 facilities (`runtime`, `standard`, `stream`, `resource`, `utf8`) and +limits. Resource close implements borrowed-standard-handle rejection, not owned +file handles. File, network, process and clock facilities are unsupported and +leave their arguments opaque. `IO.inspect` classifies one header without forcing +its payload, continuations or unknown facility arguments. + +Public embedding callers supply `IOHost.v2` with identity-bound open/event/close +transport callbacks. These callbacks enqueue cloneable commands and must not +throw or synchronously reenter a turn. A legacy text-only host rejects v2 before +callbacks; it is never silently adapted to lossy byte output or uncancellable +reads. Stale replies cannot consume later waits. Unrelated compilation/inspection +is rejected while a v2 run owns the runtime. Selected result codecs run only after +all tasks, leases and host work settle, with the same full `ValueView` contract as +pure execution—not the private probe's bounded-natural summary. Result-decoder +errors leave a settled session reusable. + +The main-page terminal keeps exact stdout/stderr bytes and displays valid UTF-8, +marked invalid/incomplete byte escapes and exact hexadecimal. Input is explicitly +line-oriented, with separate LF-producing Send and EOF controls. Stop sends one +structured cancellation request, retires pending input before acknowledging it, +and waits for cleanup. Completed cancellation retains compiled definitions. +After acknowledged settlement the terminal retires before result decoding; a +subsequent divergent **pure result observation** can therefore be hard-stopped +without abandoning IO work (and that hard Stop discards the runtime definitions). +Emergency runtime loss also rejects idle reads/steps, explicitly marks the terminal +unacknowledged and discards the unusable runtime; it does not invent cancellation +replies. Emergency-disposal listeners are removed after every wait/turn, rather +than accumulating until the session is destroyed. +Stepping pauses **all tasks before one scheduler advance** (including lifecycle +progress), not before a native reduction or just a single selected child. Already +admitted terminal operations may complete while logical scheduling is paused. Stop +bypasses a paused step and cleanup is never step-gated. Non-stepped runs advance +in batches of at most128; completely blocked terminal waits do not spin-poll. + +There is no hard pure-reduction deadline or prompt-cancellation guarantee. In v2, +a divergent native observation can prevent reaching a cancellation boundary; +reload/navigation/fatal-runtime teardown is emergency disposal, **not** evidence +of cleanup. Pure evaluation retains hard Worker Stop. V2 trace hooks run only +for scheduler stepping and never inspect arbitrary payloads. Current source and +application IO use v2. The browser compiler remains the independently pinned +**pure** module compiler, not the native filesystem compiler. + +`TEST_IO_VERSION=1` checks inert current recognition and retired/unknown-version +rejection, missing-structured-host rejection before callbacks, and separately +pinned pure compilation. For public v2, +compile `test/io-v2/browser.tri` as below and select `TEST_IO_V2_PUBLIC=1` in the +same Chromium harness on both backends. It exercises compiled direct/handled +programs, scoped reader cancellation, binary/line/EOF output, every public codec, +>64-bit naturals, expression execution, capability/inspection queries, concurrent +operation rejection, codec-error recovery and real-page structured Stop/reuse. + +### Inert IO v2 wire controls + +`io-v2-protocol.ts` classifies one core/request header without executing it or +resolving a facility. Its handles are borrowed; callers retain the action while +using them. Canonical numbers, bounded ASCII names and revision bounds follow +`IOV2.Wire` (shared by native Haskell and its browser embedding) and Zig's wire +reader. This is not a capability declaration or a v2 execution entrypoint. + +```sh +nix build .#arboricxSite --no-link +SITE=$(nix path-info .#arboricxSite) +TEST_IO_V2_PROTOCOL=1 TEST_BACKEND=haskell-wasm ARBORICX_SITE_ROOT="$SITE" \ + nix shell --inputs-from . nixpkgs#nodejs nixpkgs#chromium -c \ + node ext/demo-site/test/backend-browser.mjs "$SITE/runtime/haskell" chromium +# Repeat with TEST_BACKEND=zig-wasm. +``` + +The private Haskell worker inspection operation and the Zig WASM adapter are +checked against independent constructor-only bundles: ten core headers, future +facility headers, exact bounds and malformed encodings. The test server exposes +the same Zig adapter source separately because release bundling inlines it. +Reference-graph tests additionally trap forbidden payload/excess-head/excess-tail +observations. Keep `TEST_IO_VERSION=1` controls alongside these: current adapters +reject retired versions and hosts without structured transport before callbacks. + +### Browser v2 lifecycle components + +`io-v2-tasks.ts` owns lexical/spawned scope states, supervision, consumable joins, +cycle checks, bounded secondary diagnostics and pending host-call ownership. +Normal return closes admission and joins children; failure/cancellation requests +subtree stop. Neither ordinary Results nor arbitrary payloads are inspected. +`payloads()` lists lifecycle-held evaluator roots, not body/continuation roots. +Consumed/discarded outcomes and closed scopes are removed rather than archived. + +`io-v2-host-work.ts` starts work without blocking sibling actions and separates +abort delivery from physical completion acknowledgement. Cancelled and stale +replies cannot revive bodies. Cleanup is a separate, non-cancellable batch per +scope, using that scope's reserved identity so body-call identity exhaustion +cannot prevent release. Cleanup failures still reach the outcome accumulator. +The embedding must own all host work through this queue, release resources in +reverse order, and consume cleanup results before settling a scope. Providers +must settle promises only after their actual cleanup; a provider that ignores +cancellation can delay scope exit indefinitely. No hard deadlines, JS allocation- +failure recovery or aggregate heap bound are implied. + +These components do not yet implement tree-action execution or the browser +session event bridge. They are not advertised through +runtime capabilities by themselves; public execution uses the composed provider described above. Lifecycle and +provider controls run with: + +```sh +nix shell --inputs-from . nixpkgs#nodejs -c node --test \ + ext/demo-site/test/io-v2-tasks.test.mjs \ + ext/demo-site/test/io-v2-host-work.test.mjs +``` + +After building the site, use `TEST_IO_V2_LIFECYCLE=1` in the Chromium command +above for a real DOM read with delayed cleanup acknowledgement and concurrent +pure work on each selected WASM runtime. This is a lifecycle/provider integration +probe, not execution of a v2 language program. Repeated-task tests establish +logical retirement/root removal, not measured JS heap or throughput bounds. + + +`io-v2-resources.ts` supplies the registry through `lifecycle()`, which binds task +release/settlement to resource guards. Use that factory rather than constructing +an unguarded task machine alongside a registry. Acquisitions are admitted before +host effects; a failed publication leaves its admission live until the caller +releases the raw backend and aborts. Admitted acquisitions may complete after +scope admission closes. Existing descendants retain borrowing access while a +normal parent exit joins them. + +Transfer validates the entire bounded handle list before task admission, rekeys +all old aliases, and preserves acquisition order regardless of argument order. +Leases reject wrong-kind, stale, closed and out-of-scope access before provider +work. Close claims block new operations; a busy claim is a wait, not permission +to retry physical IO. `HostWork.cancelBody()` can interrupt one provider operation +without cancelling its task. Release/lease acknowledgement is still mandatory. + +The guarded scope cleanup batch attempts owned backends once, in reverse +publication order, and retires wrappers for borrowed standard streams without +closing those external backends. Release errors remain failures, never an +implicit retry or a claim that an uncertain physical release succeeded. Secondary +diagnostics retain at most16 values; omitted errors are counted without calling +the diagnostic builder. A diagnostic-builder failure uses a prebuilt fallback +and does not skip remaining releases. Add `io-v2-resources.test.mjs` to the focused +Node command above. The Chromium lifecycle probe also checks handoff alias +invalidation, a real DOM reader lease, and physical resource removal before join. +These are provider/registry controls, not public v2 language execution support. + +### Private v2 tree interpreter validation + +`io-v2-core.ts` connects the guarded lifecycle to tree-calculus actions and bind +continuations. Its `advance()` performs a progress turn without awaiting a host +operation. The embedding must yield between batches, retain `roots()` across +turns, and keep temporary evaluator handles alive for the whole turn. Providers +return **deferred ordinary-Result encoders**: host completion is acknowledged +first; encoding happens in a later interpreter turn, and cancelled encoders never +run. Providers must enumerate additional retained tree handles through `roots()`. +The original request arguments remain rooted through provider acknowledgement. +Evaluator/embedding faults are distinct from protocol faults and ordinary Results; +`hasFault` also records rejection with `undefined`. Cleanup drains before the +embedding rethrows the saved fault. No hard pure-reduction deadline is implied. + +The private `io-v2-core` worker request executes bare computations, not public IO +envelopes. Haskell's `BrowserTrees.hs` keeps lazy trees in a worker-private handle +table with non-reused checked identities and between-turn root sweeps. Zig uses +its native owned handles and `sweepScope()` inside an exclusive async scope. +Neither sends evaluator handles over RuntimeSession or substitutes the JS +reference evaluator. Private providers reject all named facilities as ordinary +unsupported Results; this is not the browser terminal profile or a capability +advertisement. Public sessions compose this core with the terminal provider. + +```sh +# Include the existing lifecycle, resource and non-demand controls. +nix shell --inputs-from . nixpkgs#nodejs -c node --test ext/demo-site/test/io-v2-*.test.mjs +nix build .#arboricxSite --no-link +SITE=$(nix path-info .#arboricxSite) +TEST_IO_V2_CORE=1 TEST_BACKEND=haskell-wasm ARBORICX_SITE_ROOT="$SITE" \ + nix shell --inputs-from . nixpkgs#nodejs nixpkgs#chromium -c \ + node ext/demo-site/test/backend-browser.mjs "$SITE/runtime/haskell" chromium +# Repeat with TEST_BACKEND=zig-wasm. +``` + +These constructor-fixture probes cover actual native callback application, lexical +scopes, supervision, opaque terminal outcomes, ordinary unsupported requests, +malformed computation rejection, repeated reuse and per-turn root retirement. +Source-program terminal execution and resumable session events still need their +own integration controls before production dispatch changes. + +### Private resumable stream sessions + +`io-v2-session.ts` couples the interpreter to `io-v2-events.ts` using cloneable +run IDs, operation IDs and bounded byte data only. A turn advances at most128 +progress steps and never waits for a terminal operation. Multiple pending +operations remain visible; replies, polls and structured stop are separate turns. +The Haskell worker exposes private `io-v2-session-start`/`io-v2-session-turn` +requests, keeping the tree table alive across turns and rejecting unrelated work +until settlement. Zig uses the same session inside its exclusive owned scope. +Public RuntimeSession uses the same session with opaque outcomes and selected full codecs. + +The shared provider implements named standard input/output/error, byte-stream +read/write and borrowed-resource close rejection. Chunks are1–4096 bytes; EOF is +an explicit empty read and is sticky for this browser provider. Positive partial +write acknowledgement is returned to source, never silently retried. Both output +slots share a16384 attempted-byte budget: partial progress does not refund an +attempt, and failed/cancelled output poisons that slot before later byte payloads +are demanded. External standard backends are not closed during scope retirement. +The shared session also implements bounded strict UTF-8 encode/decode. The public +profile adds runtime capability and inert-inspection queries. + +Wrong-run, stale, wrong-kind and malformed replies preserve pending operations. +An operation cancelled before dispatch has no host effect; once dispatched, +abort delivery is not completion. The peer must remove listeners/drain work before +acknowledging. A valid normal completion racing cancellation counts as physical +acknowledgement, but its data is discarded. Event copies cannot mutate internal +validation state. Structured stop drains leases and scopes; destroying a worker +is still emergency disposal, not evidence of structured cleanup. No hard deadline +for pure reduction or an uncooperative provider is promised. + +Use `TEST_IO_V2_SESSION=1` with the Chromium command above for both backends. The +probe executes a native computation tree that starts a read, performs a sibling +write, and cancels the reader; a real DOM provider must finish cleanup before the +run completes. It also rejects wrong-run stop/replies and checks empty lifecycle +and transport records. Node event/session controls independently cover native +read-value framing, EOF, output poisoning, partial progress/budget, cancellation +races, identity exhaustion and repeated operation retirement. These remain private +constructor-based checks, not compiled source-program acceptance. + + +### Private UTF-8 and compiled-source validation + +Named `utf8` revision1 implements encode (at most1024 Unicode scalars) and decode +(at most4096 bytes). It rejects surrogate/overlong/out-of-range encodings and +invalid incomplete prefixes. Nonfinal valid incomplete suffixes are copied as at +most3 bytes of source-owned carry; final incomplete input is an ordinary error. +Generic decoding preserves BOM, NUL, CR/LF and combining characters without +replacement or normalization. Source `readSource` keeps its separate initial-BOM +policy. Excess scalar/byte list cells are rejected before their heads or tails +are demanded. Result encoding remains deferred to a live interpreter turn. + +`test/io-v2/browser.tri` exercises actual `io_v2`, `io_v2_text` and handler libraries +through the private native sessions. Compile it with a selected Nix-built host, +not a JS compiler or substituted evaluator: + +```sh +WORK=$(mktemp -d) +TRICU=/path/to/selected-nix-built-tricu +"$TRICU" arboricx compile --file test/io-v2/browser.tri \ + --store "$WORK/store" --output "$WORK/browser.arboricx" --compile-timeout 240 +nix build .#arboricxSite --no-link +SITE=$(nix path-info .#arboricxSite) +IO_V2_SOURCE_BUNDLE="$WORK/browser.arboricx" TEST_IO_V2_SOURCE=1 \ + TEST_BACKEND=haskell-wasm ARBORICX_SITE_ROOT="$SITE" \ + nix shell --inputs-from . nixpkgs#nodejs nixpkgs#chromium -c \ + node ext/demo-site/test/backend-browser.mjs "$SITE/runtime/haskell" chromium +# Repeat with TEST_BACKEND=zig-wasm and the identical bundle. +``` + +The test-only server route serves the explicitly selected bundle and its SHA-256; +normal catalogue loading/publication is not bypassed by production sessions. +Independent controls compare complete output bytes and result42 for direct and +handler-backed output, a byte-at-a-time Unicode round trip, scoped reader +cancellation, and malformed UTF-8/scalar rejection. Positive one-byte write +acknowledgements exercise source `writeAll`, including multibyte splits. Input +round trips include BOM/NUL/combining scalars and CR/LF. These tests use the private +byte-event interface, not the text-only user-interface widget. + + +### Byte-preserving output presentation model + +`io-v2-output.ts` is the presentation-only buffer for the terminal adapter; +it is not public v2 dispatch and does not acknowledge host operations. It retains +at most16384 accepted bytes shared across stdout/stderr. The session separately +limits attempted output. Append validation is atomic; inputs and snapshots do +not share mutable byte arrays with the buffer. + +Snapshots expose exact raw bytes, canonical hexadecimal, and typed text/byte +presentation tokens with stream-local offsets. Valid UTF-8 remains unchanged, +including BOM, NUL, combining characters and CR/LF. Invalid bytes are separate +byte tokens, not replacement characters. A valid incomplete suffix is pending +until more bytes arrive on the SAME stream; end-of-presentation marks it incomplete +without creating an IO failure. Actual byte tokens must be distinguishable from +literal escape-looking text when rendered. Do not interpret terminal control +sequences or insert program text as HTML. The public widget uses these tokens without changing IO acknowledgement semantics. + +`io-v2-output.test.mjs` checks exact byte reconstruction, hex, malformed sequences, +chunk-split invariance, stream separation, copied ownership and shared bounds. + +### Private terminal adapter validation + +`io-v2-terminal.ts` consumes shared session byte events. Input is explicitly a +line widget, not binary stdin: at most256 Unicode scalars, no embedded CR/LF or +unpaired surrogates, followed by exactly one LF. Empty Send produces LF; the +separate EOF control produces empty EOF. Encoded bytes are retained across bounded +reads (at most1025 bytes per submitted line). Invalid/stale input does not consume +a wait. Byte write acceptance is independent of display UTF-8 decoding. + +`io-v2-terminal-view.ts` renders text through text nodes, marks actual escaped +bytes distinctly from literal escape-looking text, and provides exact per-stream +hex. It does not interpret HTML or terminal control sequences. Each input wait +owns fresh DOM controls/listeners; cancellation clears/removes them before its +acknowledgement. Stop enqueues structured cancellation; it does not destroy the +worker. Finishing retires listeners only after pending input has settled. + +For the compiled-source probe above, add `TEST_IO_V2_TERMINAL=1` while retaining +`TEST_IO_V2_SOURCE=1`. Both native evaluators run the same source bundle through +real DOM controls: text/handler output, line/EOF, scoped cancellation, malformed +codec Results, binary output and external structured Stop. Additional DOM controls +check old prompts cannot consume new waits, empty lines differ from EOF, invalid +lines remain retryable, markup is inert, and repeated Stop is ignored. + +These probes also remain available independently of public execution. Use +`TEST_IO_V2_PUBLIC=1` instead of `TEST_IO_V2_SOURCE=1` for the public adapter and +real-page acceptance. No test-only bare-computation route is used by the page. diff --git a/ext/demo-site/bundles/00ebc0925d110a0733709f2aac0dedf4de7b59f33ac3eee107965496806cd77d.arboricx b/ext/demo-site/bundles/00ebc0925d110a0733709f2aac0dedf4de7b59f33ac3eee107965496806cd77d.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/035a709a17feb361abac0844c73f9b5f3c664549e5d81567963c3193816f9808.arboricx b/ext/demo-site/bundles/035a709a17feb361abac0844c73f9b5f3c664549e5d81567963c3193816f9808.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/070c4d26248d75ce9d1d66c19a4a7ad92558b9e14a837791b72f50e07afc5463.arboricx b/ext/demo-site/bundles/070c4d26248d75ce9d1d66c19a4a7ad92558b9e14a837791b72f50e07afc5463.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/08e1b5eb98d8314fdea7216819f0481df4354ee7a56192e692828f60e2b759a2.arboricx b/ext/demo-site/bundles/08e1b5eb98d8314fdea7216819f0481df4354ee7a56192e692828f60e2b759a2.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/09d6d5f32aa2f27b2d1864e7ee19955df8692190821072b67bbf75d3babe146a.arboricx b/ext/demo-site/bundles/09d6d5f32aa2f27b2d1864e7ee19955df8692190821072b67bbf75d3babe146a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/0a5d5d83f73a91959f8ff34a45643dae056456817c22569879e4675caf461086.arboricx b/ext/demo-site/bundles/0a5d5d83f73a91959f8ff34a45643dae056456817c22569879e4675caf461086.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/0ad0c17feaf6a8241ade01283210a1cded009eb4dd1cf99adcd31ea44017afba.arboricx b/ext/demo-site/bundles/0ad0c17feaf6a8241ade01283210a1cded009eb4dd1cf99adcd31ea44017afba.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/0ce352486ef2b94dc5aed1a40d5339ab0e90c41bb98e9959e26bcc2fb1e7590a.arboricx b/ext/demo-site/bundles/0ce352486ef2b94dc5aed1a40d5339ab0e90c41bb98e9959e26bcc2fb1e7590a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/1572e500aef32b7e069ecc985778f56cb5d4914ce99221d328817e2f10e168e8.arboricx b/ext/demo-site/bundles/1572e500aef32b7e069ecc985778f56cb5d4914ce99221d328817e2f10e168e8.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/15c3b172ba505e4904ced400deca1e6fdadeac1734b732191291c92ff8d245f5.arboricx b/ext/demo-site/bundles/15c3b172ba505e4904ced400deca1e6fdadeac1734b732191291c92ff8d245f5.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/16de0d621776b79ff676b9415405b19e3088a2409d51f2efac850acca249b119.arboricx b/ext/demo-site/bundles/16de0d621776b79ff676b9415405b19e3088a2409d51f2efac850acca249b119.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/18b24788592f74b9c564c30214dff0231cb86338661d944ab697e7ae4ca1103e.arboricx b/ext/demo-site/bundles/18b24788592f74b9c564c30214dff0231cb86338661d944ab697e7ae4ca1103e.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/1a1f3ea6c15e88e5929f151df64687a3dcb28b2d5f2091acb89c39081d4fe28d.arboricx b/ext/demo-site/bundles/1a1f3ea6c15e88e5929f151df64687a3dcb28b2d5f2091acb89c39081d4fe28d.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/1bced95d1d42116194b26c53779cc68900fd7c03263266603966243ae91f8545.arboricx b/ext/demo-site/bundles/1bced95d1d42116194b26c53779cc68900fd7c03263266603966243ae91f8545.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/1e053fdb9d77ecbebd9c1d8b861dccae0c83098b7555d4fa68b310b1e5113b6e.arboricx b/ext/demo-site/bundles/1e053fdb9d77ecbebd9c1d8b861dccae0c83098b7555d4fa68b310b1e5113b6e.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/20794e110733d119d3df5ae5a30a6f4917a12a8d4b4b4e55a887eea2148bc78d.arboricx b/ext/demo-site/bundles/20794e110733d119d3df5ae5a30a6f4917a12a8d4b4b4e55a887eea2148bc78d.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/20a5c725f2dc2c02598364f26fc86e122913bde38600fd6512da52e2dcef55de.arboricx b/ext/demo-site/bundles/20a5c725f2dc2c02598364f26fc86e122913bde38600fd6512da52e2dcef55de.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/258aa91dbed2e1a0170a8f0c2a87d3e11e9ac861ff74711460c14f735c6af1a2.arboricx b/ext/demo-site/bundles/258aa91dbed2e1a0170a8f0c2a87d3e11e9ac861ff74711460c14f735c6af1a2.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/25a9d7530dbb272a32516897c08f0672dd84676da8c3bc0828bcf2824ca5b5e9.arboricx b/ext/demo-site/bundles/25a9d7530dbb272a32516897c08f0672dd84676da8c3bc0828bcf2824ca5b5e9.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/275c92fc65c5f9ae4caa0a5d4183c5ccd6055c815b17f82da4b952761f8ad987.arboricx b/ext/demo-site/bundles/275c92fc65c5f9ae4caa0a5d4183c5ccd6055c815b17f82da4b952761f8ad987.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/2eb5dd5e3a372ef4e5c1ed612ca73eff2b0c9f97ebb651fb7ab22d5db9a15ca3.arboricx b/ext/demo-site/bundles/2eb5dd5e3a372ef4e5c1ed612ca73eff2b0c9f97ebb651fb7ab22d5db9a15ca3.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/2f6f3a1a9615134165e44ebb529d73608983f421bf2b483bc3f11e85cad27fa8.arboricx b/ext/demo-site/bundles/2f6f3a1a9615134165e44ebb529d73608983f421bf2b483bc3f11e85cad27fa8.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/320fbebc4604cdff3c3a3dba649966018ffb0c65241666108be05723b33284ad.arboricx b/ext/demo-site/bundles/320fbebc4604cdff3c3a3dba649966018ffb0c65241666108be05723b33284ad.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/3670141b6ee280efe14f6f0dd2ef9469560cdbfaa08d2510ca37ddc9802fd18d.arboricx b/ext/demo-site/bundles/3670141b6ee280efe14f6f0dd2ef9469560cdbfaa08d2510ca37ddc9802fd18d.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/36d76d7c128599998792fbeaa231ee416dc79b09c36fa20e1b1b5d2ead97076a.arboricx b/ext/demo-site/bundles/36d76d7c128599998792fbeaa231ee416dc79b09c36fa20e1b1b5d2ead97076a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/3bd031b7077f8cfe87da22b1fb5960fbf911fdaa5756821185b6431bc74be43b.arboricx b/ext/demo-site/bundles/3bd031b7077f8cfe87da22b1fb5960fbf911fdaa5756821185b6431bc74be43b.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/3c7cc88a1e7d6f11892357e15647186fa400b4aa50c7dbe7b9beb259b6ed48c2.arboricx b/ext/demo-site/bundles/3c7cc88a1e7d6f11892357e15647186fa400b4aa50c7dbe7b9beb259b6ed48c2.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/3f0be618d76da309e1ebc74099978d56b1bf9c5eb0d8e732c71b91ef223fd15a.arboricx b/ext/demo-site/bundles/3f0be618d76da309e1ebc74099978d56b1bf9c5eb0d8e732c71b91ef223fd15a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/3f8b3fc627479635890fa038e5fbac100db0d3014ed2f63c41ce1c4f856fb9bf.arboricx b/ext/demo-site/bundles/3f8b3fc627479635890fa038e5fbac100db0d3014ed2f63c41ce1c4f856fb9bf.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/3ff98271f9a512c04a8f200aa7488929edad50e1c7aa3864b243145f03cd3c9a.arboricx b/ext/demo-site/bundles/3ff98271f9a512c04a8f200aa7488929edad50e1c7aa3864b243145f03cd3c9a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/422e1a6e5cc0e164bf627e19278e598665fe0edc05161c37f27b1c196e58da60.arboricx b/ext/demo-site/bundles/422e1a6e5cc0e164bf627e19278e598665fe0edc05161c37f27b1c196e58da60.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/43799eb78baaff0314d6014ef7692661759901d4a29608c9a169176beb13b231.arboricx b/ext/demo-site/bundles/43799eb78baaff0314d6014ef7692661759901d4a29608c9a169176beb13b231.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/440618420eda88e9d8867ac5f99ade97dd8d678c44aa120995a96062e57c2045.arboricx b/ext/demo-site/bundles/440618420eda88e9d8867ac5f99ade97dd8d678c44aa120995a96062e57c2045.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/445db0a1f362b908bd0cfd4e8540e51630fa0ff3c639ce54a1f282bf7e6b2a78.arboricx b/ext/demo-site/bundles/445db0a1f362b908bd0cfd4e8540e51630fa0ff3c639ce54a1f282bf7e6b2a78.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/48194ad053a11151b87d129db717d01ea34381d9684281dfe876c41884508265.arboricx b/ext/demo-site/bundles/48194ad053a11151b87d129db717d01ea34381d9684281dfe876c41884508265.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/4872d3ee67db34a9ce6844e5a80befe217d6e248441d875d335da0e706e2b501.arboricx b/ext/demo-site/bundles/4872d3ee67db34a9ce6844e5a80befe217d6e248441d875d335da0e706e2b501.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/48ae69290f1858d9534fe1f60f7540ecaca466b6ec1f48a208c7b73981277fef.arboricx b/ext/demo-site/bundles/48ae69290f1858d9534fe1f60f7540ecaca466b6ec1f48a208c7b73981277fef.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/4b329565f4c47401fe505e35090ec332e52b266c72cebe6b7d78985efeef1ba9.arboricx b/ext/demo-site/bundles/4b329565f4c47401fe505e35090ec332e52b266c72cebe6b7d78985efeef1ba9.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/4b5a7389a4b3886049863f107d7ca840c19080f27ef99d76845de2264ac3d6e7.arboricx b/ext/demo-site/bundles/4b5a7389a4b3886049863f107d7ca840c19080f27ef99d76845de2264ac3d6e7.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/50d7c2af3c4d5aec428d9f97226992c7b12a72ec67dda5540e2e7d83d3ed0715.arboricx b/ext/demo-site/bundles/50d7c2af3c4d5aec428d9f97226992c7b12a72ec67dda5540e2e7d83d3ed0715.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/50d923a5471cc42bff0a82da06b21ab6616ce90e76e94c3fa3bf4f0ab70c49bd.arboricx b/ext/demo-site/bundles/50d923a5471cc42bff0a82da06b21ab6616ce90e76e94c3fa3bf4f0ab70c49bd.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/54d1e5652a001bf19668bda2f660e7ede120ce6e8670e02a95daf18469ab9d0d.arboricx b/ext/demo-site/bundles/54d1e5652a001bf19668bda2f660e7ede120ce6e8670e02a95daf18469ab9d0d.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/5d3381723aafa9c5e4c4c3a6975de9481eb3160cbe52aca24076900528ef2603.arboricx b/ext/demo-site/bundles/5d3381723aafa9c5e4c4c3a6975de9481eb3160cbe52aca24076900528ef2603.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/5d89ef0e68e4343c7a1da04c9dc6a82ff2e2089e770a1303f575b851c2814c4a.arboricx b/ext/demo-site/bundles/5d89ef0e68e4343c7a1da04c9dc6a82ff2e2089e770a1303f575b851c2814c4a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/5ec6ccb4827f0548444e480c0e6196ddaa7a570a44ebc6a91a922d7e59bf3012.arboricx b/ext/demo-site/bundles/5ec6ccb4827f0548444e480c0e6196ddaa7a570a44ebc6a91a922d7e59bf3012.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/5fb1fab64ccc6554ebeedb74c35f616833f64e007aa787d37eb544739ca7d371.arboricx b/ext/demo-site/bundles/5fb1fab64ccc6554ebeedb74c35f616833f64e007aa787d37eb544739ca7d371.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/62f77e46a310ae6dbfcd73d096126f0f6a16c61b98842d826a0b26783f733981.arboricx b/ext/demo-site/bundles/62f77e46a310ae6dbfcd73d096126f0f6a16c61b98842d826a0b26783f733981.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/6762fb2d66c52d04b4701b2508dd9ef6a8c62fcb66bfb607424f6b2caa488b85.arboricx b/ext/demo-site/bundles/6762fb2d66c52d04b4701b2508dd9ef6a8c62fcb66bfb607424f6b2caa488b85.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/684497e21954bc0392fab20009d93efafb4ed15315ae94e345f7e5c0530d0a13.arboricx b/ext/demo-site/bundles/684497e21954bc0392fab20009d93efafb4ed15315ae94e345f7e5c0530d0a13.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/6d156c31a1ecb7762df9fd94702dbf08773d81ca739b7cf806bc12ffe30ff10a.arboricx b/ext/demo-site/bundles/6d156c31a1ecb7762df9fd94702dbf08773d81ca739b7cf806bc12ffe30ff10a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/6e185ece27a038b020b9b3f3a305009209849436697fd19f5aa888a383f92f40.arboricx b/ext/demo-site/bundles/6e185ece27a038b020b9b3f3a305009209849436697fd19f5aa888a383f92f40.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/7a59f1f37047cadd22f97fb29e07f549e5f3a41b63b9af35e013669eb7a4893f.arboricx b/ext/demo-site/bundles/7a59f1f37047cadd22f97fb29e07f549e5f3a41b63b9af35e013669eb7a4893f.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/7b6b438b2e903d6364eed8e948a814a1ebf2d08dfa9960fd7c1b278222f8cf4e.arboricx b/ext/demo-site/bundles/7b6b438b2e903d6364eed8e948a814a1ebf2d08dfa9960fd7c1b278222f8cf4e.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/7c5ec2c441d6ab4ef23edc3e9941dbf7bb4a7718d9e6e93f8826171e3f92f3bc.arboricx b/ext/demo-site/bundles/7c5ec2c441d6ab4ef23edc3e9941dbf7bb4a7718d9e6e93f8826171e3f92f3bc.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/7e698de68504e56e6c93d7822b3fe8dca79fa7aadd30710be63a3b4710fda061.arboricx b/ext/demo-site/bundles/7e698de68504e56e6c93d7822b3fe8dca79fa7aadd30710be63a3b4710fda061.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/7ff7d2c5eead111a6364ca7f0dc0cc1e60910a3ffdd9bca18b8c624856e22f8d.arboricx b/ext/demo-site/bundles/7ff7d2c5eead111a6364ca7f0dc0cc1e60910a3ffdd9bca18b8c624856e22f8d.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/832388f8b0380e7f2bce6ca3e79f38ee6236c1d25232ea2cd5b6119c6eb41fba.arboricx b/ext/demo-site/bundles/832388f8b0380e7f2bce6ca3e79f38ee6236c1d25232ea2cd5b6119c6eb41fba.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4.arboricx b/ext/demo-site/bundles/848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa.arboricx b/ext/demo-site/bundles/890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/8b38dd5934bbc51f999e3a2da1ad33736991c7dd9aab5144b716611fc5a80810.arboricx b/ext/demo-site/bundles/8b38dd5934bbc51f999e3a2da1ad33736991c7dd9aab5144b716611fc5a80810.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/8ee2c0b4fec670afc9db5552e6907ace29ae2c0bda07897e70d451f9bb56160c.arboricx b/ext/demo-site/bundles/8ee2c0b4fec670afc9db5552e6907ace29ae2c0bda07897e70d451f9bb56160c.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/925989ee208fe9cfd108c6093da5648b87ace44e134c7b28b0772eb5a5c540fc.arboricx b/ext/demo-site/bundles/925989ee208fe9cfd108c6093da5648b87ace44e134c7b28b0772eb5a5c540fc.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/9cda2cdf30790c2d13dc51183a4fb321a00bec8599683004280f7317cf7a49ab.arboricx b/ext/demo-site/bundles/9cda2cdf30790c2d13dc51183a4fb321a00bec8599683004280f7317cf7a49ab.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/9daafde4c396549744c888778485c9d2316d89db659181e1c46c3b7ed5a0f64f.arboricx b/ext/demo-site/bundles/9daafde4c396549744c888778485c9d2316d89db659181e1c46c3b7ed5a0f64f.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/9f63810fee2e20762f1a198535448a2187a90065a35428ec82a11aad85575643.arboricx b/ext/demo-site/bundles/9f63810fee2e20762f1a198535448a2187a90065a35428ec82a11aad85575643.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/a11920a322cbec778cf81d92ce6bdf28ae3ccfc772f87be92545f8462136304a.arboricx b/ext/demo-site/bundles/a11920a322cbec778cf81d92ce6bdf28ae3ccfc772f87be92545f8462136304a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d.arboricx b/ext/demo-site/bundles/a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/a2c5b9dc0013aa6df913b6ac5cef4467d15c237b1a3c5a7205ce434252cd4865.arboricx b/ext/demo-site/bundles/a2c5b9dc0013aa6df913b6ac5cef4467d15c237b1a3c5a7205ce434252cd4865.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/a7babd04ce08dd587a9cc0e71d7d52e2850eb824118a3ea8b82b6b4b73f63521.arboricx b/ext/demo-site/bundles/a7babd04ce08dd587a9cc0e71d7d52e2850eb824118a3ea8b82b6b4b73f63521.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/a96e8e66c1759e1893a70a849c68eda171baf833b651d152102a8a7013f6e608.arboricx b/ext/demo-site/bundles/a96e8e66c1759e1893a70a849c68eda171baf833b651d152102a8a7013f6e608.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/a9c978eb8475b65bd73bd6ac6474e6242df285cea8d73d5dcb19ca5d30512949.arboricx b/ext/demo-site/bundles/a9c978eb8475b65bd73bd6ac6474e6242df285cea8d73d5dcb19ca5d30512949.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/ab19d9010aba0c7fcb6995c1c096872dc3bfb22a5798407011c01adfb7c57d8a.arboricx b/ext/demo-site/bundles/ab19d9010aba0c7fcb6995c1c096872dc3bfb22a5798407011c01adfb7c57d8a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/acada8dbbd27922e757f2331fae97eedcadae1f0db369f693bbb8d413c3b0b00.arboricx b/ext/demo-site/bundles/acada8dbbd27922e757f2331fae97eedcadae1f0db369f693bbb8d413c3b0b00.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/b0bb9f08d3a902c1b9e9649e58687d3c06e09421dd0c6d326ca854196da75878.arboricx b/ext/demo-site/bundles/b0bb9f08d3a902c1b9e9649e58687d3c06e09421dd0c6d326ca854196da75878.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/b1f763c54f8cbc87aebf1bb16fbe73805e09661b4cf8550edbdb9877ecf7a32c.arboricx b/ext/demo-site/bundles/b1f763c54f8cbc87aebf1bb16fbe73805e09661b4cf8550edbdb9877ecf7a32c.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/b60da1233b8b2a10ba7179e7f8fe39a07be8d2546c89ab36fadab28e2dfd8b23.arboricx b/ext/demo-site/bundles/b60da1233b8b2a10ba7179e7f8fe39a07be8d2546c89ab36fadab28e2dfd8b23.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/b6f32449d85257fbd0a589d29ef05ea4a503c4a7e6105926d408b0551a34fd77.arboricx b/ext/demo-site/bundles/b6f32449d85257fbd0a589d29ef05ea4a503c4a7e6105926d408b0551a34fd77.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/b75e428ce6d587893829792a87c3a374993d22c900eea793f2713e822440cb9f.arboricx b/ext/demo-site/bundles/b75e428ce6d587893829792a87c3a374993d22c900eea793f2713e822440cb9f.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/bb3135842e3cebfe6bd10e0766a6711e4d46a8cf157af1dcc9f97e25795ac0a1.arboricx b/ext/demo-site/bundles/bb3135842e3cebfe6bd10e0766a6711e4d46a8cf157af1dcc9f97e25795ac0a1.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/bba8e1d66b151e6b2aa42e8c73d61fa325e81d0388c0316b3e57fb0ddee3dde9.arboricx b/ext/demo-site/bundles/bba8e1d66b151e6b2aa42e8c73d61fa325e81d0388c0316b3e57fb0ddee3dde9.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/bfc61066613fa893c0d9e6e37659617bcaff9e97a8d633cffd1d84066857462d.arboricx b/ext/demo-site/bundles/bfc61066613fa893c0d9e6e37659617bcaff9e97a8d633cffd1d84066857462d.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/bffce842f768003315e90f106d296297477db17bdd2bd61752e7197a89042402.arboricx b/ext/demo-site/bundles/bffce842f768003315e90f106d296297477db17bdd2bd61752e7197a89042402.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/c2595128cd7056a33b634abc4a67bb99736b9cda08b6788e2936d8cd51fbaff1.arboricx b/ext/demo-site/bundles/c2595128cd7056a33b634abc4a67bb99736b9cda08b6788e2936d8cd51fbaff1.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/c3639067b4bdb79daa66253b95202c490f7545652864e718d1d558de0128a66a.arboricx b/ext/demo-site/bundles/c3639067b4bdb79daa66253b95202c490f7545652864e718d1d558de0128a66a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/c60b350474c0c82734204dfd18f5780d97d296e091bef977f37157bff57102e9.arboricx b/ext/demo-site/bundles/c60b350474c0c82734204dfd18f5780d97d296e091bef977f37157bff57102e9.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/c632e6845677fc13e40f02599f66a4e243fd91f9cef79a840ec4eb6953f66905.arboricx b/ext/demo-site/bundles/c632e6845677fc13e40f02599f66a4e243fd91f9cef79a840ec4eb6953f66905.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/c80f9fa19c06b6c30f7bea3c21b9fec4516c9aafc4ec9045185f9fdda64106dd.arboricx b/ext/demo-site/bundles/c80f9fa19c06b6c30f7bea3c21b9fec4516c9aafc4ec9045185f9fdda64106dd.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/cd7be3dbf2c10f7a7e7911765cb65a5207a1558f289406e291d07cb077a69a34.arboricx b/ext/demo-site/bundles/cd7be3dbf2c10f7a7e7911765cb65a5207a1558f289406e291d07cb077a69a34.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/cda1110a3577a751c2673bc7a1ce2c3994dec541a3c1f6034b6a6769eaee4809.arboricx b/ext/demo-site/bundles/cda1110a3577a751c2673bc7a1ce2c3994dec541a3c1f6034b6a6769eaee4809.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/cdda8a8efdd40b9f2d5beac446646831d11a6f1cf916a40d68f8064b90dda45a.arboricx b/ext/demo-site/bundles/cdda8a8efdd40b9f2d5beac446646831d11a6f1cf916a40d68f8064b90dda45a.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/ce83251eed3aa18a8bc53aeedda62dbd90ed91ecd270bc56460457b435a788c4.arboricx b/ext/demo-site/bundles/ce83251eed3aa18a8bc53aeedda62dbd90ed91ecd270bc56460457b435a788c4.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/d00af17115d7a201333b92e386997f57c1808f5819a89d18312a96bcdfe777a8.arboricx b/ext/demo-site/bundles/d00af17115d7a201333b92e386997f57c1808f5819a89d18312a96bcdfe777a8.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/d015f17a71941870f8ab9ba5cbbef82b7657a9217a64edf54e0b512cf3ea3bc9.arboricx b/ext/demo-site/bundles/d015f17a71941870f8ab9ba5cbbef82b7657a9217a64edf54e0b512cf3ea3bc9.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/d6bef87913b4b0859d181920121f95663325b679404c11b236941ee10dce0f1c.arboricx b/ext/demo-site/bundles/d6bef87913b4b0859d181920121f95663325b679404c11b236941ee10dce0f1c.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/d9f2858d088128d306525252c74025eb49565187b39da7e88c88b758cc4035d1.arboricx b/ext/demo-site/bundles/d9f2858d088128d306525252c74025eb49565187b39da7e88c88b758cc4035d1.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/de2b06e659b460d0ed6407f614396d1e9f6b43c455788c6660c88b153fc97fbc.arboricx b/ext/demo-site/bundles/de2b06e659b460d0ed6407f614396d1e9f6b43c455788c6660c88b153fc97fbc.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/e07c225c29eef3588dbb65cf0178e8b82a7d774786d0e392052877f7c4aecf5f.arboricx b/ext/demo-site/bundles/e07c225c29eef3588dbb65cf0178e8b82a7d774786d0e392052877f7c4aecf5f.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/e1376f99435b5869d7f53b489a5d7552b382f670710a6e0e970f13ddc330948b.arboricx b/ext/demo-site/bundles/e1376f99435b5869d7f53b489a5d7552b382f670710a6e0e970f13ddc330948b.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/e56378259592a4867829ca5447904b30ac5e21bd496eb68ad4d872c1af973d69.arboricx b/ext/demo-site/bundles/e56378259592a4867829ca5447904b30ac5e21bd496eb68ad4d872c1af973d69.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/e75e7178eb19bba565e1422eb140411e34c02df3db1fe32224fc434c6e6f5b69.arboricx b/ext/demo-site/bundles/e75e7178eb19bba565e1422eb140411e34c02df3db1fe32224fc434c6e6f5b69.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/e965542627b83e4d55e3ce15748650c94ef0d447324d7dde772c0db1b0651788.arboricx b/ext/demo-site/bundles/e965542627b83e4d55e3ce15748650c94ef0d447324d7dde772c0db1b0651788.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/eb3c997bcd9d2cd2be93191865894f9627946462526ed3cffe3fbbdad6e3f682.arboricx b/ext/demo-site/bundles/eb3c997bcd9d2cd2be93191865894f9627946462526ed3cffe3fbbdad6e3f682.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/ec65f6eac30f1487dc64e519ae0be9769695edbff58e529719424a95059c6875.arboricx b/ext/demo-site/bundles/ec65f6eac30f1487dc64e519ae0be9769695edbff58e529719424a95059c6875.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/ef1971af4ec38386365ef2fc2a9e4fe05f2dd8af3c8ca887a91789d9959a0ed2.arboricx b/ext/demo-site/bundles/ef1971af4ec38386365ef2fc2a9e4fe05f2dd8af3c8ca887a91789d9959a0ed2.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/ef3bbb54373753b15a2d3125e0f260efe2a1e7b464d451819afcc02721683bb2.arboricx b/ext/demo-site/bundles/ef3bbb54373753b15a2d3125e0f260efe2a1e7b464d451819afcc02721683bb2.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/f056d6c372cd700f325f6077a043492c9f4fb5d59e179ddfc00f69e608c7d969.arboricx b/ext/demo-site/bundles/f056d6c372cd700f325f6077a043492c9f4fb5d59e179ddfc00f69e608c7d969.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/f78cfa7217deb642299e0fa66d0d714311ec7a196dee4baa621881b7190c97d9.arboricx b/ext/demo-site/bundles/f78cfa7217deb642299e0fa66d0d714311ec7a196dee4baa621881b7190c97d9.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/fbc9e40b382d4ed621669432f2b1fd6f9353dfaba099f4b070b344eb3a30c4a6.arboricx b/ext/demo-site/bundles/fbc9e40b382d4ed621669432f2b1fd6f9353dfaba099f4b070b344eb3a30c4a6.arboricx Binary files differ. diff --git a/ext/demo-site/bundles/ffdd1a95b974e1745b846cfdfeaf0bcbf4b3b60e010f1d7184fe3333220a03cb.arboricx b/ext/demo-site/bundles/ffdd1a95b974e1745b846cfdfeaf0bcbf4b3b60e010f1d7184fe3333220a03cb.arboricx Binary files differ. diff --git a/ext/demo-site/catalogue.json b/ext/demo-site/catalogue.json @@ -377,9 +377,9 @@ "id": "greet", "title": "A conversation", "category": "Practical Examples", - "hash": "0811457d501113ee11194f90e279ebb4ef9d548c70cb48d146be03eb9af7505f", - "source": "!import 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 as IO\n!export main\n\n-- A conversation built from the IO library's actions and explicit bind operator.\n-- Evaluate main in Browser IO mode, then answer in the terminal input.\n-- The returned name is also displayed as Text. Try changing the greeting.\n-- Importing IO does not grant filesystem or network access in the browser.\nmain = IO.io\n do IO.bind\n IO.putStr \"What is your name?\\n\"\n name <- IO.getLine\n IO.putStr \"Hello, \"\n IO.putStr name\n IO.putStr \"!\\n\"\n IO.pure name\n", - "sourceSha256": "63ba77b14e307d836c253e9fbff5d0a04971316c616b2cb65f7a56295da5595d", + "hash": "b6f32449d85257fbd0a589d29ef05ea4a503c4a7e6105926d408b0551a34fd77", + "source": "!import 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b as IO\n!import 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df as Text\n!export main\n\n-- Run main, then Send a line or choose End input in the terminal.\n-- Read and write Results are checked before continuing.\n-- Inspect the returned name as Text. Try changing the greeting.\ngreet writer [[\"line\" name _] _] = do IO.require\n Text.writeText writer \"Hello, \"\n Text.writeText writer name\n Text.writeText writer \"!\\n\"\n IO.pure name\ngreet writer [[\"eof\"] _] = IO.require\n (Text.writeText writer \"End of input.\\n\") (_ : IO.pure \"\")\ngreet _ _ = IO.fail \"Invalid line reply\"\n\nmain = IO.io\n do IO.require\n reader <- IO.stdin\n writer <- IO.stdout\n Text.writeText writer \"What is your name?\\n\"\n line <- Text.readLine (Text.lineCursor reader) 4096\n greet writer line\n", + "sourceSha256": "9db644e07fa3c58e12b19f74ba0eedadb7e5ad056fe9432ee7599f09fbdc309b", "export": "main", "arguments": [], "output": "string", diff --git a/ext/demo-site/compiler-reference.v1 b/ext/demo-site/compiler-reference.v1 @@ -1,17 +1,17 @@ tricu.compiler-library-fixture.v1 module source.index source_index lib/source/index.tri d3db01d9854f2371c0e1d36a99de6b21596cc99306a3131f3f753e037a8efbce -module source.map source_map lib/source/map.tri 72638c3d2ec6f47598cba67c730249e8381c67cd5ba6437c4d24711e206afced +module source.map source_map lib/source/map.tri efa6213780eb641f7ec9ae6006d105a8110232ac06b455c335a5552324e58591 module source.unit source_unit lib/source/unit.tri 0883c40a3f5257574290b9598dca64073f52f9af126adf14c76f6974f26dfff0 module prelude prelude lib/prelude.tri 0c26a0142d15cc2a11a4317264491c797944e76ea8afa07a1efef240a70f4289 -module io io lib/io.tri c4ec206590d25d581b9a96f242c22936d8d0b2e9903331ce50b30ad7de254bd6 -module source.object source_object lib/source/object.tri 79cd7f19d3de74be38bac823d8674f6b74327a4ae12bb0cf405119b51fbd9ea5 -module source.module source_module lib/source/module.tri fb609a75a12659d2a05a63681c0cf96ef606045b4aebe044eac077c8cd23fbf2 +module compiler.io compiler_io lib/source/io-v2.tri 575f7c3c8834bd2780f444a0078e72366fe2f0de6c4e00b4c3722e4da8bb9693 +module source.object source_object lib/source/object.tri 0f98a415e9bcdd223cd27eba9b501e91fe6b3778a2d5ea59218815081ab3d767 +module source.module source_module lib/source/module.tri 49be76ef3607cedcafd4e6a599b90a7f0601b5d030e1431011e879342c9b0e91 module source.resolve source_resolve lib/source/resolve.tri 7f5f726392cbbdaa9f30aef09952a31eda14f034e04112a210f60f10542f6add -module source.build source_build lib/source/build.tri 5c24cc853df42e433916b1528fc8cd56334bd393606f54e7b12efb6a7069be0d +module source.build source_build lib/source/build.tri 3bfd3a8ea35a6316e1ef9cc53ac2418a9dbd6115c69d129a1b2de81c87877e7a module parser parser lib/parser.tri 31e8c679e086bdf9b68e05a4bb71fd413e66e53fd93b2e4b4a806930020b36e4 module source.order source_order lib/source/order.tri ec97f34c89ead701af75053ea340ead1bb0fda13d83b474ca760c3aa359970fa module source.compile source_compile lib/source/compile.tri 3764484ae723d4311cf04b769251badf7f7a190d9480389303847be1ce665bb9 -module source.program source_program lib/source/program.tri 7a99fcc8da4c502ff67bc723b3ef317409a84f608c607cfa99331dd30245845a +module source.program source_program lib/source/program.tri 297b2195345b9bf4c49857aacd48545604157a9a0f7b3cb5d8ec7759e2e41e83 module source.ast source_ast lib/source/ast.tri b6d05cace61c04032ea39a858dedc7482988dbb3ed47581a9a3239094263d314 module base base lib/base.tri 7ba2537cc4ec0571a7aa8a02557551245ca04f5bc08d9f9c1eb4d2606ec67b37 module bytes bytes lib/bytes.tri 6d057be42bc16788a34ce80cca7cb97251cf7dbbae8fd57100a31206ab3dd2a0 @@ -19,12 +19,15 @@ module conversions conversions lib/conversions.tri fd93454ad6cf7b0d906f367bed9a6 module source.lexer source_lexer lib/source/lexer.tri 94ee3a70dbed59015891f7f5c2ef588d64f1a885218edc35eed98d96d85fd11b module source.parser source_parser lib/source/parser.tri 23c8142e9629f029ae22b7c0b377bbfb69e4f9ecc5ce452094c47d063a49c9d6 module source.core source_core lib/source/core.tri b132ca49c22098c8a5d638517d701ad8141028d9389594ac2134240d240a2637 -module source.lock source_lock lib/source/lock.tri 31ffd58166bf861e82da0a90594cfb9c6c12e19baa51d5166fe0e4414d24af94 -module source.bundle source_bundle lib/source/bundle.tri 3fedb41000f22609b0bdab8fb222175d1347202eceabe7bdb983bc28b3258627 -module graph graph lib/graph.tri bc8622d006e357b1045f2cb3c775a8a58045610ee6f4a617858762d37947b5ec +module source.lock source_lock lib/source/lock.tri 52c3f1b1f0d29975e3932850451dc8e3cee93ac475dac869d8e9eba7838bd830 +module source.bundle source_bundle lib/source/bundle.tri 2373335a22f1a6c7d00c7dd612ec8498deb8502eaf48bd6e8aa9f9407a1bfc13 +module graph graph lib/graph.tri e98fc7955feb673b86ac9176a211136493d6247b011cb80995fb53464028f976 module lexer lexer lib/lexer.tri 946bbf9797a5cf722c6e85f36ac36b1ec2dd3d1f3b2650187c182dff12162bf7 module source.pattern source_pattern lib/source/pattern.tri b27f1eac18cbe8ad80cdd91f9f546b2d2a782b6cc5c5676232491b95751abe5a module source.equations source_equations lib/source/equations.tri 825f175f5c6dd56e103b94e924283acf5913cbc4554fb42fc07a034f460d2076 -module source.stable source_stable lib/source/stable.tri b407662fec9436bfb7cc06eed2c14961174c6fa19863413ffc74d58ef72c5431 +module source.stable source_stable lib/source/stable.tri 0d38a5a7511d4a08f648dfa28beaa312f9139bf9639f7779f2f83ac45134f439 module math.native native_math lib/math/native.tri e9d0b87564d13695bef695c552e9707710d9147877f9d9dc5282c7335664cc57 -bundle aa29656c146d2fc239d27da869498670c401cf344516314d06b7e5634c02d6f1 +module io.v2 io_v2 lib/io-v2.tri 8b2e1cd6ef5acc8454a690875d2e8415869892f84c3fb5198c18fc8e665d0f32 +module io.v2.storage io_v2_storage lib/io-v2/storage.tri 64e2248dbd4a05fc78b9d92cd775bb111951af95e384b91000e7f7aa32410013 +module io.v2.text io_v2_text lib/io-v2/text.tri 1ca2682e6a35d949196ff3b1d2aabed47af57e7ef43b8e32b2b7b19ca004f64f +bundle 11e9432733bb2b4579b65c4479b1b7dda5b5a56a4fd837fdd88f9a6ebc9093dd diff --git a/ext/demo-site/compiler.json b/ext/demo-site/compiler.json @@ -1,8 +1,8 @@ { "schema": 1, - "hash": "91f78730b3623d019e33f96e9e2712d61b04837b21ef3eccec3071ac9260944d", + "hash": "890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa", "export": "source_unit.compileStableModuleDefinitions", "backend": "stable", "reference": "test/fixtures/compiler-library.arboricx", - "referenceCommit": "b11bc756c6e1dea2e52b60c26314c9d304348ca5" + "referenceCommit": "70371f1a12c20e1174a9cbdc082ff797aed56e3c" } diff --git a/ext/demo-site/demos.json b/ext/demo-site/demos.json @@ -1,11 +1,11 @@ { "schema": 1, "backend": "stable", - "nativeCompilerSha256": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e", + "nativeCompilerSha256": "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4", "compilerExport": "source_unit.compileStableModuleDefinitions", - "compilerBundleSha256": "91f78730b3623d019e33f96e9e2712d61b04837b21ef3eccec3071ac9260944d", - "compilerManifest": "c3d5c2c0efdac3641467968669031ba1bc21883db7307772a594c8dc04ef1582", - "hostSha256": "8661383d0db249c34ac2a4b33ad606d15c416446f367df905b9da7bbf23fa15a", + "compilerBundleSha256": "890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa", + "compilerManifest": "22fccc94ad2972cf4ae81c73cf6f2e13aa39c0c12456083cb9811242520c17da", + "hostSha256": "bfcb3d738c845119e3b843c771c3df96297906b380a381f0338444421b94e77c", "demos": { "names-values": { "demandCases": [], @@ -554,9 +554,9 @@ }, "greet": { "demandCases": [], - "source": "!import 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 as IO\n!export main\n\n-- A conversation built from the IO library's actions and explicit bind operator.\n-- Evaluate main in Browser IO mode, then answer in the terminal input.\n-- The returned name is also displayed as Text. Try changing the greeting.\n-- Importing IO does not grant filesystem or network access in the browser.\nmain = IO.io\n do IO.bind\n IO.putStr \"What is your name?\\n\"\n name <- IO.getLine\n IO.putStr \"Hello, \"\n IO.putStr name\n IO.putStr \"!\\n\"\n IO.pure name\n", - "sourceSha256": "63ba77b14e307d836c253e9fbff5d0a04971316c616b2cb65f7a56295da5595d", - "hash": "0811457d501113ee11194f90e279ebb4ef9d548c70cb48d146be03eb9af7505f", + "source": "!import 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b as IO\n!import 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df as Text\n!export main\n\n-- Run main, then Send a line or choose End input in the terminal.\n-- Read and write Results are checked before continuing.\n-- Inspect the returned name as Text. Try changing the greeting.\ngreet writer [[\"line\" name _] _] = do IO.require\n Text.writeText writer \"Hello, \"\n Text.writeText writer name\n Text.writeText writer \"!\\n\"\n IO.pure name\ngreet writer [[\"eof\"] _] = IO.require\n (Text.writeText writer \"End of input.\\n\") (_ : IO.pure \"\")\ngreet _ _ = IO.fail \"Invalid line reply\"\n\nmain = IO.io\n do IO.require\n reader <- IO.stdin\n writer <- IO.stdout\n Text.writeText writer \"What is your name?\\n\"\n line <- Text.readLine (Text.lineCursor reader) 4096\n greet writer line\n", + "sourceSha256": "9db644e07fa3c58e12b19f74ba0eedadb7e5ad056fe9432ee7599f09fbdc309b", + "hash": "b6f32449d85257fbd0a589d29ef05ea4a503c4a7e6105926d408b0551a34fd77", "mode": "io", "haskellCases": [ { @@ -586,6 +586,13 @@ "input": "<img src=x onerror=alert(1)>", "terminal": "What is your name?\nHello, <img src=x onerror=alert(1)>!\n", "result": "220202102102102100221020202102021021002210202102102021021002210210210202021021002202020202021002210210202021021021002202102020210210210022102102020202102100221020210210210210022020202102102102100220202020202100221021021021020210210022021021021020210210022102021020202102100220210202021021021002202102020210210210022102102102102021021002202102020210210210022102021021021021002210202020202102100220202102102021021002210202102020210210022021020202102102100220202102021021021002202020210202100221020202021021002210202021020210022021021021021021000" + }, + { + "export": "main", + "arguments": [], + "input": null, + "terminal": "What is your name?\nEnd of input.\n", + "result": "0" } ] }, diff --git a/ext/demo-site/demos/greet.tri b/ext/demo-site/demos/greet.tri @@ -1,15 +1,23 @@ -!import 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 as IO +!import 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b as IO +!import 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df as Text !export main --- A conversation built from the IO library's actions and explicit bind operator. --- Evaluate main in Browser IO mode, then answer in the terminal input. --- The returned name is also displayed as Text. Try changing the greeting. --- Importing IO does not grant filesystem or network access in the browser. +-- Run main, then Send a line or choose End input in the terminal. +-- Read and write Results are checked before continuing. +-- Inspect the returned name as Text. Try changing the greeting. +greet writer [["line" name _] _] = do IO.require + Text.writeText writer "Hello, " + Text.writeText writer name + Text.writeText writer "!\n" + IO.pure name +greet writer [["eof"] _] = IO.require + (Text.writeText writer "End of input.\n") (_ : IO.pure "") +greet _ _ = IO.fail "Invalid line reply" + main = IO.io - do IO.bind - IO.putStr "What is your name?\n" - name <- IO.getLine - IO.putStr "Hello, " - IO.putStr name - IO.putStr "!\n" - IO.pure name + do IO.require + reader <- IO.stdin + writer <- IO.stdout + Text.writeText writer "What is your name?\n" + line <- Text.readLine (Text.lineCursor reader) 4096 + greet writer line diff --git a/ext/demo-site/dist/app.js b/ext/demo-site/dist/app.js @@ -1,3 +1,4 @@ +import { terminalView } from './io-v2-terminal-view.js'; import { SourceEditor } from './source-editor.js'; import { definitionInspector } from './definition-inspector.js'; import { exampleGroups, exampleGroup, libraryLabel } from './program-catalogue.js'; @@ -33,6 +34,7 @@ let entries = [], releases = [], selected; let selection, versionPackage; let preferredExport, currentName = '', publishedNames = []; let worker, pending; +let ioVersion, v2Run, v2Widget, stoppingV2 = false; let operation, idleTimer; let compilerTimer; const deadline = new ActiveClock(() => finish(operationTimeout(operation ?? 'loading'), true)); @@ -80,16 +82,16 @@ const definitions = definitionInspector(sourceEditor, (id, name, output) => { deadline.reset(Infinity); deadline.start(); diagnostic(); + ioVersion = 0; + stoppingV2 = false; el('status').textContent = `Running IO · ${name}`; worker.postMessage({ type: 'run-io', name, output, step }); }, stop: () => stopButton.click(), - resume(value) { + resume() { if (!worker || !pending) return; - if (value === undefined && pending.kind !== 'step' || value !== undefined && pending.kind !== 'input') - return; - worker.postMessage(value === undefined ? { type: 'resume', id: pending.id } : { type: 'resume', id: pending.id, value }); + worker.postMessage({ type: 'resume', id: pending.id }); pending = undefined; deadline.start(); }, @@ -165,6 +167,11 @@ el('stored-download').onclick = () => { worker.postMessage({ type: 'export', export: el('stored-definition').value, format: el('stored-format').value }); }; function dispose() { + v2Widget?.abandon(); + v2Widget = undefined; + v2Run = undefined; + ioVersion = undefined; + stoppingV2 = false; cancelIdleCompile(); stopCompilerActivity(); deadline.pause(); @@ -177,7 +184,7 @@ function dispose() { pending = undefined; controls(false); } -function idle() { stopCompilerActivity(); deadline.pause(); operation = undefined; pending = undefined; controls(false); } +function idle() { ioVersion = undefined; stoppingV2 = false; stopCompilerActivity(); deadline.pause(); operation = undefined; pending = undefined; controls(false); } function finish(message, error = false, cancelled = false) { const previous = operation; if (error) @@ -198,6 +205,15 @@ function finish(message, error = false, cancelled = false) { el('package-info').textContent = `Package not loaded. ${message}`; } stopButton.onclick = () => { + if (operation === 'running' && ioVersion !== undefined) { + if (!stoppingV2) { + stoppingV2 = true; + worker?.postMessage({ type: 'io-stop' }); + } + stopButton.disabled = true; + el('status').textContent = 'Cancelling; waiting for cleanup…'; + return; + } imports.cancel(); finish(draft ? 'Stopped. Compile the draft again to inspect or run it.' : 'Stopped. Reload the package to inspect or run it.', false, true); el('status').textContent = 'Stopped'; @@ -241,17 +257,45 @@ function receive(data) { } definitions.event(data); switch (data.type) { + case 'io-version': + ioVersion = data.version; + break; + case 'v2-open': { + if (v2Widget) + throw new Error('Overlapping terminal runs'); + const current = worker; + v2Run = data.run; + v2Widget = terminalView(el('terminal-v2'), data.run, reply => { if (worker === current && v2Run === reply.run) + current?.postMessage({ type: 'v2-command', command: { run: reply.run, kind: 'reply', reply } }); }, () => stopButton.click()); + break; + } + case 'v2-event': + if (data.event.run === v2Run) + v2Widget?.handle(data.event); + break; + case 'v2-close': + if (data.run === v2Run) { + ioVersion = undefined; + if (data.settled) + v2Widget?.finish(); + else + v2Widget?.abandon(); + v2Widget = undefined; + v2Run = undefined; + el('status').textContent = data.settled ? 'IO settled; interpreting returned value' : 'Runtime lost'; + } + break; case 'state': if (data.state === 'running') { pending = undefined; deadline.start(); - if (operation === 'running') + if (operation === 'running' && !stoppingV2) el('status').textContent = `Running IO · ${currentName}`; } else { deadline.pause(); - pending = { kind: data.state, id: data.id }; - el('status').textContent = data.state === 'input' ? 'Waiting for input' : 'Paused before action'; + pending = { id: data.id }; + el('status').textContent = 'Paused before action'; } break; case 'done': @@ -705,12 +749,21 @@ const examples = { contracts: 'id x =@nat? x\nanswer = id 42', pair: 'pair a b = t a b\nanswer = pair 7 "hi"', definitions: 'id = (x : x)\nanswer = id 42', - input: 'io=t "tricuIO" (t 1 (t 11 t))', }; for (const button of document.querySelectorAll('[data-compiler-example]')) button.onclick = () => { const math = packageReleases(releases, 'tricu-lib-native-math')[0]; - const source = button.dataset['compilerExample'] === 'module' && math ? `!import ${math.manifest} as N\n!export answer\nanswer = N.add 40 2` : examples[button.dataset['compilerExample']]; + const choice = button.dataset['compilerExample']; + const io = packageReleases(releases, 'tricu-lib-io')[0], text = packageReleases(releases, 'tricu-lib-io-v2-text')[0]; + const source = choice === 'input' && io && text ? `!import ${io.manifest} as IO +!import ${text.manifest} as Text +!export io +io = IO.io <| IO.require IO.stdin (reader : + IO.require (Text.readLine (Text.lineCursor reader) 4096) (reply : case reply of + [["line" value _] _] = IO.pure value + [["eof"] _] = IO.pure "" + _ = IO.fail "Invalid line reply"))` + : choice === 'module' && math ? `!import ${math.manifest} as N\n!export answer\nanswer = N.add 40 2` : examples[choice]; if (!source) return; definitions.reset(); diff --git a/ext/demo-site/dist/conformance/compiler-worker.js b/ext/demo-site/dist/conformance/compiler-worker.js @@ -1,13 +1,11 @@ // Test-only JS evaluator driver; excluded from the production site. import { compileExpression } from './expression.js'; -import { resumeValue } from '../io.js'; import { environmentProfile } from '../packages.js'; import { BROWSER_BUDGET, collectionPolicy } from '../execution.js'; import { contractEnvironment } from '../contracts.js'; import { Graph } from '../graph.js'; import { COMPILER_BUNDLE_LIMIT, fetchBundle, fetchBytes, loadBundle } from '../bundle.js'; import { literals, makeLiteral, outputType, view, list, text } from '../codecs.js'; -import { runIO } from '../io.js'; import { COMPILER_EXPORT, compilerDescriptor, sourceLiteral, sourceText, compiledDefinitions } from '../compiler.js'; const scope = globalThis; const base = new URL('../../', import.meta.url); @@ -15,14 +13,6 @@ const send = (message) => scope.postMessage(message); const running = () => send({ type: 'state', state: 'running' }); let phase = 'empty'; let graph, exports; -let requestId = 0; -let pending; -function request(kind) { - if (pending) - throw new Error('Driver already waiting'); - const id = ++requestId; - return new Promise((resolve, reject) => { pending = { id, kind, resolve, reject }; send({ type: 'state', state: kind, id }); }); -} async function compile(source, profileName) { phase = 'compiling'; running(); @@ -76,12 +66,7 @@ async function run(message) { const type = outputType(message.output), args = literals(message.arguments).map(value => makeLiteral(graph, value)); let result = message.expression === undefined ? graph.apply(fn, args) : fn; if (message.mode === 'io') - result = await runIO(graph, result, { - async before(event) { send({ type: 'trace', phase: 'before', event }); if (message.step) - await request('step'); running(); }, - after(event) { send({ type: 'trace', phase: 'after', event }); }, write(text) { send({ type: 'output', text }); }, - read() { return request('input'); }, running, - }, true, exports.values()); + throw new Error('Reference graph IO execution is retired; use a native runtime'); const displayed = view(graph, result, type); graph.collect(exports.values()); // Drop this run's temporaries, retaining every definition. phase = 'ready'; @@ -91,17 +76,6 @@ scope.onmessage = ({ data }) => { if (!data || typeof data !== 'object' || Array.isArray(data)) return; const message = data; - if (message['type'] === 'resume') { - if (!pending || message['id'] !== pending.id) - return; - const value = resumeValue(pending.kind, message['value']); - if (value === undefined) - return; - const waiter = pending; - pending = undefined; - waiter.resolve(value); - return; - } let operation; try { if (message['type'] === 'restore' && phase === 'empty') { diff --git a/ext/demo-site/dist/conformance/worker.js b/ext/demo-site/dist/conformance/worker.js @@ -1,28 +1,18 @@ // Test-only JS evaluator driver; excluded from the production site. import { sourceText } from '../compiler.js'; import { compileExpression } from './expression.js'; -import { resumeValue } from '../io.js'; import { publishedBundle } from '../published-bundle.js'; import { exportTree, treeFormat } from '../tree-export.js'; import { BROWSER_BUDGET, collectionPolicy, PACKAGE_COLLECTION } from '../execution.js'; import { Graph } from '../graph.js'; import { bundleHash, loadBundle } from '../bundle.js'; import { literals, makeLiteral, outputType, view } from '../codecs.js'; -import { runIO } from '../io.js'; const scope = globalThis; const send = (message) => scope.postMessage(message); const running = () => send({ type: 'state', state: 'running' }); let phase = 'empty'; let graph, exports; let stored; -let requestId = 0; -let pending; -function request(kind) { - if (pending) - throw new Error('Driver already waiting'); - const id = ++requestId; - return new Promise((resolve, reject) => { pending = { id, kind, resolve, reject }; send({ type: 'state', state: kind, id }); }); -} async function load(hash, localId) { phase = 'loading'; running(); @@ -49,12 +39,7 @@ async function run(message) { let result = message.expression === undefined ? graph.apply(fn, args) : fn; graph.collect([result]); // Exclusive one-run session: only this application remains owned. if (message.mode === 'io') - result = await runIO(graph, result, { - async before(event) { send({ type: 'trace', phase: 'before', event }); if (message.step === true) - await request('step'); running(); }, - after(event) { send({ type: 'trace', phase: 'after', event }); }, - write(text) { send({ type: 'output', text }); }, read() { return request('input'); }, running, - }, true); + throw new Error('Reference graph IO execution is retired; use a native runtime'); const displayed = view(graph, result, type); phase = 'done'; send({ type: 'done', result: displayed, stats: graph.stats }); @@ -63,17 +48,6 @@ scope.onmessage = ({ data }) => { if (!data || typeof data !== 'object' || Array.isArray(data)) return; const message = data; - if (message['type'] === 'resume') { - if (!pending || message['id'] !== pending.id) - return; - const value = resumeValue(pending.kind, message['value']); - if (value === undefined) - return; - const waiter = pending; - pending = undefined; - waiter.resolve(value); - return; - } if (message['type'] === 'download' && phase === 'ready' && stored) { send({ type: 'downloaded', hash: stored.hash, bytes: stored.bytes }); return; diff --git a/ext/demo-site/dist/definition-inspector.js b/ext/demo-site/dist/definition-inspector.js @@ -45,16 +45,14 @@ export function definitionInspector(input, request, io) { ioPanel.className = 'inline-io'; ioPanel.hidden = true; ioPanel.innerHTML = `<div class="inline-io-actions"><button type="button" id="run-io">Run IO</button><button type="button" id="stop-io" disabled>Stop</button></div> - <p class="hint">Recognized tricuIO v1 envelope. Only Run IO executes actions.</p> - <pre id="terminal" tabindex="0" aria-label="Program output"></pre> - <form id="input-form" hidden><label for="line-input">One line, up to 256 characters</label><div class="input-row"><input id="line-input" maxlength="512" autocomplete="off"><button>Send ↵</button></div><p id="input-error" role="status"></p></form> - <details><summary>IO details</summary><label><input id="step-mode" type="checkbox"> Pause before each IO action</label><button id="next" type="button" disabled>Next IO action</button> + <p class="hint">Recognized tricuIO v2 envelope. Only Run IO executes actions.</p> + <div id="terminal-v2"></div> + <details><summary>IO details</summary><label><input id="step-mode" type="checkbox"> Pause before each scheduler turn</label><button id="next" type="button" disabled>Next scheduler turn</button> <label for="io-return-format">Returned value (next run)</label><select id="io-return-format"><option value="tricu">tricu source</option><option value="string">Text</option><option value="number">Natural number</option><option value="values">List · text / numbers</option><option value="bool">Boolean</option><option value="bytes">Bytes</option><option value="triangle">△ source</option><option value="ternary">Ternary</option><option value="dag">DAG</option></select><ol id="trace"></ol></details>`; card.append(ioPanel); const get = (id) => ioPanel.querySelector('#' + id); const run = get('run-io'), stop = get('stop-io'); - const inputForm = get('input-form'), lineInput = get('line-input'); - const next = get('next'), terminal = get('terminal'), trace = get('trace'); + const next = get('next'), trace = get('trace'); const returnFormat = get('io-return-format'), step = get('step-mode'); let saved, ioDetected = false, ioRunning = false, suspended = false; function save(value) { @@ -97,28 +95,13 @@ export function definitionInspector(input, request, io) { return; ioRunning = true; clear(); - lineInput.value = ''; - get('input-error').textContent = ''; - terminal.textContent = ''; + get('terminal-v2').replaceChildren(); trace.replaceChildren(); - inputForm.hidden = true; status.textContent = 'Running IO…'; io.run(selected, outputType(returnFormat.value), step.checked); }; stop.onclick = () => io.stop(); next.onclick = () => io.resume(); - inputForm.onsubmit = event => { - event.preventDefault(); - const value = lineInput.value; - if (/[\r\n\uD800-\uDFFF]/u.test(value) || Array.from(value).length > 256) { - get('input-error').textContent = 'Enter one line of at most 256 Unicode scalar characters.'; - return; - } - get('input-error').textContent = ''; - inputForm.hidden = true; - io.resume(value); - lineInput.value = ''; - }; clear(); let selected, source = '', ready = false, busy = false, serial = 0; let pending, last = '', generation = 0, wasReady = false, scheduled = false; @@ -248,12 +231,9 @@ export function definitionInspector(input, request, io) { selected = name; suspended = false; ioDetected = false; - lineInput.value = ''; - get('input-error').textContent = ''; - inputForm.hidden = true; next.disabled = true; ioPanel.hidden = true; - terminal.textContent = ''; + get('terminal-v2').replaceChildren(); trace.replaceChildren(); clear(); format.value = 'auto'; @@ -347,25 +327,17 @@ export function definitionInspector(input, request, io) { event(message) { if (!ioRunning) return; - if (message.type === 'output') { - terminal.textContent += message.text; - terminal.scrollTop = terminal.scrollHeight; - } if (message.type === 'trace') { const row = document.createElement('li'); - row.textContent = message.phase === 'before' ? `${message.event.step} ${message.event.name}: ${message.event.detail}` : `→ ${message.event.value}`; + row.textContent = `${message.event.step} ${message.event.name}: ${message.event.detail}`; trace.append(row); } if (message.type === 'state') { - inputForm.hidden = message.state !== 'input'; next.disabled = message.state !== 'step'; - status.textContent = message.state === 'input' ? 'Waiting for input' : message.state === 'step' ? 'Paused before action' : 'Running IO…'; - if (message.state === 'input') - lineInput.focus(); + status.textContent = message.state === 'step' ? 'Paused before action' : 'Running IO…'; } if (message.type === 'done') { ioRunning = false; - inputForm.hidden = true; next.disabled = true; status.textContent = 'IO finished · Returned value'; save(message.result); @@ -373,7 +345,7 @@ export function definitionInspector(input, request, io) { position(); }, // Stop suspends observation until the user reopens the definition. - stopped(message, suspend = false) { suspended = suspend; ioRunning = false; inputForm.hidden = true; next.disabled = true; stop.disabled = true; status.textContent = message; }, + stopped(message, suspend = false) { suspended = suspend; ioRunning = false; next.disabled = true; stop.disabled = true; status.textContent = message; }, reset() { suspended = false; ioRunning = false; ioDetected = false; ioPanel.hidden = true; clear(); selected = undefined; pending = undefined; last = ''; card.hidden = true; result.textContent = ''; position(); }, update, reply(message) { @@ -389,7 +361,7 @@ export function definitionInspector(input, request, io) { else if ('io' in message && message.io) { ioDetected = true; ioPanel.hidden = false; - status.textContent = 'IO program · tricuIO v1'; + status.textContent = 'IO program · authenticated tricuIO envelope'; clear(); } else if ('result' in message) { diff --git a/ext/demo-site/dist/execution.js b/ext/demo-site/dist/execution.js @@ -1,5 +1,6 @@ // Execution has no application node/work/frame ceilings. Browser memory remains -// finite; Stop terminates the owning Worker. GC thresholds are not budgets. +// finite. Pure/v1 Stop terminates the Worker; v2 Stop drains its structured +// session at scheduler boundaries. GC thresholds are not budgets. export const BROWSER_BUDGET = Object.freeze({ nodes: Infinity, fuel: Infinity, stack: Infinity }); export const PACKAGE_LOAD_MS = 60_000; // Export is bounded transport/rendering, not program execution. diff --git a/ext/demo-site/dist/haskell-session.js b/ext/demo-site/dist/haskell-session.js @@ -1,3 +1,4 @@ +import { drive } from './io-v2-driver.js'; import { moduleSnapshot } from './module-bundle.js'; import { isTreeResult, treeResult } from './tree-result.js'; import { expressionUnit, expressionDiagnostic } from './expression.js'; @@ -8,6 +9,7 @@ import { fetchBytes } from './bundle.js'; export class HaskellSession { client; linearMemoryBytes = 0; + v2Active = false; constructor(client) { this.client = client; } @@ -34,6 +36,8 @@ export class HaskellSession { names = []; get disposed() { return this.client.disposed; } async request(message) { + if (this.v2Active && !['io-v2-session-turn', 'io-v2-public-view', 'io-v2-public-clear'].includes(message.type)) + throw new Error('IO v2 session is pending'); const reply = await this.client.request(message, null); this.linearMemoryBytes = reply.linearBytes; return reply; @@ -41,6 +45,8 @@ export class HaskellSession { async json(message) { return JSON.parse((await this.request(message)).text); } async load(bytes, hash) { this.names = await this.json({ type: 'load', bytes: new Uint8Array(bytes).buffer, hash }); } async compile(source, profileName, localModules = []) { + if (this.v2Active) + throw new Error('IO v2 session is pending'); this.names = []; const reply = await this.request({ type: 'compile', source, localModules, base: new URL('../', import.meta.url).href, ...(profileName === undefined ? {} : { profileName }) }); @@ -101,35 +107,26 @@ export class HaskellSession { }; if (mode === 'pure') return displayed(request); + let run; try { - let event = await this.json(request); - while (event.kind !== 'done') { - let value = ''; - switch (event.kind) { - case 'before': - await host.before({ step: event.step, name: event.name, detail: event.detail, depth: event.depth }); - break; - case 'read': - value = await host.read(); - break; - case 'write': - host.write(event.text); - break; - case 'after': - host.after({ step: event.step, name: event.name, value: event.value }); - break; - default: throw new Error('Unknown runtime IO event'); - } - host.running(); - event = await this.json({ type: 'io-resume', token: event.token, value }); - } - return await displayed({ type: 'io-view', output }); + const event = await this.json({ ...request, selectIO: true, v2Step: host.v2?.stepping === true }); + if (event.kind !== 'v2') + throw new Error('Unsupported tricuIO version'); + this.v2Active = true; + run = event.run; + if (!host.v2) + throw new Error('IO version 2 requires a structured terminal host'); + host.selected?.(2); + return await drive({ run, signal: this.client.signal, discard: () => this.dispose(), + turn: command => this.json({ type: 'io-v2-session-turn', ...command }), + view: () => displayed({ type: 'io-v2-public-view', run, output }), }, host); } finally { // Release result/continuations even when decoding or a host hook fails. // A poisoned instance cannot be recovered by cancelling the interaction. - if (!this.disposed) - await this.request({ type: 'io-cancel' }); + if (run !== undefined && !this.disposed) + await this.request({ type: 'io-v2-public-clear', run }); + this.v2Active = false; } } stats() { return { backend: 'haskell-wasm', linearMemoryBytes: this.linearMemoryBytes }; } diff --git a/ext/demo-site/dist/io-v2-core.js b/ext/demo-site/dist/io-v2-core.js @@ -0,0 +1,335 @@ +// Private resumable tree interpreter. No public session dispatch or capabilities. +// Adapters must retain roots() between turns and keep temporary evaluator handles +// alive for a whole turn. Provider promises acknowledge physical completion; +// providers return deferred Result encoders, never task outcomes. Encoding runs +// in an interpreter turn, not in a promise/GC gap; cancelled encoders never run. +import { canonicalNat, decodeAction, ProtocolError } from './io-v2-protocol.js'; +import { lifecycle } from './io-v2-resources.js'; +import { HostWork } from './io-v2-host-work.js'; +const operationErrors = new Set(['stale-task', 'join-cycle', 'out-of-scope', 'stale-handle', 'not-owner', + 'borrowed-resource', 'internal-resource', 'closed-handle', 'busy', 'duplicate-handle', 'closed-scope', 'invalid-argument']); +export class OperationError extends Error { +} +function logical(operation) { + try { + return operation(); + } + catch (error) { + if (error instanceof Error && operationErrors.has(error.message)) + throw new OperationError(error.message); + throw error; + } +} +export class Wire { + tree; + identity; + constructor(tree, identity) { + this.tree = tree; + this.identity = identity; + } + pair(value) { + const node = this.tree.observe(value).node; + if (node.kind !== 'fork') + throw new OperationError('invalid-argument'); + return [node.left, node.right]; + } + list(values) { let out = this.tree.leaf; for (let i = values.length - 1; i >= 0; i--) + out = this.tree.fork(values[i], out); return out; } + result(ok, value) { return this.tree.fork(ok ? this.tree.stem(this.tree.leaf) : this.tree.leaf, this.tree.fork(value, this.tree.leaf)); } + problem(category, operation, detail = '', progress = 'none') { + return this.list([category, operation, detail, progress].map(s => this.tree.text(s))); + } + marker(name, key) { return this.list([this.tree.text(name), this.tree.natural(this.identity), this.tree.natural(BigInt(key))]); } + token(value, name) { + const [tag, rest] = this.pair(value), [run, tail] = this.pair(rest), [key, end] = this.pair(tail); + if (this.tree.observe(end).node.kind !== 'leaf') + throw new OperationError('invalid-argument'); + let cursor = tag; + for (const char of name) { + const [head, next] = this.pair(cursor); + if (canonicalNat(this.tree, head, 7) !== BigInt(char.charCodeAt(0))) + throw new OperationError('invalid-argument'); + cursor = next; + } + if (this.tree.observe(cursor).node.kind !== 'leaf') + throw new OperationError('invalid-argument'); + const r = canonicalNat(this.tree, run, 64), k = canonicalNat(this.tree, key, 64); + if (r === undefined || k === undefined) + throw new OperationError('invalid-argument'); + // Unrepresentable keys cannot alias a live JS identity. Keep the ordinary + // stale-handle/task path rather than rounding into a different live handle. + return { run: r, key: k > BigInt(Number.MAX_SAFE_INTEGER) ? -1 : Number(k) }; + } + outcome(value) { + const name = this.tree.text(value.kind); + if (value.kind === 'completed') + return this.list([name, value.value]); + const errors = [...value.cleanup?.items ?? []], omitted = value.cleanup?.omitted ?? 0; + if (omitted) + errors.push(this.problem('cleanup-omitted', 'close', String(omitted))); + return this.list([name, value.value, this.list(errors)]); + } +} +export class Core { + tree; + provider; + actionLimit; + wire; + lifecycle; + work; + bodies = new Map(); + constants; + pendingArguments = new Map(); + cursor = -1; + actions = 0; + result = null; + // Embedding/evaluator failure is retained separately and never stringified or + // mistaken for an ordinary Result. Drain cleanup before rethrowing at boundary. + fault = undefined; + hasFault = false; + constructor(tree, identity, action, standard, provider, actionLimit = 65536) { + this.tree = tree; + this.provider = provider; + this.actionLimit = actionLimit; + if (!Number.isSafeInteger(actionLimit) || actionLimit < 1) + throw new Error('invalid action limit'); + this.wire = new Wire(tree, identity); + this.constants = { + stopped: this.wire.problem('cancelled', 'scope', 'owner stopped'), + cycle: this.wire.problem('join-cycle', 'scope'), fault: this.wire.problem('runtime-error', 'core'), + cleanup: this.wire.problem('cleanup-failed', 'resource/release', '', 'unknown'), budget: this.wire.problem('limit', 'core', 'IO action limit exceeded') + }; + this.lifecycle = lifecycle(identity, this.constants.stopped, this.constants.cycle, standard); + this.work = new HostWork(this.lifecycle.tasks); + this.bodies.set(0, this.body(action)); + } + body(action) { return { action, frames: [], mode: 'runnable' }; } + stop(reason) { if (!this.result) { + this.lifecycle.tasks.stop(0, { kind: 'cancelled', value: reason }); + this.work.cancelStopped(); + } } + done(scope, outcome) { + this.lifecycle.tasks.bodyDone(scope, outcome); + const body = this.bodies.get(scope); + body.mode = 'done'; + body.action = this.tree.leaf; + body.frames = []; + } + value(scope, value) { + const body = this.bodies.get(scope); + if (body.frames.length) { + body.action = this.tree.app(body.frames.pop(), value); + body.mode = 'runnable'; + } + else + this.done(scope, { kind: 'completed', value }); + } + operation(scope, name, action) { + try { + action(); + } + catch (error) { + if (!(error instanceof OperationError)) + throw error; + this.value(scope, this.wire.result(false, this.wire.problem(error.message, name))); + } + } + step(scope) { + const { tasks, resources } = this.lifecycle, body = this.bodies.get(scope), state = tasks.state(scope); + if (body.mode === 'done' || body.mode === 'lexical') + return false; + if (state.reason) { + this.done(scope, state.reason); + return true; + } + if (body.mode === 'host') + return false; + if (body.mode === 'join') { + const outcome = tasks.pollJoin(scope); + if (!outcome) + return false; + this.value(scope, this.wire.result(true, this.wire.outcome(outcome))); + return true; + } + if (++this.actions > this.actionLimit) { + tasks.stop(0, { kind: 'failed', value: this.constants.budget }); + return true; + } + let action; + try { + action = decodeAction(this.tree, body.action); + } + catch (error) { + if (!(error instanceof ProtocolError)) + throw error; + this.done(scope, { kind: 'failed', value: this.wire.problem('protocol-error', 'core/decode', error.message) }); + return true; + } + if (action.kind === 'request') { + // Keep action/arguments rooted until provider acknowledgement, including + // cancellation. The abandoned continuation is cleared by done(). + body.mode = 'host'; + const request = action; + this.pendingArguments.set(scope, request.arguments); + this.work.start(scope, async (signal) => ({ kind: 'body', encode: await this.provider.request(this, scope, request, signal) })); + return true; + } + const p = action.payload; + switch (action.code) { + case 0: + this.value(scope, p); + break; + case 1: { + const node = this.tree.observe(p).node; + if (node.kind !== 'fork') + this.done(scope, { kind: 'failed', value: this.wire.problem('protocol-error', 'core/decode', 'invalid v2 computation') }); + else { + body.frames.push(node.right); + body.action = node.left; + } + break; + } + case 2: + this.done(scope, { kind: 'failed', value: p }); + break; + case 9: + this.done(scope, { kind: 'cancelled', value: p }); + break; + case 3: { + const child = tasks.nested(scope); + this.bodies.set(child, this.body(this.tree.leaf)); + body.mode = 'lexical'; + body.action = this.tree.leaf; + this.bodies.get(child).action = this.tree.app(p, this.wire.marker('io.v2.scope', child)); + break; + } + case 4: + this.value(scope, this.wire.marker('io.v2.scope', scope)); + break; + case 5: + this.operation(scope, 'core/spawn', () => { + const [sup, a] = this.wire.pair(p), [keep, b] = this.wire.pair(a), [handles, callback] = this.wire.pair(b); + const supervised = canonicalNat(this.tree, sup, 1), retained = canonicalNat(this.tree, keep, 1); + if (supervised === undefined || retained === undefined) + throw new OperationError('invalid-argument'); + const tokens = []; + let rest = handles; + while (this.tree.observe(rest).node.kind !== 'leaf') { + if (tokens.length === 256) + throw new OperationError('invalid-argument'); + const [head, tail] = this.wire.pair(rest); + tokens.push(this.wire.token(head, 'io.v2.resource')); + rest = tail; + } + const moved = logical(() => resources.spawn(scope, supervised === 1n, retained === 1n, tokens)), child = moved.task.key; + this.bodies.set(child, this.body(this.tree.leaf)); + this.bodies.get(child).action = this.tree.app(callback, this.wire.list(moved.handles.map(t => this.wire.marker('io.v2.resource', t.key)))); + this.value(scope, this.wire.result(true, this.wire.list([this.wire.marker('io.v2.task', child), retained === 1n ? this.wire.marker('io.v2.join', child) : this.tree.leaf]))); + }); + break; + case 6: + case 7: + this.operation(scope, action.code === 6 ? 'core/join' : 'core/discard', () => { + const token = this.wire.token(p, 'io.v2.join'); + if (action.code === 7) { + logical(() => tasks.discard(scope, token)); + this.value(scope, this.wire.result(true, this.tree.leaf)); + } + else { + logical(() => tasks.claimJoin(scope, token)); + body.mode = 'join'; + body.action = this.tree.leaf; + } + }); + break; + case 8: + this.operation(scope, 'core/cancel', () => { + const [handle, reason] = this.wire.pair(p), token = this.wire.token(handle, 'io.v2.task'); + logical(() => tasks.cancel(scope, token, reason)); + this.value(scope, this.wire.result(true, this.tree.leaf)); + }); + break; + } + return true; + } + settled(scope, errors) { + const { tasks } = this.lifecycle, state = tasks.state(scope), outcome = tasks.settle(scope, errors); + this.bodies.delete(scope); + if (scope === 0) + this.result = outcome; + else if (state.kind === 'lexical') { + const parent = state.parent, reason = tasks.state(parent).reason; + this.bodies.get(parent).mode = 'runnable'; + if (reason) + this.done(parent, reason); + else if (outcome.kind === 'completed') + this.value(parent, outcome.value); + else + this.done(parent, outcome); + } + } + // One progress turn, never waits for a provider. False means wait for host + // acknowledgement. The embedding must yield to its event loop between batches. + advance() { + if (this.result) + return false; + try { + this.work.cancelStopped(); + for (const scope of this.pendingArguments.keys()) + if (this.lifecycle.tasks.state(scope).hostCalls === 0) + this.pendingArguments.delete(scope); + const reply = this.work.take(); + if (reply) { + if (!reply.outcome.ok) + throw reply.outcome.error; + const value = reply.outcome.value; + if (value.kind === 'release') + this.settled(reply.token.scope, value.errors); + else + this.value(reply.token.scope, value.encode()); + return true; + } + const { tasks, resources } = this.lifecycle; + for (const scope of tasks.scopeIds()) + if (tasks.state(scope).phase === 'joining' && tasks.beginRelease(scope)) { + this.work.release(scope, async () => ({ kind: 'release', errors: await resources.releaseScope(scope, resource => this.provider.release(resource), () => this.constants.cleanup, this.constants.cleanup) })); + return true; + } + const ids = tasks.scopeIds(), ordered = [...ids.filter(id => id > this.cursor), ...ids.filter(id => id <= this.cursor)]; + for (const scope of ordered) + if (this.step(scope)) { + this.cursor = scope; + return true; + } + return false; + } + catch (error) { + if (!this.hasFault) + this.fault = error; + this.hasFault = true; + this.lifecycle.tasks.stop(0, { kind: 'failed', value: this.constants.fault }); + this.work.cancelStopped(); + return true; + } + } + wait() { return this.work.wait(); } + *roots() { + yield this.tree.leaf; + yield* Object.values(this.constants); + yield* this.lifecycle.tasks.payloads(); + yield* this.pendingArguments.values(); + yield* this.provider.roots?.() ?? []; + for (const body of this.bodies.values()) { + yield body.action; + yield* body.frames; + } + for (const value of this.work.values()) { + if (value.kind === 'release') + yield* value.errors.items; + } + if (this.result) { + yield this.result.value; + yield* this.result.cleanup?.items ?? []; + } + } +} diff --git a/ext/demo-site/dist/io-v2-driver.js b/ext/demo-site/dist/io-v2-driver.js @@ -0,0 +1,102 @@ +// Unlike repeated Promise.race against a lifetime-long promise, listeners are +// removed on EVERY turn/step settlement, not retained until runtime disposal. +function interrupt(promise, signal, aborted) { + if (!signal) + return promise; + let onAbort = () => { }; + return new Promise((resolve, reject) => { + onAbort = () => { try { + resolve(aborted()); + } + catch (error) { + reject(error); + } }; + if (signal.aborted) + onAbort(); + else + signal.addEventListener('abort', onAbort, { once: true }); + void promise.then(resolve, reject); + }).finally(() => signal.removeEventListener('abort', onAbort)); +} +// The queue stays alive while a read or step prompt is waiting. Stop is sticky, +// independent of the resume queue, and never implemented by worker destruction. +export async function drive(driver, host) { + const transport = host.v2; + if (!transport) + throw new Error('IO version 2 requires a structured terminal host'); + let settled = false; + const alive = (promise) => interrupt(promise, driver.signal, () => { throw driver.signal.reason; }); + let active = true, stopping = false, stopSent = false, wake = () => { }, step = 0; + const replies = []; + let boundaryFault = false, fault; + const stopped = new AbortController(); + const enqueue = (command) => { + if (!active || !command || command.run !== driver.run) + return; + if (command.kind === 'stop') { + stopping = true; + stopped.abort(); + } + else if (command.kind === 'reply') + replies.push(command); + wake(); + }; + const close = () => { + if (!active) + return; + active = false; + stopped.abort(); + wake(); + wake = () => { }; + replies.length = 0; + // A failed transport/runtime cannot be reused with admitted work abandoned. + if (!settled) + driver.discard?.(); + transport.close(driver.run, settled); + }; + try { + transport.open(driver.run, enqueue); + for (;;) { + if (!stopping && transport.stepping === true) { + try { + await alive(interrupt(host.before({ step: ++step, name: 'v2 scheduler turn', detail: 'one scheduler advance; all tasks paused', depth: 0 }), stopped.signal, () => { })); + } + catch (error) { + if (driver.signal?.aborted) + throw error; + boundaryFault = true; + fault = error; + enqueue({ run: driver.run, kind: 'stop' }); + } + } + const command = stopping && !stopSent ? (stopSent = true, { run: driver.run, kind: 'stop' }) : + replies.shift() ?? { run: driver.run, kind: 'poll' }; + const report = await alive(driver.turn(command)); + for (const event of report.events) + transport.event(event); + if (report.done) { + if (!Object.values(report.counts).every(group => Object.values(group).every(value => value === 0))) + throw new Error('IO v2 settlement retains work'); + settled = true; + close(); + if (boundaryFault) + throw fault; + if (report.outcome?.fault) + throw new Error('IO v2 interpreter fault'); + if (report.outcome?.kind !== 'completed') + throw new Error(`IO v2 ${report.outcome?.kind ?? 'failed'}`); + return await alive(driver.view()); // Full codec, only after physical settlement. + } + // Yield even during pure scheduler progress so the outer worker can receive + // stop/replies. This is not a hard deadline for native tree reduction. + if (report.waiting && !replies.length && (!stopping || stopSent)) + await alive(new Promise(resolve => { wake = resolve; })); + else + await alive(new Promise(resolve => { const timer = setTimeout(resolve, 0); wake = () => { clearTimeout(timer); resolve(); }; })); + wake = () => { }; + } + } + finally { + close(); + } +} diff --git a/ext/demo-site/dist/io-v2-events.js b/ext/demo-site/dist/io-v2-events.js @@ -0,0 +1,94 @@ +function bytes(value, limit) { + return Array.isArray(value) && value.length <= limit && value.every(x => Number.isInteger(x) && x >= 0 && x <= 255); +} +export class Events { + run; + limit; + next = 1; + pending = new Map(); + queue = []; + constructor(run, limit = Number.MAX_SAFE_INTEGER) { + this.run = run; + this.limit = limit; + if (!/^[1-9][0-9]*$/.test(run) || run.length > 20 || BigInt(run) > 0xffffffffffffffffn || + !Number.isSafeInteger(limit) || limit < 1) + throw new Error('invalid event identity'); + } + get counts() { return { pending: this.pending.size, queued: this.queue.length }; } + read(limit, signal) { + if (!Number.isInteger(limit) || limit < 1 || limit > 4096) + throw new Error('invalid read bound'); + return this.start({ run: this.run, id: 0, kind: 'read', limit }, signal); + } + write(stream, value, signal) { + if (!['output', 'error'].includes(stream) || !bytes(value, 4096) || !value.length) + throw new Error('invalid write chunk'); + return this.start({ run: this.run, id: 0, kind: 'write', stream, bytes: [...value] }, signal); + } + start(event, signal) { + if (signal.aborted) + return Promise.resolve({ kind: 'cancelled' }); + if (this.next > this.limit) + throw new Error('event identity exhausted'); + event.id = this.next++; + return new Promise(resolve => { + const cancel = () => { + const entry = this.pending.get(event.id); + if (!entry || entry.cancelling) + return; + entry.cancelling = true; + if (!entry.delivered) + this.finish(event.id, { kind: 'cancelled' }); + else + this.queue.push({ run: this.run, id: event.id, kind: 'cancel', operation: event.kind }); + }; + this.pending.set(event.id, { event, delivered: false, cancelling: false, resolve, + remove: () => signal.removeEventListener('abort', cancel) }); + signal.addEventListener('abort', cancel, { once: true }); + this.queue.push(event); + }); + } + take() { + const out = this.queue; + this.queue = []; + for (const event of out) + this.pending.get(event.id).delivered = true; + // Never expose the mutable validation record or its byte array to the peer. + return out.map(event => event.kind === 'write' ? { ...event, bytes: [...event.bytes] } : { ...event }); + } + finish(id, value) { + const entry = this.pending.get(id); + this.pending.delete(id); + this.queue = this.queue.filter(event => event.id !== id); + entry.remove(); + entry.resolve(entry.cancelling ? { kind: 'cancelled' } : value); + } + // Wrong run/kind/bounds and stale/duplicate replies never consume a live wait. + // A valid normal completion can race with cancellation: it acknowledges work, + // but its data is discarded. No extra cancellation acknowledgement is needed. + reply(value) { + if (!value || typeof value !== 'object') + return false; + const v = value; + if (v.run !== this.run || typeof v.id !== 'number' || !Number.isSafeInteger(v.id)) + return false; + const entry = this.pending.get(v.id); + if (!entry || !entry.delivered) + return false; + let completion; + if (v.kind === 'cancelled' && entry.cancelling) + completion = { kind: 'cancelled' }; + else if (v.kind === 'error') + completion = { kind: 'error' }; + else if (v.kind === 'read' && entry.event.kind === 'read' && bytes(v.bytes, entry.event.limit) && + typeof v.eof === 'boolean' && (v.eof ? v.bytes.length === 0 : v.bytes.length > 0)) + completion = { kind: 'read', bytes: [...v.bytes], eof: v.eof }; + else if (v.kind === 'write' && entry.event.kind === 'write' && typeof v.count === 'number' && + Number.isInteger(v.count) && v.count > 0 && v.count <= entry.event.bytes.length) + completion = { kind: 'write', count: v.count }; + else + return false; + this.finish(v.id, completion); + return true; + } +} diff --git a/ext/demo-site/dist/io-v2-host-work.js b/ext/demo-site/dist/io-v2-host-work.js @@ -0,0 +1,97 @@ +import { hostKey } from './io-v2-tasks.js'; +// Adapter-side asynchronous work. Starting one callback never waits for it, so +// sibling actions/cancellation can proceed. The provider must settle its promise +// AFTER cancellation cleanup; abort delivery alone is never an acknowledgement. +export class HostWork { + tasks; + active = new Map(); + replies = new Map(); + notification = null; + constructor(tasks) { + this.tasks = tasks; + } + start(scope, operation) { + return this.register(this.tasks.beginHost(scope), operation); + } + // Cleanup is not cancelled by a new stop request. Its result (including a + // failure) must still reach the driver to preserve primary/secondary outcomes. + release(scope, operation) { + return this.register(this.tasks.beginReleaseHost(scope), operation); + } + register(token, operation) { + const controller = new AbortController(); + this.active.set(hostKey(token), { token, controller }); + const complete = (outcome) => { + // Settle first; a cancelled reply is discarded without observing its value. + const disposition = this.tasks.endHost(token); + this.active.delete(hostKey(token)); + if (disposition === 'reply') + this.replies.set(hostKey(token), { token, outcome }); + const notice = this.notification; + this.notification = null; + notice?.resolve(); + }; + try { + Promise.resolve(operation(controller.signal)).then(value => complete({ ok: true, value }), error => complete({ ok: false, error })); + } + catch (error) { + complete({ ok: false, error }); + } + return token; + } + usable(token) { + if (!this.tasks.within(token.scope, token.scope)) + return false; + const state = this.tasks.state(token.scope); + return token.kind === 'release' ? state.phase === 'releasing' : state.phase === 'open' && state.reason === null; + } + // Resource close may interrupt one operation without cancelling its task. + // The resulting provider reply still reaches that body as an ordinary error. + // Never abort a cleanup batch or acknowledge work merely by sending this signal. + cancelBody(token) { + const work = this.active.get(hostKey(token)); + if (token.kind !== 'body' || !work || work.token.run !== token.run || + work.token.scope !== token.scope || work.controller.signal.aborted) + return false; + work.controller.abort(); + return true; + } + cancelStopped() { + for (const token of this.tasks.takeCancellations()) { + const work = this.active.get(hostKey(token)); + if (!work || work.token.scope !== token.scope) + throw new Error('host work ownership mismatch'); + work.controller.abort(); + } + // Completion may have won the race with cancellation. Drop undelivered data + // too; a queued reply cannot revive a stopped/settled body. + for (const [key, reply] of this.replies) + if (!this.usable(reply.token)) + this.replies.delete(key); + } + take() { + for (const [key, reply] of this.replies) { + this.replies.delete(key); + if (this.usable(reply.token)) + return reply; + } + return null; + } + wait() { + if (this.replies.size || !this.active.size) + return Promise.resolve(); + if (!this.notification) { + let resolve; + const promise = new Promise(done => { resolve = done; }); + this.notification = { promise, resolve }; + } + return this.notification.promise; + } + get counts() { return { pending: this.active.size, replies: this.replies.size }; } + // Reply data roots are the embedding's responsibility until taken/discarded. + *values() { + for (const reply of this.replies.values()) + if (this.usable(reply.token) && reply.outcome.ok) + yield reply.outcome.value; + } +} diff --git a/ext/demo-site/dist/io-v2-output.js b/ext/demo-site/dist/io-v2-output.js @@ -0,0 +1,69 @@ +// Presentation only. Byte acceptance and strict source UTF-8 Results are separate. +// No evaluator handles, host callbacks, IO acknowledgements or terminal escapes. +import { decodeUTF8, UTF8Error } from './io-v2-utf8.js'; +function view(bytes, final) { + const tokens = []; + for (let offset = 0; offset < bytes.length;) { + const lead = bytes[offset]; + const width = lead <= 0x7f ? 1 : lead >= 0xc2 && lead <= 0xdf ? 2 : + lead >= 0xe0 && lead <= 0xef ? 3 : lead >= 0xf0 && lead <= 0xf4 ? 4 : 1; + const candidate = bytes.slice(offset, offset + width); + try { + const decoded = decodeUTF8(candidate, false); + if (decoded.carry.length) { + tokens.push({ kind: 'bytes', offset, bytes: candidate, reason: final ? 'incomplete' : 'pending' }); + break; + } + const previous = tokens.at(-1); + if (previous?.kind === 'text') + previous.text += decoded.text; + else + tokens.push({ kind: 'text', offset, text: decoded.text }); + offset += width; + } + catch (error) { + if (!(error instanceof UTF8Error)) + throw error; + // Escape one offending byte; reconsider following bytes independently. + // Thus invalid prefixes cannot swallow subsequent valid text. + tokens.push({ kind: 'bytes', offset, bytes: [lead], reason: 'invalid' }); + offset++; + } + } + return { bytes, hex: bytes.map(b => b.toString(16).padStart(2, '0')).join(' '), tokens }; +} +// Owned by one UI run. The session separately enforces attempted-output budgets; +// this buffer bounds retained accepted bytes, without refunds or history. +export class OutputBuffer { + limit; + streams = { output: [], error: [] }; + size = 0; + ended = false; + constructor(limit = 16384) { + this.limit = limit; + if (!Number.isSafeInteger(limit) || limit < 0 || limit > 16384) + throw new Error('invalid output limit'); + } + append(stream, bytes) { + if (this.ended) + throw new Error('output presentation ended'); + if (stream !== 'output' && stream !== 'error') + throw new Error('invalid output stream'); + if (!Array.isArray(bytes) || bytes.length === 0 || bytes.length > 4096) + throw new Error('invalid output bytes'); + for (const byte of bytes) + if (!Number.isInteger(byte) || byte < 0 || byte > 255) + throw new Error('invalid output bytes'); + if (bytes.length > this.limit - this.size) + throw new Error('output presentation limit'); + this.streams[stream].push(...bytes); + this.size += bytes.length; + } + // End-of-presentation, including after structured cancellation, not closing a + // borrowed stream. Incomplete suffixes become visible bytes, never IO failures. + finish() { this.ended = true; } + snapshot() { + return { done: this.ended, size: this.size, + output: view([...this.streams.output], this.ended), error: view([...this.streams.error], this.ended) }; + } +} diff --git a/ext/demo-site/dist/io-v2-private.js b/ext/demo-site/dist/io-v2-private.js @@ -0,0 +1,49 @@ +// Private native-adapter validation runner, not RuntimeSession IO dispatch. +// Bare computations only; no facility implementation/capability advertisement. +import { Core } from './io-v2-core.js'; +import { canonicalNat } from './io-v2-protocol.js'; +export function constructors(base) { + const natural = (value) => { + const bits = []; + if (value < 0n) + throw new Error('negative natural'); + for (; value; value >>= 1n) + bits.push(value & 1n ? base.stem(base.leaf) : base.leaf); + let out = base.leaf; + for (let i = bits.length - 1; i >= 0; i--) + out = base.fork(bits[i], out); + return out; + }; + return { ...base, natural, text(value) { + let out = base.leaf; + const chars = Array.from(value); + for (let i = chars.length - 1; i >= 0; i--) + out = base.fork(natural(BigInt(chars[i].codePointAt(0))), out); + return out; + } }; +} +export async function runPrivate(tree, action, identity, sweep) { + const engine = new Core(tree, identity, action, [null, null, null], { + request: (core, _scope, request) => () => core.wire.result(false, core.wire.problem('unsupported', `${request.group}/${request.operation}`)), + release: () => { throw new Error('unexpected owned backend in private core probe'); } + }); + sweep(engine.roots()); + for (let turns = 0; !engine.result; turns++) { + const progress = engine.advance(); + sweep(engine.roots()); + if (!progress) { + if (!engine.work.counts.pending) + throw new Error('private interpreter stalled'); + await engine.wait(); + } + if (turns % 32 === 0) + await new Promise(resolve => setTimeout(resolve, 0)); + } + if (engine.hasFault) + throw engine.fault; + const out = engine.result; + // Terminal diagnostics stay opaque, including when the test requests a number. + const value = out.kind === 'completed' ? canonicalNat(tree, out.value, 64)?.toString() ?? null : null; + return { kind: out.kind, value, cleanup: out.cleanup?.items.length ?? 0, + tasks: engine.lifecycle.tasks.counts, resources: engine.lifecycle.resources.counts, host: engine.work.counts }; +} diff --git a/ext/demo-site/dist/io-v2-profile.js b/ext/demo-site/dist/io-v2-profile.js @@ -0,0 +1,47 @@ +// This table is the implemented browser provider, not the native catalogue. +import { canonicalNat, decodeAction, ProtocolError } from './io-v2-protocol.js'; +export const coreNames = ['pure', 'bind', 'fail', 'scope', 'current-scope', 'spawn', 'join', 'discard', 'cancel', 'cancelled']; +const groups = Object.freeze({ + runtime: ['capabilities', 'inspect'], standard: ['input', 'output', 'error'], + stream: ['read', 'write'], resource: ['close'], utf8: ['encode', 'decode'], +}); +export function available(group, revision, operation) { + return revision === 1 && Object.hasOwn(groups, group) && groups[group].includes(operation); +} +export function capabilities(wire, backend) { + const { tree } = wire, text = (s) => tree.text(s), number = (n) => tree.natural(BigInt(n)); + const strings = (xs) => wire.list(xs.map(text)), row = (name, value) => wire.list([text(name), value]); + return wire.list([ + row('schema', number(1)), row('io-version', number(2)), row('backend', text(backend)), row('authority', text('browser-terminal')), + row('core', strings(coreNames)), row('groups', wire.list(Object.entries(groups).map(([name, ops]) => wire.list([text(name), number(1), strings(ops)])))), + row('limits', wire.list(Object.entries({ 'io-actions': 65536, 'chunk-bytes': 4096, 'transfer-handles': 256, 'utf8-encode-scalars': 1024, 'input-line-scalars': 256, 'output-attempted-bytes': 16384 }).map(([key, value]) => row(key, number(value))))), + row('restrictions', strings(['action-boundary-cancellation', 'no-hard-deadlines', 'borrowed-standard-streams', 'standard-stream-leases-per-run', 'utf8-strict-scalars', 'text-line-input', 'byte-preserving-output', 'no-filesystem', 'no-network', 'no-processes'])), + ]); +} +export function inspect(wire, value) { + const action = decodeAction(wire.tree, value), t = wire.tree; + return action.kind === 'core' ? wire.list([t.text('core'), t.text(coreNames[action.code])]) : + wire.list([t.text('facility'), t.text(action.group), t.natural(BigInt(action.revision)), t.text(action.operation), t.text(available(action.group, action.revision, action.operation) ? 'available' : 'unsupported')]); +} +// Authenticate framing before selecting an interpreter. No action is observed. +export function envelope(tree, value) { + const root = tree.observe(value).node; + if (root.kind !== 'fork') + throw new ProtocolError('invalid IO envelope'); + let rest = root.left; + for (const char of 'tricuIO') { + const node = tree.observe(rest).node; + if (node.kind !== 'fork' || canonicalNat(tree, node.left, 7) !== BigInt(char.charCodeAt(0))) + throw new ProtocolError('Missing tricuIO sentinel'); + rest = node.right; + } + if (tree.observe(rest).node.kind !== 'leaf') + throw new ProtocolError('Missing tricuIO sentinel'); + const body = tree.observe(root.right).node; + if (body.kind !== 'fork') + throw new ProtocolError('invalid IO envelope'); + const version = canonicalNat(tree, body.left, 2); + if (version !== 2n) + throw new ProtocolError('Unsupported tricuIO version'); + return { version: 2, action: body.right }; +} diff --git a/ext/demo-site/dist/io-v2-protocol.js b/ext/demo-site/dist/io-v2-protocol.js @@ -0,0 +1,73 @@ +export function canonicalNat(reader, handle, bits) { + if (!Number.isInteger(bits) || bits < 0 || bits > 64) + throw new Error('Invalid natural bit bound'); + let rest = handle, value = 0n, count = 0, last = 0n; + for (;;) { + const node = reader.observe(rest).node; + if (node.kind === 'leaf') + return count === 0 || last === 1n ? value : undefined; + if (count === bits || node.kind !== 'fork') + return undefined; + const bit = reader.observe(node.left).node; + if (bit.kind === 'leaf') + last = 0n; + else if (bit.kind === 'stem' && reader.observe(bit.child).node.kind === 'leaf') + last = 1n; + else + return undefined; + value |= last << BigInt(count++); + rest = node.right; + } +} +function name(reader, handle) { + let rest = handle, value = ''; + for (;;) { + const node = reader.observe(rest).node; + if (node.kind === 'leaf') + return value || undefined; + // Observe only the excess spine cell, never its head or tail. + if (value.length === 64 || node.kind !== 'fork') + return undefined; + const code = canonicalNat(reader, node.left, 7); + if (code === undefined || !(code >= 97n && code <= 122n || value.length > 0 && (code >= 48n && code <= 57n || code === 45n))) + return undefined; + value += String.fromCharCode(Number(code)); + rest = node.right; + } +} +export class ProtocolError extends Error { +} +export function decodeAction(reader, handle) { + const action = reader.observe(handle).node; + if (action.kind !== 'fork') + throw new ProtocolError('invalid computation shape'); + const code = canonicalNat(reader, action.left, 8); + if (code !== undefined && code <= 9n) + return { kind: 'core', code: Number(code), payload: action.right }; + if (code !== 10n) + throw new ProtocolError('unknown computation constructor'); + const group = reader.observe(action.right).node; + if (group.kind !== 'fork') + throw new ProtocolError('invalid facility header'); + const revision = reader.observe(group.right).node; + if (revision.kind !== 'fork') + throw new ProtocolError('invalid facility header'); + const operation = reader.observe(revision.right).node; + if (operation.kind !== 'fork') + throw new ProtocolError('invalid facility header'); + const g = name(reader, group.left); + if (g === undefined) + throw new ProtocolError('invalid facility header'); + const v = canonicalNat(reader, revision.left, 16); + if (v === undefined || v === 0n) + throw new ProtocolError('invalid facility header'); + const op = name(reader, operation.left); + if (op === undefined) + throw new ProtocolError('invalid facility header'); + return { kind: 'request', group: g, revision: Number(v), operation: op, arguments: operation.right }; +} +export function header(reader, handle) { + const action = decodeAction(reader, handle); + return action.kind === 'core' ? { kind: action.kind, code: action.code } + : { kind: action.kind, group: action.group, revision: action.revision, operation: action.operation }; +} diff --git a/ext/demo-site/dist/io-v2-resources.js b/ext/demo-site/dist/io-v2-resources.js @@ -0,0 +1,225 @@ +import { Tasks } from './io-v2-tasks.js'; +// State only. Construct via lifecycle() to bind task settlement to registry guards. +// Single owning event loop; host effects occur AFTER admission/lease/close claims. +class Resources { + tasks; + limit; + next = 4; + resources = new Map(); + admissions = new Map(); + leases = new Map(); + constructor(tasks, standard, limit) { + this.tasks = tasks; + this.limit = limit; + if (!Number.isSafeInteger(limit) || limit < 3) + throw new Error('invalid resource identity limit'); + standard.forEach((backend, index) => this.resources.set(index + 1, { + owner: 0, kind: index === 0 ? 'reader' : 'writer', backend, flags: Object.freeze({ borrowed: true }), + busy: 0, closing: false, releasing: false + })); + } + get counts() { return { resources: this.resources.size, admissions: this.admissions.size, leases: this.leases.size }; } + token(key) { return Object.freeze({ run: this.tasks.identity, key }); } + key(token) { + if (token.run !== this.tasks.identity) + throw new Error('stale-handle'); + return token.key; + } + room(count) { + if (count > this.limit - this.next + 1) + throw new Error('resource-identity-exhausted'); + } + open(scope) { + const state = this.tasks.state(scope); + if (state.phase !== 'open' || state.reason) + throw new Error('closed-scope'); + } + entry(current, token) { + const resource = this.resources.get(this.key(token)); + if (!resource) + throw new Error('stale-handle'); + if (!this.tasks.within(current, resource.owner)) + throw new Error('out-of-scope'); + return resource; + } + lookup(current, token) { return { ...this.entry(current, token) }; } + beginAcquire(current, owner = current) { + this.tasks.checkpoint(current); + this.open(owner); + if (!this.tasks.within(current, owner)) + throw new Error('out-of-scope'); + this.room(1); + const token = this.token(this.next++); + this.admissions.set(token.key, owner); + return token; + } + abortAcquire(token) { + if (!this.admissions.delete(this.key(token))) + throw new Error('invalid-admission'); + } + // An admitted acquisition can finish after owner stop/admission closure. + // Publication failure leaves the admission live: release raw backend, then abort. + publish(admission, kind, backend, flags = {}) { + const key = this.key(admission), owner = this.admissions.get(key); + if (owner === undefined) + throw new Error('invalid-admission'); + if (!kind || Object.values(flags).some(value => typeof value !== 'boolean')) + throw new Error('invalid-argument'); + this.room(1); + const savedFlags = Object.freeze({ ...flags }), token = this.token(this.next); + this.resources.set(token.key, { owner, kind, backend, flags: savedFlags, busy: 0, closing: false, releasing: false }); + this.next++; + this.admissions.delete(key); + return token; + } + // All recoverable validation and preparation precede task admission. No host + // callback/await separates admission from rekeying. JS OOM is fatal, not rollback. + spawn(owner, supervised, keep, tokens) { + this.tasks.checkpoint(owner); + this.open(owner); + if (typeof supervised !== 'boolean' || typeof keep !== 'boolean') + throw new Error('invalid-argument'); + if (tokens.length > 256) + throw new Error('transfer-limit'); + const seen = new Set(); + const entries = tokens.map(token => { + const resource = this.entry(owner, token); + if (seen.has(token.key)) + throw new Error('duplicate-handle'); + seen.add(token.key); + if (resource.owner !== owner) + throw new Error('not-owner'); + if (resource.flags.borrowed) + throw new Error('borrowed-resource'); + if (resource.flags.internal) + throw new Error('internal-resource'); + if (resource.closing) + throw new Error('closed-handle'); + if (resource.busy) + throw new Error('busy'); + return { old: token.key, resource }; + }).sort((a, b) => a.old - b.old); + this.room(tokens.length); + const fresh = new Map(entries.map((entry, i) => [entry.old, this.token(this.next + i)])); + const handles = tokens.map(token => fresh.get(token.key)); + const task = this.tasks.spawn(owner, supervised, keep); + for (const entry of entries) { + this.resources.delete(entry.old); + entry.resource.owner = task.key; + this.resources.set(fresh.get(entry.old).key, entry.resource); + } + this.next += tokens.length; + return { task, handles }; + } + beginLease(current, token, kind, access, internal = false) { + this.tasks.checkpoint(current); + const resource = this.entry(current, token); + if (resource.kind !== kind) + throw new Error('wrong-kind'); + if (resource.closing) + throw new Error('closed-handle'); + if (resource.flags.internal && !internal) + throw new Error('internal-resource'); + if (!['exclusive', 'read', 'write'].includes(access)) + throw new Error('invalid-argument'); + const mask = !resource.flags.duplex || access === 'exclusive' ? 3 : access === 'read' ? 1 : 2; + if (resource.busy & mask) + throw new Error('busy'); + this.room(1); + const lease = this.token(this.next++); + this.leases.set(lease.key, { scope: current, resource: token.key, mask }); + resource.busy |= mask; + return { token: lease, backend: resource.backend }; + } + endLease(token) { + const key = this.key(token), lease = this.leases.get(key); + if (!lease) + throw new Error('invalid-lease'); + this.resources.get(lease.resource).busy &= ~lease.mask; + this.leases.delete(key); + } + claimClose(current, token, settling = false) { + const resource = this.entry(current, token); + if (settling) { + if (resource.owner !== current || this.tasks.state(current).phase !== 'releasing') + throw new Error('invalid-close'); + } + else { + this.tasks.checkpoint(current); + if (resource.flags.internal) + throw new Error('internal-resource'); + if (resource.flags.borrowed) + throw new Error('borrowed-resource'); + } + resource.closing = true; + if (resource.busy || resource.releasing) + return null; + resource.releasing = true; + return { ...resource }; + } + // Once, including failed release. Never retry a physically ambiguous close. + finishClose(token) { + const key = this.key(token), resource = this.resources.get(key); + if (!resource) + throw new Error('stale-handle'); + if (!resource.releasing) + throw new Error('invalid-close'); + this.resources.delete(key); + } + newestOwned(scope) { + let newest = -1; + for (const [key, resource] of this.resources) + if (resource.owner === scope && key > newest) + newest = key; + return newest < 0 ? null : this.token(newest); + } + readyForRelease(scope) { + return ![...this.admissions.values()].includes(scope) && ![...this.leases.values()].some(lease => lease.scope === scope); + } + finishScope(scope) { + if (!this.readyForRelease(scope) || this.newestOwned(scope)) + throw new Error('busy'); + } + // Use inside HostWork.release, after Tasks.beginRelease. Each provider is + // attempted once, sequentially in reverse publication order; borrowed backends + // stay open. Diagnostics construction failure must not skip remaining releases. + async releaseScope(scope, release, problem, fallback) { + if (this.tasks.state(scope).phase !== 'releasing') + throw new Error('invalid-transition'); + const items = []; + let omitted = 0; + for (let token = this.newestOwned(scope); token; token = this.newestOwned(scope)) { + const resource = this.claimClose(scope, token, true); + if (!resource) + throw new Error('busy'); + try { + if (!resource.flags.borrowed) + await release(resource); + } + catch (error) { + if (items.length === 16) + omitted++; + else { + let value = fallback; + try { + value = problem(error); + } + catch { /* retain prebuilt fallback */ } + items.push(value); + } + } + finally { + this.finishClose(token); + } + } + return { items, omitted }; + } +} +export function lifecycle(identity, ownerStopped, cycle, standard, limits = {}) { + let resources; + const tasks = new Tasks(identity, ownerStopped, cycle, limits.taskIdentities ?? Number.MAX_SAFE_INTEGER, { + readyForRelease: scope => resources.readyForRelease(scope), finishScope: scope => resources.finishScope(scope) + }); + resources = new Resources(tasks, standard, limits.resourceIdentities ?? Number.MAX_SAFE_INTEGER); + return { tasks, resources }; +} diff --git a/ext/demo-site/dist/io-v2-session.js b/ext/demo-site/dist/io-v2-session.js @@ -0,0 +1,216 @@ +// Cloneable event session shared by private probes and public native adapters. +import { Core, OperationError } from './io-v2-core.js'; +import { available, capabilities, inspect } from './io-v2-profile.js'; +import { ProtocolError, decodeAction, canonicalNat } from './io-v2-protocol.js'; +import { encodeUTF8, decodeUTF8, UTF8Error } from './io-v2-utf8.js'; +import { Events } from './io-v2-events.js'; +const faults = new Set(['stale-handle', 'out-of-scope', 'closed-handle', 'wrong-kind', 'internal-resource', 'borrowed-resource', 'busy']); +function logical(f) { + try { + return f(); + } + catch (error) { + if (error instanceof Error && faults.has(error.message)) + throw new OperationError(error.message); + throw error; + } +} +function provider(events, backend) { + let outputBudget = 16384; // Attempted bytes across both slots; no replay/refund on uncertainty. + return { + release: () => { throw new Error('unexpected owned terminal resource'); }, + async request(engine, scope, request, signal) { + const { tree, wire } = engine, registry = engine.lifecycle.resources, op = `${request.group}/${request.operation}`; + const bad = (category, progress = 'none') => () => wire.result(false, wire.problem(category, op, '', progress)); + // Unsupported names/revisions leave arguments completely opaque. + if (!available(request.group, request.revision, request.operation)) + return bad('unsupported'); + try { + if (request.group === 'runtime') { + if (request.operation === 'capabilities') { + if (tree.observe(request.arguments).node.kind !== 'leaf') + return bad('invalid-argument'); + return () => wire.result(true, capabilities(wire, backend)); + } + // Classify in a live turn; defer construction so no newly allocated + // handles can be swept between promise resolution and consumption. + try { + decodeAction(tree, request.arguments); + } + catch (error) { + if (error instanceof ProtocolError) + return bad('invalid-argument'); + throw error; + } + return () => wire.result(true, inspect(wire, request.arguments)); + } + if (request.group === 'utf8') { + const readList = (value, limit, bits, scalar) => { + const values = []; + let rest = value; + for (;;) { + const node = tree.observe(rest).node; + if (node.kind === 'leaf') + return values; + // Reject the excess spine cell before observing its head or tail. + if (values.length === limit || node.kind !== 'fork') + throw new OperationError('invalid-argument'); + const number = canonicalNat(tree, node.left, bits); + if (number === undefined || scalar && (number > 0x10ffffn || number >= 0xd800n && number <= 0xdfffn)) + throw new OperationError('invalid-argument'); + values.push(Number(number)); + rest = node.right; + } + }; + if (request.operation === 'encode') { + const scalars = readList(request.arguments, 1024, 21, true); + const bytes = encodeUTF8(String.fromCodePoint(...scalars)); + return () => wire.result(true, wire.list(bytes.map(byte => tree.natural(BigInt(byte))))); + } + const [final, rest] = wire.pair(request.arguments), [bytes, end] = wire.pair(rest); + if (tree.observe(end).node.kind !== 'leaf') + throw new OperationError('invalid-argument'); + const last = canonicalNat(tree, final, 1); + if (last === undefined) + throw new OperationError('invalid-argument'); + const input = readList(bytes, 4096, 8, false); + try { + const decoded = decodeUTF8(input, last === 1n); + return () => wire.result(true, wire.list([tree.text(decoded.text), wire.list(decoded.carry.map(byte => tree.natural(BigInt(byte))))])); + } + catch (error) { + if (!(error instanceof UTF8Error)) + throw error; + return () => wire.result(false, wire.problem('invalid-data', op, error.message)); + } + } + if (request.group === 'standard') { + if (tree.observe(request.arguments).node.kind !== 'leaf') + throw new OperationError('invalid-argument'); + const key = ['input', 'output', 'error'].indexOf(request.operation) + 1; + return () => wire.result(true, wire.marker('io.v2.resource', key)); + } + if (request.group === 'resource') { + const token = wire.token(request.arguments, 'io.v2.resource'); + logical(() => registry.claimClose(scope, token)); + throw new Error('unexpected owned terminal close'); + } + const [handle, argument] = wire.pair(request.arguments), token = wire.token(handle, 'io.v2.resource'); + const read = request.operation === 'read'; + const lease = logical(() => registry.beginLease(scope, token, read ? 'reader' : 'writer', read ? 'read' : 'write')); + try { + const slot = lease.backend; + if (slot.poisoned) + return bad('invalid-state', 'unknown'); + let reply; + if (read) { + const limit = canonicalNat(tree, argument, 16); + if (limit === undefined) + throw new OperationError('invalid-argument'); + if (limit < 1n || limit > 4096n) + return bad('invalid-bound'); + if (slot.eof) + return () => wire.result(true, wire.list([tree.text('eof')])); + reply = await events.read(Number(limit), signal); + if (reply.kind === 'read') { + const received = reply; + slot.eof = received.eof; + return () => wire.result(true, received.eof ? wire.list([tree.text('eof')]) : + tree.fork(tree.text('data'), wire.list(received.bytes.map(byte => tree.natural(BigInt(byte)))))); + } + } + else { + const bytes = []; + let rest = argument; + while (tree.observe(rest).node.kind !== 'leaf') { + if (bytes.length === 4096) + throw new OperationError('invalid-argument'); + const [head, tail] = wire.pair(rest), byte = canonicalNat(tree, head, 8); + if (byte === undefined) + throw new OperationError('invalid-argument'); + bytes.push(Number(byte)); + rest = tail; + } + if (!bytes.length) + return bad('invalid-bound'); + if (bytes.length > outputBudget) + return bad('invalid-bound'); + outputBudget -= bytes.length; + reply = await events.write(slot.name === 'error' ? 'error' : 'output', bytes, signal); + if (reply.kind === 'write') { + const count = reply.count; + return () => wire.result(true, tree.natural(BigInt(count))); + } + } + if (!read) + slot.poisoned = true; + return bad(reply.kind === 'cancelled' ? 'cancelled' : 'io-error', 'unknown'); + } + finally { + registry.endLease(lease.token); + } + } + catch (error) { + if (!(error instanceof OperationError)) + throw error; + return bad(error.message); + } + } + }; +} +export class Session { + tree; + run; + sweep; + options; + events; + core; + busy = false; + finished = false; + constructor(tree, action, run, sweep, options = {}) { + this.tree = tree; + this.run = run; + this.sweep = sweep; + this.options = options; + if (options.steps !== undefined && (!Number.isInteger(options.steps) || options.steps < 1 || options.steps > 128)) + throw new Error('invalid session step bound'); + this.events = new Events(run); + this.core = new Core(tree, BigInt(run), action, ['input', 'output', 'error'].map(name => ({ name, poisoned: false, eof: false })), provider(this.events, options.backend ?? 'browser')); + sweep(this.core.roots()); + } + async turn(command) { + if (this.busy) + throw new Error('session turn already active'); + if (this.finished || command.run !== this.run) + throw new Error('stale session run'); + if (!['poll', 'stop', 'reply'].includes(command.kind)) + throw new Error('invalid session command'); + this.busy = true; + try { + const accepted = command.kind === 'reply' ? this.events.reply(command.reply) : null; + if (command.kind === 'stop') + this.core.stop(this.tree.leaf); + // Never await a terminal operation. Bounded progress batches leave the + // transport available for multiple replies, polls and structured stop. + let progress = false; + for (let turns = 0; turns < (this.options.steps ?? 128) && !this.core.result; turns++) { + progress = this.core.advance(); + this.sweep(this.core.roots()); + await Promise.resolve(); + if (!progress) + break; + } + const outcome = this.core.result; + if (outcome) + this.finished = true; + return { run: this.run, accepted, waiting: !progress && this.events.counts.pending > 0 && this.core.work.counts.pending === this.events.counts.pending && this.core.work.counts.replies === 0, done: !!outcome, events: this.events.take(), + outcome: outcome ? { kind: outcome.kind, value: outcome.kind === 'completed' && this.options.summarize !== false ? canonicalNat(this.tree, outcome.value, 64)?.toString() ?? null : null, + cleanup: outcome.cleanup?.items.length ?? 0, fault: this.core.hasFault } : null, + counts: { tasks: this.core.lifecycle.tasks.counts, resources: this.core.lifecycle.resources.counts, + work: this.core.work.counts, events: this.events.counts } }; + } + finally { + this.busy = false; + } + } +} diff --git a/ext/demo-site/dist/io-v2-tasks.js b/ext/demo-site/dist/io-v2-tasks.js @@ -0,0 +1,325 @@ +export const hostKey = (token) => `${token.kind}:${token.key}`; +const empty = () => ({ items: [], omitted: 0 }); +const primary = (outcome) => ({ kind: outcome.kind, value: outcome.value }); +function append(left, right) { + if (!Number.isSafeInteger(right.omitted) || right.omitted < 0) + throw new Error('invalid diagnostics'); + const available = 16 - left.items.length; + return { items: [...left.items, ...right.items.slice(0, available)], + omitted: Math.min(Number.MAX_SAFE_INTEGER, left.omitted + right.omitted + Math.max(0, right.items.length - available)) }; +} +const capture = (outcome) => { + if (!['completed', 'failed', 'cancelled'].includes(outcome.kind)) + throw new Error('invalid outcome'); + return { ...primary(outcome), cleanup: append(empty(), outcome.cleanup ?? empty()) }; +}; +export class Tasks { + identity; + ownerStopped; + cycle; + identityLimit; + resources; + groups = new Map(); + tasks = new Map(); + hosts = new Map(); + nextScope = 1; + nextHost = 1; + constructor(identity, ownerStopped, cycle, identityLimit = Number.MAX_SAFE_INTEGER, resources) { + this.identity = identity; + this.ownerStopped = ownerStopped; + this.cycle = cycle; + this.identityLimit = identityLimit; + this.resources = resources; + if (typeof identity !== 'bigint' || identity <= 0n || identity > 0xffffffffffffffffn || + !Number.isSafeInteger(identityLimit) || identityLimit < 1) + throw new Error('invalid run identity or limit'); + this.groups.set(0, this.group(null, 0, 'root', false)); + } + group(parent, task, kind, propagate) { + return { parent, task, kind, propagate, phase: 'open', reason: null, body: null, + cleanup: empty(), lexical: null, wait: null, hostCalls: new Set(), releaseStarted: false }; + } + get(scope) { + const group = this.groups.get(scope); + if (!group) + throw new Error('closed-scope'); + return group; + } + // Read-only copies: embeddings cannot mutate lifecycle state or diagnostic arrays. + state(scope) { + const g = this.get(scope); + return { parent: g.parent, task: g.task, kind: g.kind, phase: g.phase, + reason: g.reason && primary(g.reason), lexical: g.lexical, wait: g.wait, hostCalls: g.hostCalls.size }; + } + get counts() { return { scopes: this.groups.size, tasks: this.tasks.size, hosts: this.hosts.size }; } + scopeIds() { return [...this.groups.keys()]; } + within(current, owner) { + for (let scope = current; scope !== null;) { + const g = this.groups.get(scope); + if (!g) + return false; + if (scope === owner) + return true; + scope = g.parent; + } + return false; + } + checkpoint(scope) { + const g = this.get(scope); + if (g.reason) + throw new Error('stopped'); + if (g.phase !== 'open' || g.lexical !== null || g.wait !== null) + throw new Error('invalid-transition'); + } + scopeId() { + if (this.nextScope > this.identityLimit) + throw new Error('scope-identity-exhausted'); + return this.nextScope++; + } + nested(parent) { + this.checkpoint(parent); + const id = this.scopeId(), child = this.group(parent, this.get(parent).task, 'lexical', true); + this.groups.set(id, child); + this.get(parent).lexical = id; + return id; + } + spawn(owner, supervised, keep) { + this.checkpoint(owner); + if (typeof supervised !== 'boolean' || typeof keep !== 'boolean') + throw new Error('invalid-argument'); + const id = this.scopeId(), child = this.group(owner, id, 'spawned', !supervised); + this.groups.set(id, child); + this.tasks.set(id, { owner, supervised, ticket: keep, waiter: null, outcome: null }); + return { run: this.identity, key: id }; + } + recordStop(scope, reason) { + const g = this.groups.get(scope); + if (!g || g.reason) + return false; + g.reason = primary(reason); + return true; + } + stop(scope, reason) { + if (reason.kind !== 'failed' && reason.kind !== 'cancelled') + throw new Error('invalid stop'); + if (!this.recordStop(scope, reason)) + return; + // Fixed point propagates first failure upward and owner cancellation downward. + for (let changed = true; changed;) { + changed = false; + for (const [id, g] of this.groups) + if (g.parent !== null) { + if (g.reason?.kind === 'failed' && g.propagate) + changed = this.recordStop(g.parent, g.reason) || changed; + if (this.get(g.parent).reason) + changed = this.recordStop(id, { kind: 'cancelled', value: this.ownerStopped }) || changed; + } + } + } + checked(current, token) { + if (token.run !== this.identity) + throw new Error('stale-task'); + const task = this.tasks.get(token.key); + if (!task) + throw new Error('stale-task'); + if (!this.within(current, task.owner)) + throw new Error('out-of-scope'); + return task; + } + // Edges are task identities, not lexical scope IDs. Normal scope exit adds + // implicit edges to unfinished children; explicit join cycle checks include them. + reaches(start, wanted) { + const pending = [start], seen = new Set(); + while (pending.length) { + const task = pending.pop(); + if (task === wanted) + return true; + if (seen.has(task)) + continue; + seen.add(task); + for (const [id, g] of this.groups) + if (g.task === task) { + if (g.wait !== null) + pending.push(g.wait); + if (g.phase === 'joining') + for (const [child, t] of this.tasks) + if (t.owner === id && t.outcome === null) + pending.push(child); + } + } + return false; + } + claimJoin(current, token) { + this.checkpoint(current); + const task = this.checked(current, token); + if (!task.ticket) + throw new Error('stale-task'); + if (this.within(current, token.key) || this.reaches(token.key, this.get(current).task)) + throw new Error('join-cycle'); + task.ticket = false; + task.waiter = current; + this.get(current).wait = token.key; + } + pollJoin(current) { + const g = this.get(current); + if (g.wait === null) + throw new Error('invalid-transition'); + const task = this.tasks.get(g.wait); + if (!task.outcome) + return null; + const outcome = capture(task.outcome); + this.tasks.delete(g.wait); + g.wait = null; + return outcome; + } + abandonJoin(current) { + const g = this.groups.get(current); + if (!g || g.wait === null) + return; + const task = this.tasks.get(g.wait); + task.waiter = null; + if (task.outcome) + this.tasks.delete(g.wait); + g.wait = null; + } + discard(current, token) { + this.checkpoint(current); + const task = this.checked(current, token); + if (!task.ticket) + throw new Error('stale-task'); + task.ticket = false; + if (task.outcome) + this.tasks.delete(token.key); + } + cancel(current, token, reason) { + this.checkpoint(current); + const task = this.checked(current, token); + if (task.outcome) + throw new Error('stale-task'); + this.stop(token.key, { kind: 'cancelled', value: reason }); + } + bodyDone(scope, outcome) { + const g = this.get(scope), captured = capture(outcome); + if (g.phase !== 'open' || g.lexical !== null) + throw new Error('invalid-transition'); + this.abandonJoin(scope); + if (captured.kind !== 'completed') + this.stop(scope, captured); + g.body = captured; + for (const [id, t] of this.tasks) + if (t.owner === scope && !t.outcome && this.reaches(id, g.task)) { + this.stop(scope, { kind: 'failed', value: this.cycle }); + break; + } + g.phase = 'joining'; + } + beginHost(scope) { + this.checkpoint(scope); + return this.registerHost(scope, 'body'); + } + beginReleaseHost(scope) { + const group = this.get(scope); + if (group.phase !== 'releasing' || group.releaseStarted) + throw new Error('invalid-transition'); + // One cleanup batch per scope uses its already-reserved identity, in a + // separate namespace. Body-call identity exhaustion cannot prevent cleanup. + const token = this.registerHost(scope, 'release'); + group.releaseStarted = true; + return token; + } + registerHost(scope, kind) { + if (kind === 'body' && this.nextHost > this.identityLimit) + throw new Error('host-identity-exhausted'); + const token = Object.freeze({ run: this.identity, key: kind === 'body' ? this.nextHost++ : scope, scope, kind }); + const key = hostKey(token); + this.hosts.set(key, { token, cancelling: false }); + this.get(scope).hostCalls.add(key); + return token; + } + // Delivery is separate from acknowledgement. Issuing AbortController.abort() + // cannot settle a task whose host callback/lease is still outstanding. + takeCancellations() { + const pending = []; + for (const call of this.hosts.values()) + if (call.token.kind === 'body' && !call.cancelling && this.get(call.token.scope).reason) { + call.cancelling = true; + pending.push(call.token); + } + return pending; + } + endHost(token) { + const key = hostKey(token), call = this.hosts.get(key); + if (token.run !== this.identity || !call || call.token.scope !== token.scope || call.token.kind !== token.kind) + return 'stale'; + const g = this.get(token.scope); + this.hosts.delete(key); + g.hostCalls.delete(key); + return token.kind === 'release' || !g.reason && !g.body ? 'reply' : 'ignored'; + } + beginRelease(scope) { + const g = this.get(scope); + if (g.phase !== 'joining') + throw new Error('invalid-transition'); + if (g.hostCalls.size || [...this.tasks.values()].some(t => t.owner === scope && !t.outcome) || + this.resources && !this.resources.readyForRelease(scope)) + return false; + g.phase = 'releasing'; + return true; + } + // Call only after the embedding has physically released this scope's resources. + settle(scope, ownCleanup = empty()) { + const g = this.get(scope); + if (g.phase !== 'releasing' || !g.body || g.hostCalls.size) + throw new Error('invalid-transition'); + let final = primary(g.reason ?? g.body); + let errors = append(append(g.body.cleanup ?? empty(), g.cleanup), ownCleanup); + if (final.kind === 'completed' && errors.items.length) { + final = { kind: 'failed', value: errors.items[0] }; + errors = { items: errors.items.slice(1), omitted: errors.omitted }; + } + this.resources?.finishScope(scope); + final = { ...final, cleanup: errors }; + if (final.kind === 'failed') + this.stop(scope, final); + for (const [id, task] of this.tasks) + if (task.owner === scope) + this.tasks.delete(id); + if (g.parent !== null) { + const parent = this.get(g.parent); + if (g.kind === 'lexical') { + parent.cleanup = append(parent.cleanup, errors); + parent.lexical = null; + } + else { + const task = this.tasks.get(scope); + if (!task.supervised) + parent.cleanup = append(parent.cleanup, errors); + task.outcome = final; + if (!task.ticket && task.waiter === null) + this.tasks.delete(scope); + } + } + this.groups.delete(scope); + return g.kind === 'lexical' ? primary(final) : capture(final); + } + // Evaluator roots, in addition to embedding-owned bodies, continuations and + // pending host arguments. No task/result history remains after consumption. + *payloads() { + yield this.ownerStopped; + yield this.cycle; + for (const g of this.groups.values()) { + if (g.reason) + yield g.reason.value; + if (g.body) { + yield g.body.value; + yield* g.body.cleanup?.items ?? []; + } + yield* g.cleanup.items; + } + for (const task of this.tasks.values()) + if (task.outcome) { + yield task.outcome.value; + yield* task.outcome.cleanup?.items ?? []; + } + } +} diff --git a/ext/demo-site/dist/io-v2-terminal-view.js b/ext/demo-site/dist/io-v2-terminal-view.js @@ -0,0 +1,125 @@ +import { Terminal } from './io-v2-terminal.js'; +// One identity-bound UI per run. Callbacks enqueue cloneable replies/stop requests; +// they must not synchronously re-enter this widget or throw. +export function terminalView(parent, run, reply, stop) { + const terminal = new Terminal(run), doc = parent.ownerDocument; + const root = doc.createElement('section'), status = doc.createElement('p'); + const outputs = ['output', 'error'].map(name => { + const section = doc.createElement('section'), title = doc.createElement('h4'); + title.textContent = name === 'output' ? 'Standard output' : 'Standard error'; + const text = doc.createElement('pre'), details = doc.createElement('details'), summary = doc.createElement('summary'), hex = doc.createElement('pre'); + summary.textContent = 'Exact bytes (hexadecimal)'; + details.append(summary, hex); + section.append(title, text, details); + root.append(section); + text.dataset.stream = name; + hex.dataset.hex = name; + return { text, hex }; + }); + let form = doc.createElement('form'), clearInput = () => { }, waitingId; + const stopping = doc.createElement('button'); + stopping.textContent = 'Stop (cancel and wait for cleanup)'; + stopping.type = 'button'; + root.append(form, stopping, status); + parent.append(root); + let stopped = false, finished = false; + const onStop = () => { if (stopped || finished) + return; stopped = true; stopping.disabled = true; status.textContent = 'Cancelling; waiting for cleanup…'; stop(); }; + stopping.addEventListener('click', onStop); + function inputView() { + const wait = terminal.waiting; + if (wait?.id === waitingId) { + form.hidden = !wait; + return; + } + clearInput(); + const fresh = doc.createElement('form'); + form.replaceWith(fresh); + form = fresh; + form.hidden = !wait; + waitingId = wait?.id; + if (!wait) + return; + const label = doc.createElement('label'), input = doc.createElement('input'), send = doc.createElement('button'), eof = doc.createElement('button'); + label.textContent = 'One line, at most 256 Unicode scalars (Send appends LF)'; + input.maxLength = 512; + label.append(input); + send.textContent = 'Send'; + send.type = 'submit'; + eof.textContent = 'End input (EOF)'; + eof.type = 'button'; + form.append(label, send, eof); + function submit(end) { + const result = end ? terminal.endInput(wait.run, wait.id) : terminal.line(wait.run, wait.id, input.value); + if (!result) { + status.textContent = 'Enter at most 256 Unicode scalars without CR or LF.'; + return; + } + render(); + reply(result); + } + const onSubmit = (event) => { event.preventDefault(); submit(false); }, onEOF = () => submit(true); + form.addEventListener('submit', onSubmit); + eof.addEventListener('click', onEOF); + const owned = form; + clearInput = () => { owned.removeEventListener('submit', onSubmit); eof.removeEventListener('click', onEOF); input.value = ''; }; + } + function render() { + const snapshot = terminal.output.snapshot(); + [snapshot.output, snapshot.error].forEach((view, index) => { + const target = outputs[index]; + target.text.replaceChildren(); + target.hex.textContent = view.hex; + for (const token of view.tokens) { + if (token.kind === 'text') + target.text.append(doc.createTextNode(token.text)); + else { + const span = doc.createElement('mark'); + span.dataset.byteReason = token.reason; + span.title = `${token.reason} UTF-8 bytes at stream offset ${token.offset}; see exact hex view`; + span.textContent = token.bytes.map(b => `\\x${b.toString(16).padStart(2, '0')}`).join(''); + target.text.append(span); + } + } + }); + inputView(); + if (!stopped) + status.textContent = terminal.waiting ? 'Waiting for a line or explicit EOF.' : 'Running…'; + } + render(); + return { + root, + handle(event) { + if (finished) + throw new Error('terminal view retired'); + const result = terminal.handle(event); + // Physically hide/clear an abandoned input BEFORE acknowledging cancel. + render(); + if (result) + reply(result); + }, + finish() { + terminal.finish(); + finished = true; + render(); + clearInput(); + stopping.removeEventListener('click', onStop); + form.remove(); + stopping.disabled = true; + status.textContent = 'Settled.'; + }, + // Fatal runtime loss/navigation is NOT a physical cleanup acknowledgement. + // Retire DOM callbacks without fabricating a settled interpreter outcome. + abandon() { + if (finished) + return; + finished = true; + clearInput(); + form.remove(); + stopping.removeEventListener('click', onStop); + stopping.disabled = true; + status.textContent = 'Runtime lost; cleanup was not acknowledged.'; + }, + snapshot: () => terminal.output.snapshot() + }; +} diff --git a/ext/demo-site/dist/io-v2-terminal.js b/ext/demo-site/dist/io-v2-terminal.js @@ -0,0 +1,77 @@ +import { OutputBuffer } from './io-v2-output.js'; +export class Terminal { + run; + output = new OutputBuffer(); + nextAfter = 0; + reader; + buffered = []; + eof = false; + ended = false; + constructor(run) { + this.run = run; + if (!/^[1-9][0-9]*$/.test(run) || run.length > 20 || BigInt(run) > 0xffffffffffffffffn) + throw new Error('invalid terminal run'); + } + get waiting() { + return this.reader ? { run: this.run, id: this.reader.id } : undefined; + } + read() { + const reader = this.reader; + if (!reader || !this.buffered.length && !this.eof) + return; + const bytes = this.buffered.splice(0, reader.limit); + this.reader = undefined; + return { run: this.run, id: reader.id, kind: 'read', bytes, eof: bytes.length === 0 }; + } + handle(event) { + if (this.ended || event.run !== this.run || !Number.isSafeInteger(event.id) || event.id < 1) + throw new Error('invalid terminal event'); + if (event.kind === 'cancel') { + if (event.id > this.nextAfter || !['read', 'write'].includes(event.operation)) + throw new Error('invalid terminal cancellation'); + // Completed synchronous writes/reads already have a normal acknowledgement + // queued. A racing abort does not erase it or manufacture a second reply. + if (this.reader?.id !== event.id) + return; + if (event.operation !== 'read') + throw new Error('invalid terminal cancellation'); + this.reader = undefined; + return { run: this.run, id: event.id, kind: 'cancelled' }; + } + if (event.id <= this.nextAfter) + throw new Error('stale terminal event'); + if (event.kind === 'read') { + if (this.reader || !Number.isInteger(event.limit) || event.limit < 1 || event.limit > 4096) + throw new Error('invalid terminal read'); + this.reader = { ...event }; + } + else if (event.kind === 'write') + this.output.append(event.stream, event.bytes); + else + throw new Error('invalid terminal operation'); + this.nextAfter = event.id; + return event.kind === 'read' ? this.read() : { run: this.run, id: event.id, kind: 'write', count: event.bytes.length }; + } + // Explicit one-line UI: no CR/LF in the submitted text, append ONE LF; an + // empty line is data, never EOF. Scalars, not UTF-16 code units, are bounded. + line(run, id, value) { + if (run !== this.run || this.reader?.id !== id || typeof value !== 'string' || value.length > 512 || + /[\r\n\uD800-\uDFFF]/u.test(value) || Array.from(value).length > 256) + return; + this.buffered = [...new TextEncoder().encode(value), 10]; + return this.read(); + } + endInput(run, id) { + if (run !== this.run || this.reader?.id !== id) + return; + this.eof = true; + return this.read(); + } + finish() { + if (this.reader) + throw new Error('terminal input still pending'); + this.ended = true; + this.buffered = []; + this.output.finish(); + } +} diff --git a/ext/demo-site/dist/io-v2-utf8.js b/ext/demo-site/dist/io-v2-utf8.js @@ -0,0 +1,65 @@ +// Strict stateless UTF-8. No BOM stripping, replacement or normalization. +// Source carries the copied <=3-byte suffix between requests. +export class UTF8Error extends Error { +} +export function decodeUTF8(bytes, final) { + if (bytes.length > 4096 || bytes.some(b => !Number.isInteger(b) || b < 0 || b > 255)) + throw new UTF8Error('invalid byte chunk'); + const chars = []; + const bad = (offset, why) => { throw new UTF8Error(`${why} at byte ${offset}`); }; + for (let offset = 0; offset < bytes.length;) { + const lead = bytes[offset]; + if (lead <= 0x7f) { + chars.push(String.fromCodePoint(lead)); + offset++; + continue; + } + let count, low = 0x80, high = 0xbf, code; + if (lead >= 0xc2 && lead <= 0xdf) { + count = 2; + code = lead & 0x1f; + } + else if (lead >= 0xe0 && lead <= 0xef) { + count = 3; + code = lead & 0x0f; + if (lead === 0xe0) + low = 0xa0; + if (lead === 0xed) + high = 0x9f; + } + else if (lead >= 0xf0 && lead <= 0xf4) { + count = 4; + code = lead & 7; + if (lead === 0xf0) + low = 0x90; + if (lead === 0xf4) + high = 0x8f; + } + else + return bad(offset, 'invalid UTF-8 leading byte'); + const available = Math.min(count - 1, bytes.length - offset - 1); + for (let i = 1; i <= available; i++) { + const b = bytes[offset + i]; + if (b < (i === 1 ? low : 0x80) || b > (i === 1 ? high : 0xbf)) + return bad(offset, 'invalid UTF-8 sequence'); + code = code * 64 + (b & 63); + } + if (available < count - 1) { + if (final) + return bad(offset, 'incomplete UTF-8 sequence'); + return { text: chars.join(''), carry: bytes.slice(offset) }; + } + chars.push(String.fromCodePoint(code)); + offset += count; + } + return { text: chars.join(''), carry: [] }; +} +export function encodeUTF8(text) { + let count = 0; + for (const char of text) { + const scalar = char.codePointAt(0); + if (++count > 1024 || scalar >= 0xd800 && scalar <= 0xdfff) + throw new UTF8Error('invalid scalar chunk'); + } + return [...new TextEncoder().encode(text)]; +} diff --git a/ext/demo-site/dist/io.js b/ext/demo-site/dist/io.js @@ -1,63 +1,3 @@ -import { readNatural, readText, line } from './codecs.js'; -// collectActions requires exclusive ownership: unrelated caller handles may be -// invalidated unless included in retainedRoots. Normal callers retain the -// non-collecting API by default. Snapshot extra roots before the first action. -export async function runIO(graph, handle, host, collectActions = false, retainedRoots = []) { - const retained = [...retainedRoots]; - const [sentinel, body] = graph.pair(handle, 'IO envelope'); - if (readText(graph, sentinel, 7) !== 'tricuIO') - throw new Error('Missing tricuIO sentinel'); - const [version, initial] = graph.pair(body, 'IO version/action'); - if (readNatural(graph, version) !== 1n) - throw new Error('Unsupported tricuIO version'); - const frames = []; - let action = initial, output = 0; - const names = new Map([[0n, 'pure'], [1n, 'bind'], [2n, 'abortIO'], [10n, 'putStr'], [11n, 'getLine']]); - for (let step = 1; step <= 128; step++) { - if (collectActions) - graph.collect([action, ...frames, ...retained]); - const [tag, payload] = graph.pair(action, 'action'); - const code = readNatural(graph, tag), name = names.get(code); - if (!name) - throw new Error(`Unsupported browser IO action ${code}`); - // Don't inspect arbitrary pure/abort payloads merely to build a trace. - const detail = code === 10n ? readText(graph, payload) : `node #${payload}`; - await host.before({ step, name, detail, depth: frames.length }); - if (code === 1n) { - const [left, continuation] = graph.pair(payload, 'bind'); - frames.push(continuation); - action = left; - host.after({ step, name, value: 'continuation handle saved' }); - continue; - } - if (code === 2n) - throw new Error(`Execution aborted (diagnostic node #${payload}; no payload forcing).`); - let value; - if (code === 0n) - value = payload; - else if (code === 10n) { - output += Array.from(detail).length; - if (output > 16_384) - throw new Error('Terminal output limit exceeded'); - host.write(detail); - value = graph.leaf; - } - else - value = line(graph, await host.read()); - host.after({ step, name, value: `node #${value}` }); - if (!frames.length) - return value; - host.running(); - action = graph.app(frames.pop(), value); - } - throw new Error('IO action limit exceeded'); -} -// Ignore malformed/wrong-kind/stale resumes without consuming the waiting token. -// The page validates interactively; the Worker must also retain a retryable wait. -export function resumeValue(kind, value) { - if (kind === 'step') - return value === undefined ? '' : undefined; - if (typeof value !== 'string' || value.length > 512 || /[\r\n\uD800-\uDFFF]/u.test(value) || Array.from(value).length > 256) - return undefined; - return value; -} +// A scheduler-step acknowledgement carries no terminal text. Malformed replies +// leave the waiting token live; terminal input uses run-bound v2 byte replies. +export function validStepResume(value) { return value === undefined; } diff --git a/ext/demo-site/dist/wasm-worker.js b/ext/demo-site/dist/wasm-worker.js @@ -1,7 +1,7 @@ import { autoInterpretation } from './auto-interpretation.js'; import { importPlan } from './import-plan.js'; import { localModuleInputs, validateLocalModule } from './local-modules.js'; -import { resumeValue } from './io.js'; +import { validStepResume } from './io.js'; import { createRuntimeSession } from './runtime-session.js'; const selectedBackend = new URL(globalThis.location.href).searchParams.get('backend'); import { publishedBundle } from './published-bundle.js'; @@ -15,25 +15,61 @@ const running = () => send({ type: 'state', state: 'running' }); let session, stored; let busy = false, requestId = 0; let pending; -function request(kind) { +function requestStep() { if (pending) throw new Error('Driver already waiting'); + if (requestId >= Number.MAX_SAFE_INTEGER) + throw new Error('Driver request identity exhausted'); const id = ++requestId; - return new Promise((resolve, reject) => { pending = { id, resolve, reject, kind }; send({ type: 'state', state: kind, id }); }); + return new Promise((resolve, reject) => { pending = { id, resolve, reject }; send({ type: 'state', state: 'step', id }); }); +} +let v2Command, v2Run, stopRequested = false; +function host(stepping) { + stopRequested = false; + return { + selected(version) { send({ type: 'io-version', version }); }, + async before(event) { send({ type: 'trace', phase: 'before', event }); if (stepping) + await requestStep(); running(); }, + v2: { + stepping, + open(run, command) { v2Run = run; v2Command = command; send({ type: 'v2-open', run }); if (stopRequested) + command({ run, kind: 'stop' }); }, + event(event) { send({ type: 'v2-event', event }); }, + close(run, settled) { v2Command = undefined; v2Run = undefined; pending = undefined; send({ type: 'v2-close', run, settled }); }, + }, + }; } scope.onmessage = ({ data }) => { if (!data || typeof data !== 'object' || Array.isArray(data)) return; const m = data; + if (m['type'] === 'io-stop') { + if (!busy) + return; + stopRequested = true; + if (v2Run) + v2Command?.({ run: v2Run, kind: 'stop' }); + if (pending) { + const waiter = pending; + pending = undefined; + waiter.resolve(); + } + return; + } + if (m['type'] === 'v2-command') { + const command = m['command']; + if (command && typeof command === 'object') + v2Command?.(command); + return; + } if (m['type'] === 'resume') { if (!pending || m['id'] !== pending.id) return; - const value = resumeValue(pending.kind, m['value']); - if (value === undefined) + if (!validStepResume(m['value'])) return; const waiter = pending; pending = undefined; - waiter.resolve(value); + waiter.resolve(); return; } if (busy) @@ -119,12 +155,7 @@ scope.onmessage = ({ data }) => { throw new Error('Invalid IO definition'); const output = outputType(m['output']); running(); - const result = await session.runDefinitionIO(m['name'], output, { - async before(event) { send({ type: 'trace', phase: 'before', event }); if (m['step'] === true) - await request('step'); running(); }, - after(event) { send({ type: 'trace', phase: 'after', event }); }, write(text) { send({ type: 'output', text }); }, - read() { return request('input'); }, running, - }); + const result = await session.runDefinitionIO(m['name'], output, host(m['step'] === true)); send({ type: 'done', result, stats: session.stats() }); } else if (m['type'] === 'run') { @@ -136,15 +167,10 @@ scope.onmessage = ({ data }) => { throw new Error('Unsupported execution mode'); const args = literals(m['arguments']), output = outputType(m['output']); running(); - const host = { - async before(event) { send({ type: 'trace', phase: 'before', event }); if (m['step'] === true) - await request('step'); running(); }, - after(event) { send({ type: 'trace', phase: 'after', event }); }, write(text) { send({ type: 'output', text }); }, - read() { return request('input'); }, running, - }; + const ioHost = host(m['step'] === true); const result = m['expression'] === undefined - ? await session.run(m['export'], args, output, m['mode'], host) - : await session.runExpression(sourceText(m['expression']), output, m['mode'], host); + ? await session.run(m['export'], args, output, m['mode'], ioHost) + : await session.runExpression(sourceText(m['expression']), output, m['mode'], ioHost); send({ type: 'done', result, stats: session.stats() }); } else if (m['type'] === 'export' && stored) { diff --git a/ext/demo-site/dist/zig-helpers.js b/ext/demo-site/dist/zig-helpers.js @@ -8,3 +8,8 @@ export * as expressions from './expression.js'; export * as treeResults from './tree-result.js'; export * as modules from './module-stage.js'; export * as moduleBundle from './module-bundle.js'; +export * as ioV2Protocol from './io-v2-protocol.js'; +export * as ioV2Private from './io-v2-private.js'; +export * as ioV2Session from './io-v2-session.js'; +export * as ioV2Profile from './io-v2-profile.js'; +export * as ioV2Driver from './io-v2-driver.js'; diff --git a/ext/demo-site/environments.json b/ext/demo-site/environments.json @@ -1,6 +1,6 @@ { "schema": 1, - "compiler": "91f78730b3623d019e33f96e9e2712d61b04837b21ef3eccec3071ac9260944d", + "compiler": "890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa", "profiles": { "empty": null, "practical": { diff --git a/ext/demo-site/lexer-demo.json b/ext/demo-site/lexer-demo.json @@ -3,7 +3,7 @@ "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export scanWord\n!export scanWhitespace\n\n-- Positioned prefix scans, not whole-input parsers. The remainder begins\n-- at the first nonmatching character. Positions count source characters,\n-- not UTF-8 bytes or display columns: only LF advances the line.\n-- Edit a predicate below and evaluate scanWord \"hello,world\" to compare.\n-- scanWhitespace accepts space, LF and tab, but deliberately not CR.\nreport reply = let\n payload = parserReplyPayload reply\n end = snd payload\n in [\n [\"matched\" 0 (fst payload)]\n [\"remaining\" 0 (parserReplyRest reply)]\n [\"line\" 1 (lexerPositionLine end)]\n [\"column\" 1 (lexerPositionColumn end)]\n [\"offset\" 1 (lexerPositionOffset end)]\n [\"consumed\" 2 (parserReplyConsumed reply)]]\n\nscan predicate text = lexerScanWhile text lexerStart [] predicate (state character : pair character state)\n |> lexerMapScan reverse\n |> report\nscanWord text = scan (character : N.inRange character 97 122) text\nscanWhitespace text = scan (character : or? (equal? character 32)\n (or? (equal? character 9) (equal? character 10))) text\n", "hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2", "mode": "pure", - "compilerSha256": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e", + "compilerSha256": "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4", "sourceSha256": "2a441d6d881acd6e34cd0caa8179ee1afefbce68c2e5ff1e44fb1768f69eeb3c", "haskellCases": [ { diff --git a/ext/demo-site/packages.json b/ext/demo-site/packages.json @@ -1887,6 +1887,2246 @@ "manifest": "ed99e054e9c862668e165f458d3afcdc56279f766eea7a3886d440aea7252b63", "title": "Located scans: matched prefix, remainder and character positions", "demo": "lexer" + }, + { + "name": "tricu-base", + "version": "0.4.0", + "hash": "f9f0ab0e44f6ae39479d74c6c66a2bdaba3f40bd26781c3ec95300c2ee3c07f3", + "manifest": "fa9d4df0f82cac2754fdd3e92009cb48064856ff4cc5b18f092309aef7ff5ed4", + "title": "Foundation: lists, Results and decimal formatting" + }, + { + "name": "tricu-file-io", + "version": "0.4.0", + "hash": "401961d889131c735bf48b415f53d3b85dfab0136496299f60cf1dcb0b264f14", + "manifest": "6a22866c2cff143599cb854b2cd3bb3c8948e40835425ed62d942d88bed5c5aa", + "title": "File IO and hashing actions" + }, + { + "name": "arboricx-inspection", + "version": "0.4.0", + "hash": "f571888c5aa3f7a91e9d5d6c45c674851ea8e3598514ad8d535fd3e9056d0314", + "manifest": "fe9b420e1e776fa1dda7b77857e322c0509ed308bb18441b76a3b03023f93468", + "title": "Arboricx inspection: validate without execution" + }, + { + "name": "tricu-compiler", + "version": "0.4.0", + "hash": "15c3b172ba505e4904ced400deca1e6fdadeac1734b732191291c92ff8d245f5", + "manifest": "bfa5bd549d0e721d7a776b481e42416dd9ad2ec2746fba7ab278a147311e15e7", + "title": "Compiler library (stable browser source-unit API; native helpers)" + }, + { + "name": "tricu-source-builder", + "version": "0.4.0", + "hash": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e", + "manifest": "a0820abd99bc31fb9aa0e463c61b37d0a181d44e540a03a9016e3b00c751b25c", + "title": "Stable native application compiler (modules, imports and publication)" + }, + { + "name": "arboricx-server", + "version": "0.4.0", + "hash": "3ff98271f9a512c04a8f200aa7488929edad50e1c7aa3864b243145f03cd3c9a", + "manifest": "800fd0557eb020bf4395e5b73d9a819d9c730da60f83351261b4cd8c07610ca6", + "title": "Arboricx registry server (native sockets and filesystem)" + }, + { + "name": "tricu-http", + "version": "0.4.0", + "hash": "43799eb78baaff0314d6014ef7692661759901d4a29608c9a169176beb13b231", + "manifest": "280dea7b29a3dc280a5cc5fda0c096a7698b36226d22c3e51ca93d39336dfa02", + "title": "HTTP parsing, responses and native socket handlers" + }, + { + "name": "tricu-intensional-contracts", + "version": "0.4.0", + "hash": "dd39639f1d0a1a14f7856b8d0db682adf36d1e7501ea1889bd185ea500989380", + "manifest": "7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1", + "title": "Intensional contracts: tree shapes and numeric structure" + }, + { + "name": "tricu-graphs", + "version": "0.4.0", + "hash": "b588b07ac71f87ab85059ea0788ee2e11d3ee77ad319310e5a21f844c98dabf4", + "manifest": "7b798a3afae6dce07b718bed855c7209ec3b898bb70b379dece935c4033cf9b6", + "title": "Dependency ordering and cycle witnesses (validated small graphs)" + }, + { + "name": "tricu-parser", + "version": "0.4.0", + "hash": "3d364a349e4a623bc022a6cdc558a490d764c4c9d6a224b65a727f2380ce5b0d", + "manifest": "14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d", + "title": "Parser combinators: diagnostics, consumption and commitment" + }, + { + "name": "tricu-math", + "version": "0.4.0", + "hash": "ef024a03aeb00f82d77a0c710303e88ce254b20cd1d0d0aebb29c0309900c480", + "manifest": "7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db", + "title": "Binary arithmetic and ASCII classifiers (canonical naturals only)" + }, + { + "name": "tricu-lexer", + "version": "0.4.0", + "hash": "9bc1c7c66b150fdac8f046b2bad48b07bf585c9ea2c63b416dc72c4191b0128e", + "manifest": "816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b", + "title": "Located character scans and token positions" + }, + { + "name": "tricu-binary", + "version": "0.4.0", + "hash": "fe3b02f68025b90d0ce782885c8f72582642a985694eb4bcd702c2e006846a86", + "manifest": "7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc", + "title": "Checked byte readers, prefix parsing and UTF-8 validation" + }, + { + "name": "tricu-patterns", + "version": "0.4.0", + "hash": "fb81fe47421564ac80fedc90be6003906e017978385f749d2da95297e58587c2", + "manifest": "703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e", + "title": "Ordered predicate/action dispatch and thunked conditionals" + }, + { + "name": "tricu-list-traversal", + "version": "0.4.0", + "hash": "60afc41f04fa65130326eeed4ca9e128bee5e90c7041f9641bbf9cfe87c142cd", + "manifest": "4c79b7ad124027147b8fddd98c2b66cfd776c36d915c95dfb1fbf9b31a51fb88", + "title": "Early-stopping list folds with retained input suffixes" + }, + { + "name": "tricu-formatter", + "version": "0.4.0", + "hash": "3bd4df273ab4e4599417a18210ec3ad39e2ace2f7df6c0b7cad62999c4cb52a2", + "manifest": "e8bad7dcea5944336083f5bfa3ada63d7542286fbdec0f34dfeb65e9879ba2fc", + "title": "Conservative tricu equation alignment (not an AST printer)" + }, + { + "name": "parser-demo", + "version": "0.4.0", + "hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e", + "manifest": "0c34bfdf800f8bd1b742e375a8f7c555c88d0c827268f684259433e834d94fed", + "title": "Parser combinators: complete input, prefixes and committed errors", + "demo": "parser" + }, + { + "name": "lexer-demo", + "version": "0.4.0", + "hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2", + "manifest": "ed99e054e9c862668e165f458d3afcdc56279f766eea7a3886d440aea7252b63", + "title": "Located scans: matched prefix, remainder and character positions", + "demo": "lexer" + }, + { + "name": "tricu-lib-arboricx", + "version": "0.4.0", + "manifest": "3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55", + "hash": "ef3bbb54373753b15a2d3125e0f260efe2a1e7b464d451819afcc02721683bb2", + "title": "lib/arboricx/arboricx.tri" + }, + { + "name": "tricu-lib-arboricx-common", + "version": "0.4.0", + "manifest": "0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4", + "hash": "b1f763c54f8cbc87aebf1bb16fbe73805e09661b4cf8550edbdb9877ecf7a32c", + "title": "lib/arboricx/common.tri" + }, + { + "name": "tricu-lib-arboricx-dispatch", + "version": "0.4.0", + "manifest": "eda078ba018de68718b2b7aa2d790578ac079cf98c963a78f7fcae5178fd2c19", + "hash": "1e053fdb9d77ecbebd9c1d8b861dccae0c83098b7555d4fa68b310b1e5113b6e", + "title": "lib/arboricx/dispatch.tri" + }, + { + "name": "tricu-lib-arboricx-handlers", + "version": "0.4.0", + "manifest": "8a75e3d3c2fe9f4f535d1e220c90b5743ea4299890d73b334f502bf28f645dc0", + "hash": "9cda2cdf30790c2d13dc51183a4fb321a00bec8599683004280f7317cf7a49ab", + "title": "lib/arboricx/handlers.tri" + }, + { + "name": "tricu-lib-arboricx-manifest", + "version": "0.4.0", + "manifest": "2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa", + "hash": "bffce842f768003315e90f106d296297477db17bdd2bd61752e7197a89042402", + "title": "lib/arboricx/manifest.tri" + }, + { + "name": "tricu-lib-arboricx-nodes", + "version": "0.4.0", + "manifest": "d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23", + "hash": "6762fb2d66c52d04b4701b2508dd9ef6a8c62fcb66bfb607424f6b2caa488b85", + "title": "lib/arboricx/nodes.tri" + }, + { + "name": "tricu-lib-arboricx-server", + "version": "0.4.0", + "manifest": "800fd0557eb020bf4395e5b73d9a819d9c730da60f83351261b4cd8c07610ca6", + "hash": "3ff98271f9a512c04a8f200aa7488929edad50e1c7aa3864b243145f03cd3c9a", + "title": "lib/arboricx/server.tri" + }, + { + "name": "tricu-lib-arboricx-streaming-v2", + "version": "0.4.0", + "manifest": "6a3c9f666801c3d29aaa71e22400a5726e137ea2fe28693217db6160a9d65db1", + "hash": "fbc9e40b382d4ed621669432f2b1fd6f9353dfaba099f4b070b344eb3a30c4a6", + "title": "lib/arboricx/streaming-v2.tri" + }, + { + "name": "tricu-lib-base", + "version": "0.4.0", + "manifest": "bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81", + "hash": "1f722cc4ebbe2096dd68e1d9720596cfe9c99b327fd02d55ea01c5c66e66477a", + "title": "lib/base.tri" + }, + { + "name": "tricu-lib-binary", + "version": "0.4.0", + "manifest": "7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc", + "hash": "fe3b02f68025b90d0ce782885c8f72582642a985694eb4bcd702c2e006846a86", + "title": "lib/binary.tri" + }, + { + "name": "tricu-lib-bytes", + "version": "0.4.0", + "manifest": "16cff955cb9ab44063ed21651002d209ef7d3592fa005cd3dc32e287d6ca5b68", + "hash": "c0a9ffd923b4dd17f6c1fd105502d9e82a9064f75b6312b37c155c94a3ce73d3", + "title": "lib/bytes.tri" + }, + { + "name": "tricu-lib-conversions", + "version": "0.4.0", + "manifest": "78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e", + "hash": "fc37dd8c558961c88211c3f4faf31bc438bcda12384f94125f563f1651e09ac9", + "title": "lib/conversions.tri" + }, + { + "name": "tricu-lib-graph", + "version": "0.4.0", + "manifest": "7b798a3afae6dce07b718bed855c7209ec3b898bb70b379dece935c4033cf9b6", + "hash": "b588b07ac71f87ab85059ea0788ee2e11d3ee77ad319310e5a21f844c98dabf4", + "title": "lib/graph.tri" + }, + { + "name": "tricu-lib-handler-artifacts", + "version": "0.4.0", + "manifest": "0786c1fc251e88b12e081dc098a9bc7037d7589d251d6c84d594a9232b5ae117", + "hash": "4b329565f4c47401fe505e35090ec332e52b266c72cebe6b7d78985efeef1ba9", + "title": "lib/handlers/artifacts.tri" + }, + { + "name": "tricu-lib-handler-log", + "version": "0.4.0", + "manifest": "ea8a3d3db064d9cd2a6a6ab0725f2db5741f65a2b3ec76349abe14dfcc915b77", + "hash": "c60b350474c0c82734204dfd18f5780d97d296e091bef977f37157bff57102e9", + "title": "lib/handlers/log.tri" + }, + { + "name": "tricu-lib-handlers", + "version": "0.4.0", + "manifest": "48f0ae7349309cd4f445115b52ca638f53c87e69be7f8a59c08038d102813712", + "hash": "ffdd1a95b974e1745b846cfdfeaf0bcbf4b3b60e010f1d7184fe3333220a03cb", + "title": "lib/handlers.tri" + }, + { + "name": "tricu-lib-http", + "version": "0.4.0", + "manifest": "280dea7b29a3dc280a5cc5fda0c096a7698b36226d22c3e51ca93d39336dfa02", + "hash": "43799eb78baaff0314d6014ef7692661759901d4a29608c9a169176beb13b231", + "title": "lib/http.tri" + }, + { + "name": "tricu-lib-intensional", + "version": "0.4.0", + "manifest": "7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1", + "hash": "dd39639f1d0a1a14f7856b8d0db682adf36d1e7501ea1889bd185ea500989380", + "title": "lib/intensionalContracts.tri" + }, + { + "name": "tricu-lib-io-v2-artifacts", + "version": "0.4.0", + "manifest": "45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3", + "hash": "5d89ef0e68e4343c7a1da04c9dc6a82ff2e2089e770a1303f575b851c2814c4a", + "title": "lib/io-v2/artifacts.tri" + }, + { + "name": "tricu-lib-io-v2-http", + "version": "0.4.0", + "manifest": "87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52", + "hash": "36d76d7c128599998792fbeaa231ee416dc79b09c36fa20e1b1b5d2ead97076a", + "title": "lib/io-v2/http-transport.tri" + }, + { + "name": "tricu-lib-io-v2-storage", + "version": "0.4.0", + "manifest": "e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4", + "hash": "e965542627b83e4d55e3ce15748650c94ef0d447324d7dde772c0db1b0651788", + "title": "lib/io-v2/storage.tri" + }, + { + "name": "tricu-lib-io-v2-text", + "version": "0.4.0", + "manifest": "11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df", + "hash": "bfc61066613fa893c0d9e6e37659617bcaff9e97a8d633cffd1d84066857462d", + "title": "lib/io-v2/text.tri" + }, + { + "name": "tricu-lib-io-v2", + "version": "0.4.0", + "manifest": "3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b", + "hash": "ce83251eed3aa18a8bc53aeedda62dbd90ed91ecd270bc56460457b435a788c4", + "title": "lib/io-v2.tri" + }, + { + "name": "tricu-lib-io", + "version": "0.4.0", + "manifest": "70f08855b73ec6d2d262f4c985d5bf64166b3990767f245e7d8c8e8e4ea1137b", + "hash": "4b5a7389a4b3886049863f107d7ca840c19080f27ef99d76845de2264ac3d6e7", + "title": "lib/io.tri" + }, + { + "name": "tricu-lib-lexer", + "version": "0.4.0", + "manifest": "816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b", + "hash": "9bc1c7c66b150fdac8f046b2bad48b07bf585c9ea2c63b416dc72c4191b0128e", + "title": "lib/lexer.tri" + }, + { + "name": "tricu-lib-native-math", + "version": "0.4.0", + "manifest": "7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db", + "hash": "ef024a03aeb00f82d77a0c710303e88ce254b20cd1d0d0aebb29c0309900c480", + "title": "lib/math/native.tri" + }, + { + "name": "tricu-lib-parser", + "version": "0.4.0", + "manifest": "14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d", + "hash": "3d364a349e4a623bc022a6cdc558a490d764c4c9d6a224b65a727f2380ce5b0d", + "title": "lib/parser.tri" + }, + { + "name": "tricu-lib-patterns", + "version": "0.4.0", + "manifest": "703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e", + "hash": "fb81fe47421564ac80fedc90be6003906e017978385f749d2da95297e58587c2", + "title": "lib/patterns.tri" + }, + { + "name": "tricu-lib-prelude", + "version": "0.4.0", + "manifest": "6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823", + "hash": "7dc94b30f4fd1043ff6e1cc4c7ddde55d9338c262de145d1d1446d7b156fac33", + "title": "lib/prelude.tri" + }, + { + "name": "tricu-lib-socket", + "version": "0.4.0", + "manifest": "49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44", + "hash": "decfff4079c7c202f664a55496926cce1fcd1e9c67cf67b84718bf7d663e6f4d", + "title": "lib/socket.tri" + }, + { + "name": "tricu-lib-source-ast", + "version": "0.4.0", + "manifest": "c6579f89e47a16694f1c08fe9825b183d8bc272e23cd744eb954c106b689c32f", + "hash": "af66062931c5de826bd336625d53c1c354fe6cc736e56e247ae550c542f0fa8a", + "title": "lib/source/ast.tri" + }, + { + "name": "tricu-lib-source-build-stable", + "version": "0.4.0", + "manifest": "899ebd78231147cbf6b72a71298a72c7047d6fb1d8033fac88898e178613e6a7", + "hash": "d015f17a71941870f8ab9ba5cbbef82b7657a9217a64edf54e0b512cf3ea3bc9", + "title": "lib/source/build-stable.tri" + }, + { + "name": "tricu-lib-source-build", + "version": "0.4.0", + "manifest": "5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f", + "hash": "5bbdeb9f451895d515464d0fc2c3fe7f83a282bf8a0d51b9c2a52c6ca9748c93", + "title": "lib/source/build.tri" + }, + { + "name": "tricu-lib-source-bundle", + "version": "0.4.0", + "manifest": "d1af60d301f08b0c8715da80b9c632b04e71270817e0925aec81121734f3d1e6", + "hash": "660f9077134b141f0398b5506f0295084953801e9360ef668e185d244f529c40", + "title": "lib/source/bundle.tri" + }, + { + "name": "tricu-lib-source-compile", + "version": "0.4.0", + "manifest": "fb41d71410ad8fb9c0da35a5ab0a6b782b9814bd7629737b710b7404648939cd", + "hash": "6a8e34e2185846c701afdd8f5ee842e21d0fa2c8cee2040113e72f1b94b5e6ab", + "title": "lib/source/compile.tri" + }, + { + "name": "tricu-lib-source-core", + "version": "0.4.0", + "manifest": "83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2", + "hash": "d7105ef477224dfb10ad2773e54744cb3f87ddf304eb753d7cb9ba40ea9cc3df", + "title": "lib/source/core.tri" + }, + { + "name": "tricu-lib-source-equations", + "version": "0.4.0", + "manifest": "568c5d782a847e4f8d6b7061d51d34fc527a466b872ede27032104293df3bf8f", + "hash": "0898334053aa3bd7cea49407ec3d51c5b155a030eb51dee670da1e349229e1b3", + "title": "lib/source/equations.tri" + }, + { + "name": "tricu-lib-source-export-cache", + "version": "0.4.0", + "manifest": "7f29e52b9154b0ba6410836b055f8628f76bc51aa572efa2ef691f37ecaec077", + "hash": "d6bef87913b4b0859d181920121f95663325b679404c11b236941ee10dce0f1c", + "title": "lib/source/export-cache.tri" + }, + { + "name": "tricu-lib-source-index", + "version": "0.4.0", + "manifest": "c2946fe3ac0daf1a06d6f7deab9f7ad08d679c241b04ad9c0c6d3b6038cf091d", + "hash": "1f18232479e776982515edc3fe2c0f39fad79e2b428c2ff040d92edf5441ac6a", + "title": "lib/source/index.tri" + }, + { + "name": "tricu-lib-source-lexer", + "version": "0.4.0", + "manifest": "4cc4ed214d6e474b339c970d04cc09ced20c6472758b58897aabbd6f3a5bbb42", + "hash": "b61e8d7d31f730c1e4db08c729435302e76eb911ee2c45b110f2932a60b2eb33", + "title": "lib/source/lexer.tri" + }, + { + "name": "tricu-lib-source-lock", + "version": "0.4.0", + "manifest": "eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e", + "hash": "dcb5f1c60fac3c0cf1752d88b9e2482223d0759ef310cd7b401ef0148b7a6ffa", + "title": "lib/source/lock.tri" + }, + { + "name": "tricu-lib-source-map", + "version": "0.4.0", + "manifest": "b8326b79c0b2f7b1c95d7892752a8543077897ce0d61fe5050cbb60d1a9f0cf9", + "hash": "93174e0bad1894364a0a12560a75eea646e5747bcc2a71b7fd198bf9bb635596", + "title": "lib/source/map.tri" + }, + { + "name": "tricu-lib-source-module", + "version": "0.4.0", + "manifest": "4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74", + "hash": "80b2e70901d380aaca74a7a40019d3394b1171e6e76e771f58955f03bf8da798", + "title": "lib/source/module.tri" + }, + { + "name": "tricu-lib-source-object", + "version": "0.4.0", + "manifest": "dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf", + "hash": "acb79eb9c2f56a18c1c740aaaee97e9e5e53f55b56602bd16a351d85cfadc192", + "title": "lib/source/object.tri" + }, + { + "name": "tricu-lib-source-order", + "version": "0.4.0", + "manifest": "1d55b1616e892be989d2c987099c84bef61686fc67f6a3eb5b1a373626b0ca5e", + "hash": "4586d8b96a93d22e2382f40a0eed353dccebe6ab05713336451b13dfd756e813", + "title": "lib/source/order.tri" + }, + { + "name": "tricu-lib-source-parser", + "version": "0.4.0", + "manifest": "425b921ef62e99dd93f6d3389c82fdbb583eaeb857975717441c5469f98f3a73", + "hash": "ec7f3cddb70f48ec5db7eac7afb7641150a6bb3400d0734ca3419b6f7a5bbfff", + "title": "lib/source/parser.tri" + }, + { + "name": "tricu-lib-source-pattern", + "version": "0.4.0", + "manifest": "d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123", + "hash": "c2426615e6f90e2084bbb268305b11c06aea844e6cd3905efb92708fb2b91dde", + "title": "lib/source/pattern.tri" + }, + { + "name": "tricu-lib-source-program", + "version": "0.4.0", + "manifest": "447aa7d321344146772a2693e83b2ab87a0058e281c42780e1bb5b24e0ea995d", + "hash": "7da891b3098e3f405741f78a59e068445bbf4def4c41553bda2e5a57a4e63e2c", + "title": "lib/source/program.tri" + }, + { + "name": "tricu-lib-source-resolve", + "version": "0.4.0", + "manifest": "ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe", + "hash": "6fc6e4138589d8550886cbb2cc95d1de91940bee589ef400e5e6098bf3cc55ff", + "title": "lib/source/resolve.tri" + }, + { + "name": "tricu-lib-source-stable", + "version": "0.4.0", + "manifest": "5189315a72af93ee9fe3dbc8c73e07e97b980b4c91b9c2f6244d447461db55dd", + "hash": "b4bc7576279d3721981a0254b55184fdd7cc11426c2591adf8a6405c3e388283", + "title": "lib/source/stable.tri" + }, + { + "name": "tricu-lib-source-staged-conditional-experiment", + "version": "0.4.0", + "manifest": "43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682", + "hash": "c632e6845677fc13e40f02599f66a4e243fd91f9cef79a840ec4eb6953f66905", + "title": "lib/source/staged-conditional-experiment.tri" + }, + { + "name": "tricu-lib-source-staged-list-experiment", + "version": "0.4.0", + "manifest": "c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98", + "hash": "cd7be3dbf2c10f7a7e7911765cb65a5207a1558f289406e291d07cb077a69a34", + "title": "lib/source/staged-list-experiment.tri" + }, + { + "name": "tricu-lib-source-staging-experiment", + "version": "0.4.0", + "manifest": "db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790", + "hash": "445db0a1f362b908bd0cfd4e8540e51630fa0ff3c639ce54a1f282bf7e6b2a78", + "title": "lib/source/staging-experiment.tri" + }, + { + "name": "tricu-lib-source-unit", + "version": "0.4.0", + "manifest": "1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9", + "hash": "8e179edc0710085a37167af76c1f5bad871bb84901a7aa7345febda5d0f6c460", + "title": "lib/source/unit.tri" + }, + { + "name": "tricu-lib-arboricx-inspect", + "version": "0.4.0", + "manifest": "fe9b420e1e776fa1dda7b77857e322c0509ed308bb18441b76a3b03023f93468", + "hash": "f571888c5aa3f7a91e9d5d6c45c674851ea8e3598514ad8d535fd3e9056d0314", + "title": "lib/arboricx/inspect.tri" + }, + { + "name": "tricu-lib-arboricx-streaming", + "version": "0.4.0", + "manifest": "006850a50e39a3ef45abf611542637587496bed8c1af49c2e109045c5fef3c51", + "hash": "258aa91dbed2e1a0170a8f0c2a87d3e11e9ac861ff74711460c14f735c6af1a2", + "title": "lib/arboricx/streaming.tri" + }, + { + "name": "tricu-lib-handler-testing", + "version": "0.4.0", + "manifest": "4a7b39b868bc30f46460b5cee2bb225a9db665775353aa5c71aafa567de7881e", + "hash": "e56378259592a4867829ca5447904b30ac5e21bd496eb68ad4d872c1af973d69", + "title": "lib/handlers/testing.tri" + }, + { + "name": "tricu-lib-handler-upload", + "version": "0.4.0", + "manifest": "322db72c3bcad16b4876283d7648131d90235f21feb2513eca663e9121b9b87e", + "hash": "7ff7d2c5eead111a6364ca7f0dc0cc1e60910a3ffdd9bca18b8c624856e22f8d", + "title": "lib/handlers/upload.tri" + }, + { + "name": "tricu-lib-io-v2-objects", + "version": "0.4.0", + "manifest": "1ed153be17b754f1a058e419d84a39e2169d5d6518bb47e8ee8785b02860e424", + "hash": "bb3135842e3cebfe6bd10e0766a6711e4d46a8cf157af1dcc9f97e25795ac0a1", + "title": "lib/io-v2/objects.tri" + }, + { + "name": "tricu-lib-packages-base", + "version": "0.4.0", + "manifest": "fa9d4df0f82cac2754fdd3e92009cb48064856ff4cc5b18f092309aef7ff5ed4", + "hash": "f9f0ab0e44f6ae39479d74c6c66a2bdaba3f40bd26781c3ec95300c2ee3c07f3", + "title": "lib/packages/base.tri" + }, + { + "name": "tricu-lib-packages-io-v2", + "version": "0.4.0", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "title": "lib/packages/io-v2.tri" + }, + { + "name": "tricu-lib-packages-io", + "version": "0.4.0", + "manifest": "6a22866c2cff143599cb854b2cd3bb3c8948e40835425ed62d942d88bed5c5aa", + "hash": "401961d889131c735bf48b415f53d3b85dfab0136496299f60cf1dcb0b264f14", + "title": "lib/packages/io.tri" + }, + { + "name": "tricu-lib-recursion-list", + "version": "0.4.0", + "manifest": "4c79b7ad124027147b8fddd98c2b66cfd776c36d915c95dfb1fbf9b31a51fb88", + "hash": "60afc41f04fa65130326eeed4ca9e128bee5e90c7041f9641bbf9cfe87c142cd", + "title": "lib/recursion/list.tri" + }, + { + "name": "tricu-lib-source-bundle-stable", + "version": "0.4.0", + "manifest": "a06948780686a4432f97ce0a7ab5f8ed66a535a6d5563275e5f32375a679cb59", + "hash": "3de401378f33dd8f93e9ca11c1c2bd73a458dabcd8d631a33ba17306f83d7d3b", + "title": "lib/source/bundle-stable.tri" + }, + { + "name": "tricu-lib-source-io-v2", + "version": "0.4.0", + "manifest": "5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90", + "hash": "a7babd04ce08dd587a9cc0e71d7d52e2850eb824118a3ea8b82b6b4b73f63521", + "title": "lib/source/io-v2.tri" + }, + { + "name": "tricu-lib-tools-format-tri", + "version": "0.4.0", + "manifest": "e8bad7dcea5944336083f5bfa3ada63d7542286fbdec0f34dfeb65e9879ba2fc", + "hash": "3bd4df273ab4e4599417a18210ec3ad39e2ace2f7df6c0b7cad62999c4cb52a2", + "title": "lib/tools/format-tri.tri" + }, + { + "name": "greet", + "version": "0.5.0", + "hash": "b6f32449d85257fbd0a589d29ef05ea4a503c4a7e6105926d408b0551a34fd77", + "manifest": "e29abf0fedb2886d37e1ac26644ad833b6e66c950c1fbc8482189445b435b855", + "title": "A conversation", + "demo": "greet" + }, + { + "name": "tricu-base", + "version": "0.5.0", + "hash": "f9f0ab0e44f6ae39479d74c6c66a2bdaba3f40bd26781c3ec95300c2ee3c07f3", + "manifest": "fa9d4df0f82cac2754fdd3e92009cb48064856ff4cc5b18f092309aef7ff5ed4", + "title": "Foundation: lists, Results and decimal formatting" + }, + { + "name": "tricu-file-io", + "version": "0.5.0", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "title": "File IO and hashing actions" + }, + { + "name": "arboricx-inspection", + "version": "0.5.0", + "hash": "f571888c5aa3f7a91e9d5d6c45c674851ea8e3598514ad8d535fd3e9056d0314", + "manifest": "fe9b420e1e776fa1dda7b77857e322c0509ed308bb18441b76a3b03023f93468", + "title": "Arboricx inspection: validate without execution" + }, + { + "name": "tricu-compiler", + "version": "0.5.0", + "hash": "890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa", + "manifest": "22fccc94ad2972cf4ae81c73cf6f2e13aa39c0c12456083cb9811242520c17da", + "title": "Compiler library (stable browser source-unit API; native helpers)" + }, + { + "name": "tricu-source-builder", + "version": "0.5.0", + "hash": "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4", + "manifest": "44ca2cddfc2940055f30a16a70ff847dbd12f52317c949752f196a88567d3e73", + "title": "Stable native application compiler (modules, imports and publication)" + }, + { + "name": "arboricx-server", + "version": "0.5.0", + "hash": "3ff98271f9a512c04a8f200aa7488929edad50e1c7aa3864b243145f03cd3c9a", + "manifest": "800fd0557eb020bf4395e5b73d9a819d9c730da60f83351261b4cd8c07610ca6", + "title": "Arboricx registry server (native sockets and filesystem)" + }, + { + "name": "tricu-http", + "version": "0.5.0", + "hash": "43799eb78baaff0314d6014ef7692661759901d4a29608c9a169176beb13b231", + "manifest": "280dea7b29a3dc280a5cc5fda0c096a7698b36226d22c3e51ca93d39336dfa02", + "title": "HTTP parsing, responses and native socket handlers" + }, + { + "name": "tricu-intensional-contracts", + "version": "0.5.0", + "hash": "dd39639f1d0a1a14f7856b8d0db682adf36d1e7501ea1889bd185ea500989380", + "manifest": "7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1", + "title": "Intensional contracts: tree shapes and numeric structure" + }, + { + "name": "tricu-graphs", + "version": "0.5.0", + "hash": "5d3381723aafa9c5e4c4c3a6975de9481eb3160cbe52aca24076900528ef2603", + "manifest": "7b798a3afae6dce07b718bed855c7209ec3b898bb70b379dece935c4033cf9b6", + "title": "Dependency ordering and cycle witnesses (validated small graphs)" + }, + { + "name": "tricu-parser", + "version": "0.5.0", + "hash": "18b24788592f74b9c564c30214dff0231cb86338661d944ab697e7ae4ca1103e", + "manifest": "14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d", + "title": "Parser combinators: diagnostics, consumption and commitment" + }, + { + "name": "tricu-math", + "version": "0.5.0", + "hash": "48194ad053a11151b87d129db717d01ea34381d9684281dfe876c41884508265", + "manifest": "7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db", + "title": "Binary arithmetic and ASCII classifiers (canonical naturals only)" + }, + { + "name": "tricu-lexer", + "version": "0.5.0", + "hash": "5ec6ccb4827f0548444e480c0e6196ddaa7a570a44ebc6a91a922d7e59bf3012", + "manifest": "816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b", + "title": "Located character scans and token positions" + }, + { + "name": "tricu-binary", + "version": "0.5.0", + "hash": "fe3b02f68025b90d0ce782885c8f72582642a985694eb4bcd702c2e006846a86", + "manifest": "7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc", + "title": "Checked byte readers, prefix parsing and UTF-8 validation" + }, + { + "name": "tricu-patterns", + "version": "0.5.0", + "hash": "d00af17115d7a201333b92e386997f57c1808f5819a89d18312a96bcdfe777a8", + "manifest": "703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e", + "title": "Ordered predicate/action dispatch and thunked conditionals" + }, + { + "name": "tricu-list-traversal", + "version": "0.5.0", + "hash": "60afc41f04fa65130326eeed4ca9e128bee5e90c7041f9641bbf9cfe87c142cd", + "manifest": "4c79b7ad124027147b8fddd98c2b66cfd776c36d915c95dfb1fbf9b31a51fb88", + "title": "Early-stopping list folds with retained input suffixes" + }, + { + "name": "tricu-formatter", + "version": "0.5.0", + "hash": "3bd4df273ab4e4599417a18210ec3ad39e2ace2f7df6c0b7cad62999c4cb52a2", + "manifest": "e8bad7dcea5944336083f5bfa3ada63d7542286fbdec0f34dfeb65e9879ba2fc", + "title": "Conservative tricu equation alignment (not an AST printer)" + }, + { + "name": "parser-demo", + "version": "0.5.0", + "hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e", + "manifest": "0c34bfdf800f8bd1b742e375a8f7c555c88d0c827268f684259433e834d94fed", + "title": "Parser combinators: complete input, prefixes and committed errors", + "demo": "parser" + }, + { + "name": "lexer-demo", + "version": "0.5.0", + "hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2", + "manifest": "ed99e054e9c862668e165f458d3afcdc56279f766eea7a3886d440aea7252b63", + "title": "Located scans: matched prefix, remainder and character positions", + "demo": "lexer" + }, + { + "name": "tricu-lib-arboricx", + "version": "0.5.0", + "manifest": "3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55", + "hash": "ef3bbb54373753b15a2d3125e0f260efe2a1e7b464d451819afcc02721683bb2", + "title": "lib/arboricx/arboricx.tri" + }, + { + "name": "tricu-lib-arboricx-common", + "version": "0.5.0", + "manifest": "0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4", + "hash": "b1f763c54f8cbc87aebf1bb16fbe73805e09661b4cf8550edbdb9877ecf7a32c", + "title": "lib/arboricx/common.tri" + }, + { + "name": "tricu-lib-arboricx-dispatch", + "version": "0.5.0", + "manifest": "eda078ba018de68718b2b7aa2d790578ac079cf98c963a78f7fcae5178fd2c19", + "hash": "1e053fdb9d77ecbebd9c1d8b861dccae0c83098b7555d4fa68b310b1e5113b6e", + "title": "lib/arboricx/dispatch.tri" + }, + { + "name": "tricu-lib-arboricx-handlers", + "version": "0.5.0", + "manifest": "8a75e3d3c2fe9f4f535d1e220c90b5743ea4299890d73b334f502bf28f645dc0", + "hash": "9daafde4c396549744c888778485c9d2316d89db659181e1c46c3b7ed5a0f64f", + "title": "lib/arboricx/handlers.tri" + }, + { + "name": "tricu-lib-arboricx-manifest", + "version": "0.5.0", + "manifest": "2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa", + "hash": "bffce842f768003315e90f106d296297477db17bdd2bd61752e7197a89042402", + "title": "lib/arboricx/manifest.tri" + }, + { + "name": "tricu-lib-arboricx-nodes", + "version": "0.5.0", + "manifest": "d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23", + "hash": "6762fb2d66c52d04b4701b2508dd9ef6a8c62fcb66bfb607424f6b2caa488b85", + "title": "lib/arboricx/nodes.tri" + }, + { + "name": "tricu-lib-arboricx-server", + "version": "0.5.0", + "manifest": "800fd0557eb020bf4395e5b73d9a819d9c730da60f83351261b4cd8c07610ca6", + "hash": "3ff98271f9a512c04a8f200aa7488929edad50e1c7aa3864b243145f03cd3c9a", + "title": "lib/arboricx/server.tri" + }, + { + "name": "tricu-lib-arboricx-streaming-v2", + "version": "0.5.0", + "manifest": "6a3c9f666801c3d29aaa71e22400a5726e137ea2fe28693217db6160a9d65db1", + "hash": "3f0be618d76da309e1ebc74099978d56b1bf9c5eb0d8e732c71b91ef223fd15a", + "title": "lib/arboricx/streaming-v2.tri" + }, + { + "name": "tricu-lib-base", + "version": "0.5.0", + "manifest": "bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81", + "hash": "2eb5dd5e3a372ef4e5c1ed612ca73eff2b0c9f97ebb651fb7ab22d5db9a15ca3", + "title": "lib/base.tri" + }, + { + "name": "tricu-lib-binary", + "version": "0.5.0", + "manifest": "7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc", + "hash": "fe3b02f68025b90d0ce782885c8f72582642a985694eb4bcd702c2e006846a86", + "title": "lib/binary.tri" + }, + { + "name": "tricu-lib-bytes", + "version": "0.5.0", + "manifest": "16cff955cb9ab44063ed21651002d209ef7d3592fa005cd3dc32e287d6ca5b68", + "hash": "8ee2c0b4fec670afc9db5552e6907ace29ae2c0bda07897e70d451f9bb56160c", + "title": "lib/bytes.tri" + }, + { + "name": "tricu-lib-conversions", + "version": "0.5.0", + "manifest": "78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e", + "hash": "0ad0c17feaf6a8241ade01283210a1cded009eb4dd1cf99adcd31ea44017afba", + "title": "lib/conversions.tri" + }, + { + "name": "tricu-lib-graph", + "version": "0.5.0", + "manifest": "7b798a3afae6dce07b718bed855c7209ec3b898bb70b379dece935c4033cf9b6", + "hash": "5d3381723aafa9c5e4c4c3a6975de9481eb3160cbe52aca24076900528ef2603", + "title": "lib/graph.tri" + }, + { + "name": "tricu-lib-handler-artifacts", + "version": "0.5.0", + "manifest": "0786c1fc251e88b12e081dc098a9bc7037d7589d251d6c84d594a9232b5ae117", + "hash": "acada8dbbd27922e757f2331fae97eedcadae1f0db369f693bbb8d413c3b0b00", + "title": "lib/handlers/artifacts.tri" + }, + { + "name": "tricu-lib-handler-log", + "version": "0.5.0", + "manifest": "ea8a3d3db064d9cd2a6a6ab0725f2db5741f65a2b3ec76349abe14dfcc915b77", + "hash": "a9c978eb8475b65bd73bd6ac6474e6242df285cea8d73d5dcb19ca5d30512949", + "title": "lib/handlers/log.tri" + }, + { + "name": "tricu-lib-handlers", + "version": "0.5.0", + "manifest": "48f0ae7349309cd4f445115b52ca638f53c87e69be7f8a59c08038d102813712", + "hash": "bba8e1d66b151e6b2aa42e8c73d61fa325e81d0388c0316b3e57fb0ddee3dde9", + "title": "lib/handlers.tri" + }, + { + "name": "tricu-lib-http", + "version": "0.5.0", + "manifest": "280dea7b29a3dc280a5cc5fda0c096a7698b36226d22c3e51ca93d39336dfa02", + "hash": "43799eb78baaff0314d6014ef7692661759901d4a29608c9a169176beb13b231", + "title": "lib/http.tri" + }, + { + "name": "tricu-lib-intensional", + "version": "0.5.0", + "manifest": "7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1", + "hash": "dd39639f1d0a1a14f7856b8d0db682adf36d1e7501ea1889bd185ea500989380", + "title": "lib/intensionalContracts.tri" + }, + { + "name": "tricu-lib-io-v2-artifacts", + "version": "0.5.0", + "manifest": "45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3", + "hash": "20a5c725f2dc2c02598364f26fc86e122913bde38600fd6512da52e2dcef55de", + "title": "lib/io-v2/artifacts.tri" + }, + { + "name": "tricu-lib-io-v2-http", + "version": "0.5.0", + "manifest": "87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52", + "hash": "1572e500aef32b7e069ecc985778f56cb5d4914ce99221d328817e2f10e168e8", + "title": "lib/io-v2/http-transport.tri" + }, + { + "name": "tricu-lib-io-v2-storage", + "version": "0.5.0", + "manifest": "e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4", + "hash": "e1376f99435b5869d7f53b489a5d7552b382f670710a6e0e970f13ddc330948b", + "title": "lib/io-v2/storage.tri" + }, + { + "name": "tricu-lib-io-v2-text", + "version": "0.5.0", + "manifest": "11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df", + "hash": "f056d6c372cd700f325f6077a043492c9f4fb5d59e179ddfc00f69e608c7d969", + "title": "lib/io-v2/text.tri" + }, + { + "name": "tricu-lib-io-v2", + "version": "0.5.0", + "manifest": "3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b", + "hash": "d9f2858d088128d306525252c74025eb49565187b39da7e88c88b758cc4035d1", + "title": "lib/io-v2.tri" + }, + { + "name": "tricu-lib-io", + "version": "0.5.0", + "manifest": "70f08855b73ec6d2d262f4c985d5bf64166b3990767f245e7d8c8e8e4ea1137b", + "hash": "e75e7178eb19bba565e1422eb140411e34c02df3db1fe32224fc434c6e6f5b69", + "title": "lib/io.tri" + }, + { + "name": "tricu-lib-lexer", + "version": "0.5.0", + "manifest": "816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b", + "hash": "5ec6ccb4827f0548444e480c0e6196ddaa7a570a44ebc6a91a922d7e59bf3012", + "title": "lib/lexer.tri" + }, + { + "name": "tricu-lib-native-math", + "version": "0.5.0", + "manifest": "7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db", + "hash": "48194ad053a11151b87d129db717d01ea34381d9684281dfe876c41884508265", + "title": "lib/math/native.tri" + }, + { + "name": "tricu-lib-parser", + "version": "0.5.0", + "manifest": "14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d", + "hash": "18b24788592f74b9c564c30214dff0231cb86338661d944ab697e7ae4ca1103e", + "title": "lib/parser.tri" + }, + { + "name": "tricu-lib-patterns", + "version": "0.5.0", + "manifest": "703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e", + "hash": "d00af17115d7a201333b92e386997f57c1808f5819a89d18312a96bcdfe777a8", + "title": "lib/patterns.tri" + }, + { + "name": "tricu-lib-prelude", + "version": "0.5.0", + "manifest": "6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823", + "hash": "00ebc0925d110a0733709f2aac0dedf4de7b59f33ac3eee107965496806cd77d", + "title": "lib/prelude.tri" + }, + { + "name": "tricu-lib-socket", + "version": "0.5.0", + "manifest": "49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44", + "hash": "decfff4079c7c202f664a55496926cce1fcd1e9c67cf67b84718bf7d663e6f4d", + "title": "lib/socket.tri" + }, + { + "name": "tricu-lib-source-ast", + "version": "0.5.0", + "manifest": "c6579f89e47a16694f1c08fe9825b183d8bc272e23cd744eb954c106b689c32f", + "hash": "af66062931c5de826bd336625d53c1c354fe6cc736e56e247ae550c542f0fa8a", + "title": "lib/source/ast.tri" + }, + { + "name": "tricu-lib-source-build-stable", + "version": "0.5.0", + "manifest": "d4c7521ff4bfea552124779a06ae3616e0dbbb3f00f711903b6f0f85902268df", + "hash": "6e185ece27a038b020b9b3f3a305009209849436697fd19f5aa888a383f92f40", + "title": "lib/source/build-stable.tri" + }, + { + "name": "tricu-lib-source-build", + "version": "0.5.0", + "manifest": "f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74", + "hash": "3bd031b7077f8cfe87da22b1fb5960fbf911fdaa5756821185b6431bc74be43b", + "title": "lib/source/build.tri" + }, + { + "name": "tricu-lib-source-bundle", + "version": "0.5.0", + "manifest": "2937e908e943ffb69470824d06805374b91301ae73b3b7eb1cb3cf2cf32b011f", + "hash": "925989ee208fe9cfd108c6093da5648b87ace44e134c7b28b0772eb5a5c540fc", + "title": "lib/source/bundle.tri" + }, + { + "name": "tricu-lib-source-compile", + "version": "0.5.0", + "manifest": "fb41d71410ad8fb9c0da35a5ab0a6b782b9814bd7629737b710b7404648939cd", + "hash": "ec65f6eac30f1487dc64e519ae0be9769695edbff58e529719424a95059c6875", + "title": "lib/source/compile.tri" + }, + { + "name": "tricu-lib-source-core", + "version": "0.5.0", + "manifest": "83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2", + "hash": "d7105ef477224dfb10ad2773e54744cb3f87ddf304eb753d7cb9ba40ea9cc3df", + "title": "lib/source/core.tri" + }, + { + "name": "tricu-lib-source-equations", + "version": "0.5.0", + "manifest": "568c5d782a847e4f8d6b7061d51d34fc527a466b872ede27032104293df3bf8f", + "hash": "62f77e46a310ae6dbfcd73d096126f0f6a16c61b98842d826a0b26783f733981", + "title": "lib/source/equations.tri" + }, + { + "name": "tricu-lib-source-export-cache", + "version": "0.5.0", + "manifest": "adb94daf05edaab172bf385632665cf03122ff71a288199243062f0a6c72b573", + "hash": "5fb1fab64ccc6554ebeedb74c35f616833f64e007aa787d37eb544739ca7d371", + "title": "lib/source/export-cache.tri" + }, + { + "name": "tricu-lib-source-index", + "version": "0.5.0", + "manifest": "c2946fe3ac0daf1a06d6f7deab9f7ad08d679c241b04ad9c0c6d3b6038cf091d", + "hash": "1f18232479e776982515edc3fe2c0f39fad79e2b428c2ff040d92edf5441ac6a", + "title": "lib/source/index.tri" + }, + { + "name": "tricu-lib-compiler-io", + "version": "0.5.0", + "manifest": "5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90", + "hash": "4872d3ee67db34a9ce6844e5a80befe217d6e248441d875d335da0e706e2b501", + "title": "lib/source/io-v2.tri" + }, + { + "name": "tricu-lib-source-lexer", + "version": "0.5.0", + "manifest": "4cc4ed214d6e474b339c970d04cc09ced20c6472758b58897aabbd6f3a5bbb42", + "hash": "b75e428ce6d587893829792a87c3a374993d22c900eea793f2713e822440cb9f", + "title": "lib/source/lexer.tri" + }, + { + "name": "tricu-lib-source-lock", + "version": "0.5.0", + "manifest": "4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad", + "hash": "cdda8a8efdd40b9f2d5beac446646831d11a6f1cf916a40d68f8064b90dda45a", + "title": "lib/source/lock.tri" + }, + { + "name": "tricu-lib-source-map", + "version": "0.5.0", + "manifest": "5b59e01e15cb66ae42014dea08143de5856b4ddc38858057ba57e44eebdca91d", + "hash": "1bced95d1d42116194b26c53779cc68900fd7c03263266603966243ae91f8545", + "title": "lib/source/map.tri" + }, + { + "name": "tricu-lib-source-module", + "version": "0.5.0", + "manifest": "c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e", + "hash": "54d1e5652a001bf19668bda2f660e7ede120ce6e8670e02a95daf18469ab9d0d", + "title": "lib/source/module.tri" + }, + { + "name": "tricu-lib-source-object", + "version": "0.5.0", + "manifest": "c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f", + "hash": "eb3c997bcd9d2cd2be93191865894f9627946462526ed3cffe3fbbdad6e3f682", + "title": "lib/source/object.tri" + }, + { + "name": "tricu-lib-source-order", + "version": "0.5.0", + "manifest": "1d55b1616e892be989d2c987099c84bef61686fc67f6a3eb5b1a373626b0ca5e", + "hash": "7c5ec2c441d6ab4ef23edc3e9941dbf7bb4a7718d9e6e93f8826171e3f92f3bc", + "title": "lib/source/order.tri" + }, + { + "name": "tricu-lib-source-parser", + "version": "0.5.0", + "manifest": "425b921ef62e99dd93f6d3389c82fdbb583eaeb857975717441c5469f98f3a73", + "hash": "9f63810fee2e20762f1a198535448a2187a90065a35428ec82a11aad85575643", + "title": "lib/source/parser.tri" + }, + { + "name": "tricu-lib-source-pattern", + "version": "0.5.0", + "manifest": "d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123", + "hash": "440618420eda88e9d8867ac5f99ade97dd8d678c44aa120995a96062e57c2045", + "title": "lib/source/pattern.tri" + }, + { + "name": "tricu-lib-source-program", + "version": "0.5.0", + "manifest": "447aa7d321344146772a2693e83b2ab87a0058e281c42780e1bb5b24e0ea995d", + "hash": "070c4d26248d75ce9d1d66c19a4a7ad92558b9e14a837791b72f50e07afc5463", + "title": "lib/source/program.tri" + }, + { + "name": "tricu-lib-source-resolve", + "version": "0.5.0", + "manifest": "ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe", + "hash": "2f6f3a1a9615134165e44ebb529d73608983f421bf2b483bc3f11e85cad27fa8", + "title": "lib/source/resolve.tri" + }, + { + "name": "tricu-lib-source-stable", + "version": "0.5.0", + "manifest": "5189315a72af93ee9fe3dbc8c73e07e97b980b4c91b9c2f6244d447461db55dd", + "hash": "3670141b6ee280efe14f6f0dd2ef9469560cdbfaa08d2510ca37ddc9802fd18d", + "title": "lib/source/stable.tri" + }, + { + "name": "tricu-lib-source-staged-conditional-experiment", + "version": "0.5.0", + "manifest": "43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682", + "hash": "f78cfa7217deb642299e0fa66d0d714311ec7a196dee4baa621881b7190c97d9", + "title": "lib/source/staged-conditional-experiment.tri" + }, + { + "name": "tricu-lib-source-staged-list-experiment", + "version": "0.5.0", + "manifest": "c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98", + "hash": "cda1110a3577a751c2673bc7a1ce2c3994dec541a3c1f6034b6a6769eaee4809", + "title": "lib/source/staged-list-experiment.tri" + }, + { + "name": "tricu-lib-source-staging-experiment", + "version": "0.5.0", + "manifest": "db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790", + "hash": "422e1a6e5cc0e164bf627e19278e598665fe0edc05161c37f27b1c196e58da60", + "title": "lib/source/staging-experiment.tri" + }, + { + "name": "tricu-lib-source-unit", + "version": "0.5.0", + "manifest": "1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9", + "hash": "16de0d621776b79ff676b9415405b19e3088a2409d51f2efac850acca249b119", + "title": "lib/source/unit.tri" + }, + { + "name": "tricu-lib-arboricx-inspect", + "version": "0.5.0", + "manifest": "fe9b420e1e776fa1dda7b77857e322c0509ed308bb18441b76a3b03023f93468", + "hash": "f571888c5aa3f7a91e9d5d6c45c674851ea8e3598514ad8d535fd3e9056d0314", + "title": "lib/arboricx/inspect.tri" + }, + { + "name": "tricu-lib-arboricx-streaming", + "version": "0.5.0", + "manifest": "006850a50e39a3ef45abf611542637587496bed8c1af49c2e109045c5fef3c51", + "hash": "258aa91dbed2e1a0170a8f0c2a87d3e11e9ac861ff74711460c14f735c6af1a2", + "title": "lib/arboricx/streaming.tri" + }, + { + "name": "tricu-lib-handler-testing", + "version": "0.5.0", + "manifest": "4a7b39b868bc30f46460b5cee2bb225a9db665775353aa5c71aafa567de7881e", + "hash": "e56378259592a4867829ca5447904b30ac5e21bd496eb68ad4d872c1af973d69", + "title": "lib/handlers/testing.tri" + }, + { + "name": "tricu-lib-handler-upload", + "version": "0.5.0", + "manifest": "322db72c3bcad16b4876283d7648131d90235f21feb2513eca663e9121b9b87e", + "hash": "7ff7d2c5eead111a6364ca7f0dc0cc1e60910a3ffdd9bca18b8c624856e22f8d", + "title": "lib/handlers/upload.tri" + }, + { + "name": "tricu-lib-io-v2-objects", + "version": "0.5.0", + "manifest": "1ed153be17b754f1a058e419d84a39e2169d5d6518bb47e8ee8785b02860e424", + "hash": "bb3135842e3cebfe6bd10e0766a6711e4d46a8cf157af1dcc9f97e25795ac0a1", + "title": "lib/io-v2/objects.tri" + }, + { + "name": "tricu-lib-packages-base", + "version": "0.5.0", + "manifest": "fa9d4df0f82cac2754fdd3e92009cb48064856ff4cc5b18f092309aef7ff5ed4", + "hash": "f9f0ab0e44f6ae39479d74c6c66a2bdaba3f40bd26781c3ec95300c2ee3c07f3", + "title": "lib/packages/base.tri" + }, + { + "name": "tricu-lib-packages-io-v2", + "version": "0.5.0", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "title": "lib/packages/io-v2.tri" + }, + { + "name": "tricu-lib-packages-io", + "version": "0.5.0", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "title": "lib/packages/io.tri" + }, + { + "name": "tricu-lib-recursion-list", + "version": "0.5.0", + "manifest": "4c79b7ad124027147b8fddd98c2b66cfd776c36d915c95dfb1fbf9b31a51fb88", + "hash": "60afc41f04fa65130326eeed4ca9e128bee5e90c7041f9641bbf9cfe87c142cd", + "title": "lib/recursion/list.tri" + }, + { + "name": "tricu-lib-source-bundle-stable", + "version": "0.5.0", + "manifest": "a06948780686a4432f97ce0a7ab5f8ed66a535a6d5563275e5f32375a679cb59", + "hash": "3de401378f33dd8f93e9ca11c1c2bd73a458dabcd8d631a33ba17306f83d7d3b", + "title": "lib/source/bundle-stable.tri" + }, + { + "name": "tricu-lib-tools-format-tri", + "version": "0.5.0", + "manifest": "e8bad7dcea5944336083f5bfa3ada63d7542286fbdec0f34dfeb65e9879ba2fc", + "hash": "3bd4df273ab4e4599417a18210ec3ad39e2ace2f7df6c0b7cad62999c4cb52a2", + "title": "lib/tools/format-tri.tri" + }, + { + "name": "tricu-lib-arboricx", + "version": "0.6.0", + "manifest": "3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55", + "hash": "7e698de68504e56e6c93d7822b3fe8dca79fa7aadd30710be63a3b4710fda061", + "title": "lib/arboricx/arboricx.tri" + }, + { + "name": "tricu-lib-arboricx-common", + "version": "0.6.0", + "manifest": "0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4", + "hash": "c2595128cd7056a33b634abc4a67bb99736b9cda08b6788e2936d8cd51fbaff1", + "title": "lib/arboricx/common.tri" + }, + { + "name": "tricu-lib-arboricx-dispatch", + "version": "0.6.0", + "manifest": "eda078ba018de68718b2b7aa2d790578ac079cf98c963a78f7fcae5178fd2c19", + "hash": "3c7cc88a1e7d6f11892357e15647186fa400b4aa50c7dbe7b9beb259b6ed48c2", + "title": "lib/arboricx/dispatch.tri" + }, + { + "name": "tricu-lib-arboricx-handlers", + "version": "0.6.0", + "manifest": "69dfc2b3bf17652845b198b06eb9b04a8c974529c623ba873e06c7654e44c271", + "hash": "b60da1233b8b2a10ba7179e7f8fe39a07be8d2546c89ab36fadab28e2dfd8b23", + "title": "lib/arboricx/handlers.tri" + }, + { + "name": "tricu-lib-arboricx-manifest", + "version": "0.6.0", + "manifest": "2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa", + "hash": "ab19d9010aba0c7fcb6995c1c096872dc3bfb22a5798407011c01adfb7c57d8a", + "title": "lib/arboricx/manifest.tri" + }, + { + "name": "tricu-lib-arboricx-nodes", + "version": "0.6.0", + "manifest": "d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23", + "hash": "832388f8b0380e7f2bce6ca3e79f38ee6236c1d25232ea2cd5b6119c6eb41fba", + "title": "lib/arboricx/nodes.tri" + }, + { + "name": "tricu-lib-arboricx-server", + "version": "0.6.0", + "manifest": "a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d", + "hash": "0a5d5d83f73a91959f8ff34a45643dae056456817c22569879e4675caf461086", + "title": "lib/arboricx/server.tri" + }, + { + "name": "tricu-lib-arboricx-streaming-v2", + "version": "0.6.0", + "manifest": "6a3c9f666801c3d29aaa71e22400a5726e137ea2fe28693217db6160a9d65db1", + "hash": "b0bb9f08d3a902c1b9e9649e58687d3c06e09421dd0c6d326ca854196da75878", + "title": "lib/arboricx/streaming-v2.tri" + }, + { + "name": "tricu-lib-base", + "version": "0.6.0", + "manifest": "bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81", + "hash": "2eb5dd5e3a372ef4e5c1ed612ca73eff2b0c9f97ebb651fb7ab22d5db9a15ca3", + "title": "lib/base.tri" + }, + { + "name": "tricu-lib-binary", + "version": "0.6.0", + "manifest": "7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc", + "hash": "7b6b438b2e903d6364eed8e948a814a1ebf2d08dfa9960fd7c1b278222f8cf4e", + "title": "lib/binary.tri" + }, + { + "name": "tricu-lib-bytes", + "version": "0.6.0", + "manifest": "16cff955cb9ab44063ed21651002d209ef7d3592fa005cd3dc32e287d6ca5b68", + "hash": "8ee2c0b4fec670afc9db5552e6907ace29ae2c0bda07897e70d451f9bb56160c", + "title": "lib/bytes.tri" + }, + { + "name": "tricu-lib-conversions", + "version": "0.6.0", + "manifest": "78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e", + "hash": "0ad0c17feaf6a8241ade01283210a1cded009eb4dd1cf99adcd31ea44017afba", + "title": "lib/conversions.tri" + }, + { + "name": "tricu-lib-graph", + "version": "0.6.0", + "manifest": "7b798a3afae6dce07b718bed855c7209ec3b898bb70b379dece935c4033cf9b6", + "hash": "5d3381723aafa9c5e4c4c3a6975de9481eb3160cbe52aca24076900528ef2603", + "title": "lib/graph.tri" + }, + { + "name": "tricu-lib-handler-artifacts", + "version": "0.6.0", + "manifest": "7ea0170462f611146ccb5e3c3e9a7f335a6c7764a5b06c73f66bacb919de2955", + "hash": "c80f9fa19c06b6c30f7bea3c21b9fec4516c9aafc4ec9045185f9fdda64106dd", + "title": "lib/handlers/artifacts.tri" + }, + { + "name": "tricu-lib-handler-log", + "version": "0.6.0", + "manifest": "ea8a3d3db064d9cd2a6a6ab0725f2db5741f65a2b3ec76349abe14dfcc915b77", + "hash": "de2b06e659b460d0ed6407f614396d1e9f6b43c455788c6660c88b153fc97fbc", + "title": "lib/handlers/log.tri" + }, + { + "name": "tricu-lib-handlers", + "version": "0.6.0", + "manifest": "e1b6de45c2f6dbab09046912f724f4cff9960c78946dbe9f3602d1d4066f23a4", + "hash": "a11920a322cbec778cf81d92ce6bdf28ae3ccfc772f87be92545f8462136304a", + "title": "lib/handlers.tri" + }, + { + "name": "tricu-lib-http", + "version": "0.6.0", + "manifest": "b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6", + "hash": "8b38dd5934bbc51f999e3a2da1ad33736991c7dd9aab5144b716611fc5a80810", + "title": "lib/http.tri" + }, + { + "name": "tricu-lib-intensional", + "version": "0.6.0", + "manifest": "7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1", + "hash": "a96e8e66c1759e1893a70a849c68eda171baf833b651d152102a8a7013f6e608", + "title": "lib/intensionalContracts.tri" + }, + { + "name": "tricu-lib-io-v2-artifacts", + "version": "0.6.0", + "manifest": "45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3", + "hash": "a2c5b9dc0013aa6df913b6ac5cef4467d15c237b1a3c5a7205ce434252cd4865", + "title": "lib/io-v2/artifacts.tri" + }, + { + "name": "tricu-lib-io-v2-http", + "version": "0.6.0", + "manifest": "87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52", + "hash": "1a1f3ea6c15e88e5929f151df64687a3dcb28b2d5f2091acb89c39081d4fe28d", + "title": "lib/io-v2/http-transport.tri" + }, + { + "name": "tricu-lib-io-v2-storage", + "version": "0.6.0", + "manifest": "e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4", + "hash": "e1376f99435b5869d7f53b489a5d7552b382f670710a6e0e970f13ddc330948b", + "title": "lib/io-v2/storage.tri" + }, + { + "name": "tricu-lib-io-v2-text", + "version": "0.6.0", + "manifest": "11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df", + "hash": "f056d6c372cd700f325f6077a043492c9f4fb5d59e179ddfc00f69e608c7d969", + "title": "lib/io-v2/text.tri" + }, + { + "name": "tricu-lib-io-v2", + "version": "0.6.0", + "manifest": "3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b", + "hash": "d9f2858d088128d306525252c74025eb49565187b39da7e88c88b758cc4035d1", + "title": "lib/io-v2.tri" + }, + { + "name": "tricu-lib-io", + "version": "0.6.0", + "manifest": "3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b", + "hash": "25a9d7530dbb272a32516897c08f0672dd84676da8c3bc0828bcf2824ca5b5e9", + "title": "lib/io.tri" + }, + { + "name": "tricu-lib-lexer", + "version": "0.6.0", + "manifest": "816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b", + "hash": "5ec6ccb4827f0548444e480c0e6196ddaa7a570a44ebc6a91a922d7e59bf3012", + "title": "lib/lexer.tri" + }, + { + "name": "tricu-lib-native-math", + "version": "0.6.0", + "manifest": "7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db", + "hash": "48194ad053a11151b87d129db717d01ea34381d9684281dfe876c41884508265", + "title": "lib/math/native.tri" + }, + { + "name": "tricu-lib-parser", + "version": "0.6.0", + "manifest": "14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d", + "hash": "18b24788592f74b9c564c30214dff0231cb86338661d944ab697e7ae4ca1103e", + "title": "lib/parser.tri" + }, + { + "name": "tricu-lib-patterns", + "version": "0.6.0", + "manifest": "703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e", + "hash": "7a59f1f37047cadd22f97fb29e07f549e5f3a41b63b9af35e013669eb7a4893f", + "title": "lib/patterns.tri" + }, + { + "name": "tricu-lib-prelude", + "version": "0.6.0", + "manifest": "6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823", + "hash": "00ebc0925d110a0733709f2aac0dedf4de7b59f33ac3eee107965496806cd77d", + "title": "lib/prelude.tri" + }, + { + "name": "tricu-lib-socket", + "version": "0.6.0", + "manifest": "f43fa8a321b5cd25954941270ea7f56d68115c77fac007f4e899dbc221f2cd38", + "hash": "50d923a5471cc42bff0a82da06b21ab6616ce90e76e94c3fa3bf4f0ab70c49bd", + "title": "lib/socket.tri" + }, + { + "name": "tricu-lib-source-ast", + "version": "0.6.0", + "manifest": "c6579f89e47a16694f1c08fe9825b183d8bc272e23cd744eb954c106b689c32f", + "hash": "af66062931c5de826bd336625d53c1c354fe6cc736e56e247ae550c542f0fa8a", + "title": "lib/source/ast.tri" + }, + { + "name": "tricu-lib-source-build-stable", + "version": "0.6.0", + "manifest": "d4c7521ff4bfea552124779a06ae3616e0dbbb3f00f711903b6f0f85902268df", + "hash": "684497e21954bc0392fab20009d93efafb4ed15315ae94e345f7e5c0530d0a13", + "title": "lib/source/build-stable.tri" + }, + { + "name": "tricu-lib-source-build", + "version": "0.6.0", + "manifest": "f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74", + "hash": "3bd031b7077f8cfe87da22b1fb5960fbf911fdaa5756821185b6431bc74be43b", + "title": "lib/source/build.tri" + }, + { + "name": "tricu-lib-source-bundle", + "version": "0.6.0", + "manifest": "2937e908e943ffb69470824d06805374b91301ae73b3b7eb1cb3cf2cf32b011f", + "hash": "925989ee208fe9cfd108c6093da5648b87ace44e134c7b28b0772eb5a5c540fc", + "title": "lib/source/bundle.tri" + }, + { + "name": "tricu-lib-source-compile", + "version": "0.6.0", + "manifest": "fb41d71410ad8fb9c0da35a5ab0a6b782b9814bd7629737b710b7404648939cd", + "hash": "ec65f6eac30f1487dc64e519ae0be9769695edbff58e529719424a95059c6875", + "title": "lib/source/compile.tri" + }, + { + "name": "tricu-lib-source-core", + "version": "0.6.0", + "manifest": "83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2", + "hash": "d7105ef477224dfb10ad2773e54744cb3f87ddf304eb753d7cb9ba40ea9cc3df", + "title": "lib/source/core.tri" + }, + { + "name": "tricu-lib-source-equations", + "version": "0.6.0", + "manifest": "568c5d782a847e4f8d6b7061d51d34fc527a466b872ede27032104293df3bf8f", + "hash": "62f77e46a310ae6dbfcd73d096126f0f6a16c61b98842d826a0b26783f733981", + "title": "lib/source/equations.tri" + }, + { + "name": "tricu-lib-source-export-cache", + "version": "0.6.0", + "manifest": "adb94daf05edaab172bf385632665cf03122ff71a288199243062f0a6c72b573", + "hash": "ef1971af4ec38386365ef2fc2a9e4fe05f2dd8af3c8ca887a91789d9959a0ed2", + "title": "lib/source/export-cache.tri" + }, + { + "name": "tricu-lib-source-index", + "version": "0.6.0", + "manifest": "c2946fe3ac0daf1a06d6f7deab9f7ad08d679c241b04ad9c0c6d3b6038cf091d", + "hash": "1f18232479e776982515edc3fe2c0f39fad79e2b428c2ff040d92edf5441ac6a", + "title": "lib/source/index.tri" + }, + { + "name": "tricu-lib-compiler-io", + "version": "0.6.0", + "manifest": "5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90", + "hash": "4872d3ee67db34a9ce6844e5a80befe217d6e248441d875d335da0e706e2b501", + "title": "lib/source/io-v2.tri" + }, + { + "name": "tricu-lib-source-lexer", + "version": "0.6.0", + "manifest": "4cc4ed214d6e474b339c970d04cc09ced20c6472758b58897aabbd6f3a5bbb42", + "hash": "b75e428ce6d587893829792a87c3a374993d22c900eea793f2713e822440cb9f", + "title": "lib/source/lexer.tri" + }, + { + "name": "tricu-lib-source-lock", + "version": "0.6.0", + "manifest": "4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad", + "hash": "cdda8a8efdd40b9f2d5beac446646831d11a6f1cf916a40d68f8064b90dda45a", + "title": "lib/source/lock.tri" + }, + { + "name": "tricu-lib-source-map", + "version": "0.6.0", + "manifest": "5b59e01e15cb66ae42014dea08143de5856b4ddc38858057ba57e44eebdca91d", + "hash": "1bced95d1d42116194b26c53779cc68900fd7c03263266603966243ae91f8545", + "title": "lib/source/map.tri" + }, + { + "name": "tricu-lib-source-module", + "version": "0.6.0", + "manifest": "c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e", + "hash": "54d1e5652a001bf19668bda2f660e7ede120ce6e8670e02a95daf18469ab9d0d", + "title": "lib/source/module.tri" + }, + { + "name": "tricu-lib-source-object", + "version": "0.6.0", + "manifest": "c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f", + "hash": "eb3c997bcd9d2cd2be93191865894f9627946462526ed3cffe3fbbdad6e3f682", + "title": "lib/source/object.tri" + }, + { + "name": "tricu-lib-source-order", + "version": "0.6.0", + "manifest": "1d55b1616e892be989d2c987099c84bef61686fc67f6a3eb5b1a373626b0ca5e", + "hash": "7c5ec2c441d6ab4ef23edc3e9941dbf7bb4a7718d9e6e93f8826171e3f92f3bc", + "title": "lib/source/order.tri" + }, + { + "name": "tricu-lib-source-parser", + "version": "0.6.0", + "manifest": "425b921ef62e99dd93f6d3389c82fdbb583eaeb857975717441c5469f98f3a73", + "hash": "9f63810fee2e20762f1a198535448a2187a90065a35428ec82a11aad85575643", + "title": "lib/source/parser.tri" + }, + { + "name": "tricu-lib-source-pattern", + "version": "0.6.0", + "manifest": "d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123", + "hash": "440618420eda88e9d8867ac5f99ade97dd8d678c44aa120995a96062e57c2045", + "title": "lib/source/pattern.tri" + }, + { + "name": "tricu-lib-source-program", + "version": "0.6.0", + "manifest": "447aa7d321344146772a2693e83b2ab87a0058e281c42780e1bb5b24e0ea995d", + "hash": "070c4d26248d75ce9d1d66c19a4a7ad92558b9e14a837791b72f50e07afc5463", + "title": "lib/source/program.tri" + }, + { + "name": "tricu-lib-source-resolve", + "version": "0.6.0", + "manifest": "ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe", + "hash": "2f6f3a1a9615134165e44ebb529d73608983f421bf2b483bc3f11e85cad27fa8", + "title": "lib/source/resolve.tri" + }, + { + "name": "tricu-lib-source-stable", + "version": "0.6.0", + "manifest": "5189315a72af93ee9fe3dbc8c73e07e97b980b4c91b9c2f6244d447461db55dd", + "hash": "3670141b6ee280efe14f6f0dd2ef9469560cdbfaa08d2510ca37ddc9802fd18d", + "title": "lib/source/stable.tri" + }, + { + "name": "tricu-lib-source-staged-conditional-experiment", + "version": "0.6.0", + "manifest": "43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682", + "hash": "3f8b3fc627479635890fa038e5fbac100db0d3014ed2f63c41ce1c4f856fb9bf", + "title": "lib/source/staged-conditional-experiment.tri" + }, + { + "name": "tricu-lib-source-staged-list-experiment", + "version": "0.6.0", + "manifest": "c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98", + "hash": "275c92fc65c5f9ae4caa0a5d4183c5ccd6055c815b17f82da4b952761f8ad987", + "title": "lib/source/staged-list-experiment.tri" + }, + { + "name": "tricu-lib-source-staging-experiment", + "version": "0.6.0", + "manifest": "db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790", + "hash": "09d6d5f32aa2f27b2d1864e7ee19955df8692190821072b67bbf75d3babe146a", + "title": "lib/source/staging-experiment.tri" + }, + { + "name": "tricu-lib-source-unit", + "version": "0.6.0", + "manifest": "1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9", + "hash": "16de0d621776b79ff676b9415405b19e3088a2409d51f2efac850acca249b119", + "title": "lib/source/unit.tri" + }, + { + "name": "tricu-lib-arboricx-inspect", + "version": "0.6.0", + "manifest": "fe9b420e1e776fa1dda7b77857e322c0509ed308bb18441b76a3b03023f93468", + "hash": "f571888c5aa3f7a91e9d5d6c45c674851ea8e3598514ad8d535fd3e9056d0314", + "title": "lib/arboricx/inspect.tri" + }, + { + "name": "tricu-lib-arboricx-streaming", + "version": "0.6.0", + "manifest": "6a3c9f666801c3d29aaa71e22400a5726e137ea2fe28693217db6160a9d65db1", + "hash": "c3639067b4bdb79daa66253b95202c490f7545652864e718d1d558de0128a66a", + "title": "lib/arboricx/streaming.tri" + }, + { + "name": "tricu-lib-handler-testing", + "version": "0.6.0", + "manifest": "4a7b39b868bc30f46460b5cee2bb225a9db665775353aa5c71aafa567de7881e", + "hash": "e56378259592a4867829ca5447904b30ac5e21bd496eb68ad4d872c1af973d69", + "title": "lib/handlers/testing.tri" + }, + { + "name": "tricu-lib-handler-upload", + "version": "0.6.0", + "manifest": "45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3", + "hash": "035a709a17feb361abac0844c73f9b5f3c664549e5d81567963c3193816f9808", + "title": "lib/handlers/upload.tri" + }, + { + "name": "tricu-lib-io-v2-objects", + "version": "0.6.0", + "manifest": "1ed153be17b754f1a058e419d84a39e2169d5d6518bb47e8ee8785b02860e424", + "hash": "bb3135842e3cebfe6bd10e0766a6711e4d46a8cf157af1dcc9f97e25795ac0a1", + "title": "lib/io-v2/objects.tri" + }, + { + "name": "tricu-lib-packages-base", + "version": "0.6.0", + "manifest": "fa9d4df0f82cac2754fdd3e92009cb48064856ff4cc5b18f092309aef7ff5ed4", + "hash": "f9f0ab0e44f6ae39479d74c6c66a2bdaba3f40bd26781c3ec95300c2ee3c07f3", + "title": "lib/packages/base.tri" + }, + { + "name": "tricu-lib-packages-io-v2", + "version": "0.6.0", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "title": "lib/packages/io-v2.tri" + }, + { + "name": "tricu-lib-packages-io", + "version": "0.6.0", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "title": "lib/packages/io.tri" + }, + { + "name": "tricu-lib-recursion-list", + "version": "0.6.0", + "manifest": "4c79b7ad124027147b8fddd98c2b66cfd776c36d915c95dfb1fbf9b31a51fb88", + "hash": "60afc41f04fa65130326eeed4ca9e128bee5e90c7041f9641bbf9cfe87c142cd", + "title": "lib/recursion/list.tri" + }, + { + "name": "tricu-lib-source-bundle-stable", + "version": "0.6.0", + "manifest": "a06948780686a4432f97ce0a7ab5f8ed66a535a6d5563275e5f32375a679cb59", + "hash": "3de401378f33dd8f93e9ca11c1c2bd73a458dabcd8d631a33ba17306f83d7d3b", + "title": "lib/source/bundle-stable.tri" + }, + { + "name": "tricu-lib-tools-format-tri", + "version": "0.6.0", + "manifest": "e8bad7dcea5944336083f5bfa3ada63d7542286fbdec0f34dfeb65e9879ba2fc", + "hash": "3bd4df273ab4e4599417a18210ec3ad39e2ace2f7df6c0b7cad62999c4cb52a2", + "title": "lib/tools/format-tri.tri" + }, + { + "name": "tricu-base", + "version": "0.7.0", + "hash": "f9f0ab0e44f6ae39479d74c6c66a2bdaba3f40bd26781c3ec95300c2ee3c07f3", + "manifest": "fa9d4df0f82cac2754fdd3e92009cb48064856ff4cc5b18f092309aef7ff5ed4", + "title": "Foundation: lists, Results and decimal formatting" + }, + { + "name": "tricu-file-io", + "version": "0.7.0", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "title": "File IO and hashing actions" + }, + { + "name": "arboricx-inspection", + "version": "0.7.0", + "hash": "f571888c5aa3f7a91e9d5d6c45c674851ea8e3598514ad8d535fd3e9056d0314", + "manifest": "fe9b420e1e776fa1dda7b77857e322c0509ed308bb18441b76a3b03023f93468", + "title": "Arboricx inspection: validate without execution" + }, + { + "name": "tricu-compiler", + "version": "0.7.0", + "hash": "890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa", + "manifest": "22fccc94ad2972cf4ae81c73cf6f2e13aa39c0c12456083cb9811242520c17da", + "title": "Compiler library (stable browser source-unit API; native helpers)" + }, + { + "name": "tricu-source-builder", + "version": "0.7.0", + "hash": "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4", + "manifest": "44ca2cddfc2940055f30a16a70ff847dbd12f52317c949752f196a88567d3e73", + "title": "Stable native application compiler (modules, imports and publication)" + }, + { + "name": "arboricx-server", + "version": "0.7.0", + "hash": "0a5d5d83f73a91959f8ff34a45643dae056456817c22569879e4675caf461086", + "manifest": "a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d", + "title": "Arboricx registry server (native sockets and filesystem)" + }, + { + "name": "tricu-http", + "version": "0.7.0", + "hash": "8b38dd5934bbc51f999e3a2da1ad33736991c7dd9aab5144b716611fc5a80810", + "manifest": "b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6", + "title": "HTTP parsing, responses and native socket handlers" + }, + { + "name": "tricu-intensional-contracts", + "version": "0.7.0", + "hash": "a96e8e66c1759e1893a70a849c68eda171baf833b651d152102a8a7013f6e608", + "manifest": "7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1", + "title": "Intensional contracts: tree shapes and numeric structure" + }, + { + "name": "tricu-graphs", + "version": "0.7.0", + "hash": "5d3381723aafa9c5e4c4c3a6975de9481eb3160cbe52aca24076900528ef2603", + "manifest": "7b798a3afae6dce07b718bed855c7209ec3b898bb70b379dece935c4033cf9b6", + "title": "Dependency ordering and cycle witnesses (validated small graphs)" + }, + { + "name": "tricu-parser", + "version": "0.7.0", + "hash": "18b24788592f74b9c564c30214dff0231cb86338661d944ab697e7ae4ca1103e", + "manifest": "14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d", + "title": "Parser combinators: diagnostics, consumption and commitment" + }, + { + "name": "tricu-math", + "version": "0.7.0", + "hash": "48194ad053a11151b87d129db717d01ea34381d9684281dfe876c41884508265", + "manifest": "7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db", + "title": "Binary arithmetic and ASCII classifiers (canonical naturals only)" + }, + { + "name": "tricu-lexer", + "version": "0.7.0", + "hash": "5ec6ccb4827f0548444e480c0e6196ddaa7a570a44ebc6a91a922d7e59bf3012", + "manifest": "816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b", + "title": "Located character scans and token positions" + }, + { + "name": "tricu-binary", + "version": "0.7.0", + "hash": "7b6b438b2e903d6364eed8e948a814a1ebf2d08dfa9960fd7c1b278222f8cf4e", + "manifest": "7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc", + "title": "Checked byte readers, prefix parsing and UTF-8 validation" + }, + { + "name": "tricu-patterns", + "version": "0.7.0", + "hash": "7a59f1f37047cadd22f97fb29e07f549e5f3a41b63b9af35e013669eb7a4893f", + "manifest": "703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e", + "title": "Ordered predicate/action dispatch and thunked conditionals" + }, + { + "name": "tricu-list-traversal", + "version": "0.7.0", + "hash": "60afc41f04fa65130326eeed4ca9e128bee5e90c7041f9641bbf9cfe87c142cd", + "manifest": "4c79b7ad124027147b8fddd98c2b66cfd776c36d915c95dfb1fbf9b31a51fb88", + "title": "Early-stopping list folds with retained input suffixes" + }, + { + "name": "tricu-formatter", + "version": "0.7.0", + "hash": "48ae69290f1858d9534fe1f60f7540ecaca466b6ec1f48a208c7b73981277fef", + "manifest": "05e75a9f4d3c40db786f64820b78774566b8d51d77d11e6e1e244dad9360568e", + "title": "Conservative tricu equation alignment (not an AST printer)" + }, + { + "name": "parser-demo", + "version": "0.7.0", + "hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e", + "manifest": "0c34bfdf800f8bd1b742e375a8f7c555c88d0c827268f684259433e834d94fed", + "title": "Parser combinators: complete input, prefixes and committed errors", + "demo": "parser" + }, + { + "name": "lexer-demo", + "version": "0.7.0", + "hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2", + "manifest": "ed99e054e9c862668e165f458d3afcdc56279f766eea7a3886d440aea7252b63", + "title": "Located scans: matched prefix, remainder and character positions", + "demo": "lexer" + }, + { + "name": "tricu-lib-arboricx", + "version": "0.7.0", + "manifest": "3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55", + "hash": "7e698de68504e56e6c93d7822b3fe8dca79fa7aadd30710be63a3b4710fda061", + "title": "lib/arboricx/arboricx.tri" + }, + { + "name": "tricu-lib-arboricx-common", + "version": "0.7.0", + "manifest": "0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4", + "hash": "c2595128cd7056a33b634abc4a67bb99736b9cda08b6788e2936d8cd51fbaff1", + "title": "lib/arboricx/common.tri" + }, + { + "name": "tricu-lib-arboricx-dispatch", + "version": "0.7.0", + "manifest": "eda078ba018de68718b2b7aa2d790578ac079cf98c963a78f7fcae5178fd2c19", + "hash": "3c7cc88a1e7d6f11892357e15647186fa400b4aa50c7dbe7b9beb259b6ed48c2", + "title": "lib/arboricx/dispatch.tri" + }, + { + "name": "tricu-lib-arboricx-handlers", + "version": "0.7.0", + "manifest": "ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c", + "hash": "6d156c31a1ecb7762df9fd94702dbf08773d81ca739b7cf806bc12ffe30ff10a", + "title": "lib/arboricx/handlers.tri" + }, + { + "name": "tricu-lib-arboricx-manifest", + "version": "0.7.0", + "manifest": "2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa", + "hash": "ab19d9010aba0c7fcb6995c1c096872dc3bfb22a5798407011c01adfb7c57d8a", + "title": "lib/arboricx/manifest.tri" + }, + { + "name": "tricu-lib-arboricx-nodes", + "version": "0.7.0", + "manifest": "d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23", + "hash": "832388f8b0380e7f2bce6ca3e79f38ee6236c1d25232ea2cd5b6119c6eb41fba", + "title": "lib/arboricx/nodes.tri" + }, + { + "name": "tricu-lib-arboricx-server", + "version": "0.7.0", + "manifest": "a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d", + "hash": "0a5d5d83f73a91959f8ff34a45643dae056456817c22569879e4675caf461086", + "title": "lib/arboricx/server.tri" + }, + { + "name": "tricu-lib-arboricx-streaming-v2", + "version": "0.7.0", + "manifest": "7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17", + "hash": "20794e110733d119d3df5ae5a30a6f4917a12a8d4b4b4e55a887eea2148bc78d", + "title": "lib/arboricx/streaming-v2.tri" + }, + { + "name": "tricu-lib-base", + "version": "0.7.0", + "manifest": "bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81", + "hash": "2eb5dd5e3a372ef4e5c1ed612ca73eff2b0c9f97ebb651fb7ab22d5db9a15ca3", + "title": "lib/base.tri" + }, + { + "name": "tricu-lib-binary", + "version": "0.7.0", + "manifest": "7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc", + "hash": "7b6b438b2e903d6364eed8e948a814a1ebf2d08dfa9960fd7c1b278222f8cf4e", + "title": "lib/binary.tri" + }, + { + "name": "tricu-lib-bytes", + "version": "0.7.0", + "manifest": "16cff955cb9ab44063ed21651002d209ef7d3592fa005cd3dc32e287d6ca5b68", + "hash": "8ee2c0b4fec670afc9db5552e6907ace29ae2c0bda07897e70d451f9bb56160c", + "title": "lib/bytes.tri" + }, + { + "name": "tricu-lib-conversions", + "version": "0.7.0", + "manifest": "78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e", + "hash": "0ad0c17feaf6a8241ade01283210a1cded009eb4dd1cf99adcd31ea44017afba", + "title": "lib/conversions.tri" + }, + { + "name": "tricu-lib-graph", + "version": "0.7.0", + "manifest": "7b798a3afae6dce07b718bed855c7209ec3b898bb70b379dece935c4033cf9b6", + "hash": "5d3381723aafa9c5e4c4c3a6975de9481eb3160cbe52aca24076900528ef2603", + "title": "lib/graph.tri" + }, + { + "name": "tricu-lib-handler-artifacts", + "version": "0.7.0", + "manifest": "fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8", + "hash": "e07c225c29eef3588dbb65cf0178e8b82a7d774786d0e392052877f7c4aecf5f", + "title": "lib/handlers/artifacts.tri" + }, + { + "name": "tricu-lib-handler-log", + "version": "0.7.0", + "manifest": "8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31", + "hash": "320fbebc4604cdff3c3a3dba649966018ffb0c65241666108be05723b33284ad", + "title": "lib/handlers/log.tri" + }, + { + "name": "tricu-lib-handlers", + "version": "0.7.0", + "manifest": "15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d", + "hash": "50d7c2af3c4d5aec428d9f97226992c7b12a72ec67dda5540e2e7d83d3ed0715", + "title": "lib/handlers.tri" + }, + { + "name": "tricu-lib-http", + "version": "0.7.0", + "manifest": "b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6", + "hash": "8b38dd5934bbc51f999e3a2da1ad33736991c7dd9aab5144b716611fc5a80810", + "title": "lib/http.tri" + }, + { + "name": "tricu-lib-intensional", + "version": "0.7.0", + "manifest": "7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1", + "hash": "a96e8e66c1759e1893a70a849c68eda171baf833b651d152102a8a7013f6e608", + "title": "lib/intensionalContracts.tri" + }, + { + "name": "tricu-lib-io-v2-artifacts", + "version": "0.7.0", + "manifest": "45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3", + "hash": "0ce352486ef2b94dc5aed1a40d5339ab0e90c41bb98e9959e26bcc2fb1e7590a", + "title": "lib/io-v2/artifacts.tri" + }, + { + "name": "tricu-lib-io-v2-http", + "version": "0.7.0", + "manifest": "87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52", + "hash": "1a1f3ea6c15e88e5929f151df64687a3dcb28b2d5f2091acb89c39081d4fe28d", + "title": "lib/io-v2/http-transport.tri" + }, + { + "name": "tricu-lib-io-v2-storage", + "version": "0.7.0", + "manifest": "e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4", + "hash": "e1376f99435b5869d7f53b489a5d7552b382f670710a6e0e970f13ddc330948b", + "title": "lib/io-v2/storage.tri" + }, + { + "name": "tricu-lib-io-v2-text", + "version": "0.7.0", + "manifest": "11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df", + "hash": "f056d6c372cd700f325f6077a043492c9f4fb5d59e179ddfc00f69e608c7d969", + "title": "lib/io-v2/text.tri" + }, + { + "name": "tricu-lib-io-v2", + "version": "0.7.0", + "manifest": "3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b", + "hash": "d9f2858d088128d306525252c74025eb49565187b39da7e88c88b758cc4035d1", + "title": "lib/io-v2.tri" + }, + { + "name": "tricu-lib-io", + "version": "0.7.0", + "manifest": "3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b", + "hash": "25a9d7530dbb272a32516897c08f0672dd84676da8c3bc0828bcf2824ca5b5e9", + "title": "lib/io.tri" + }, + { + "name": "tricu-lib-lexer", + "version": "0.7.0", + "manifest": "816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b", + "hash": "5ec6ccb4827f0548444e480c0e6196ddaa7a570a44ebc6a91a922d7e59bf3012", + "title": "lib/lexer.tri" + }, + { + "name": "tricu-lib-native-math", + "version": "0.7.0", + "manifest": "7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db", + "hash": "48194ad053a11151b87d129db717d01ea34381d9684281dfe876c41884508265", + "title": "lib/math/native.tri" + }, + { + "name": "tricu-lib-parser", + "version": "0.7.0", + "manifest": "14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d", + "hash": "18b24788592f74b9c564c30214dff0231cb86338661d944ab697e7ae4ca1103e", + "title": "lib/parser.tri" + }, + { + "name": "tricu-lib-patterns", + "version": "0.7.0", + "manifest": "703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e", + "hash": "7a59f1f37047cadd22f97fb29e07f549e5f3a41b63b9af35e013669eb7a4893f", + "title": "lib/patterns.tri" + }, + { + "name": "tricu-lib-prelude", + "version": "0.7.0", + "manifest": "6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823", + "hash": "00ebc0925d110a0733709f2aac0dedf4de7b59f33ac3eee107965496806cd77d", + "title": "lib/prelude.tri" + }, + { + "name": "tricu-lib-socket", + "version": "0.7.0", + "manifest": "f43fa8a321b5cd25954941270ea7f56d68115c77fac007f4e899dbc221f2cd38", + "hash": "50d923a5471cc42bff0a82da06b21ab6616ce90e76e94c3fa3bf4f0ab70c49bd", + "title": "lib/socket.tri" + }, + { + "name": "tricu-lib-source-ast", + "version": "0.7.0", + "manifest": "c6579f89e47a16694f1c08fe9825b183d8bc272e23cd744eb954c106b689c32f", + "hash": "af66062931c5de826bd336625d53c1c354fe6cc736e56e247ae550c542f0fa8a", + "title": "lib/source/ast.tri" + }, + { + "name": "tricu-lib-source-build-stable", + "version": "0.7.0", + "manifest": "d4c7521ff4bfea552124779a06ae3616e0dbbb3f00f711903b6f0f85902268df", + "hash": "684497e21954bc0392fab20009d93efafb4ed15315ae94e345f7e5c0530d0a13", + "title": "lib/source/build-stable.tri" + }, + { + "name": "tricu-lib-source-build", + "version": "0.7.0", + "manifest": "f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74", + "hash": "3bd031b7077f8cfe87da22b1fb5960fbf911fdaa5756821185b6431bc74be43b", + "title": "lib/source/build.tri" + }, + { + "name": "tricu-lib-source-bundle", + "version": "0.7.0", + "manifest": "2937e908e943ffb69470824d06805374b91301ae73b3b7eb1cb3cf2cf32b011f", + "hash": "925989ee208fe9cfd108c6093da5648b87ace44e134c7b28b0772eb5a5c540fc", + "title": "lib/source/bundle.tri" + }, + { + "name": "tricu-lib-source-compile", + "version": "0.7.0", + "manifest": "fb41d71410ad8fb9c0da35a5ab0a6b782b9814bd7629737b710b7404648939cd", + "hash": "ec65f6eac30f1487dc64e519ae0be9769695edbff58e529719424a95059c6875", + "title": "lib/source/compile.tri" + }, + { + "name": "tricu-lib-source-core", + "version": "0.7.0", + "manifest": "83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2", + "hash": "d7105ef477224dfb10ad2773e54744cb3f87ddf304eb753d7cb9ba40ea9cc3df", + "title": "lib/source/core.tri" + }, + { + "name": "tricu-lib-source-equations", + "version": "0.7.0", + "manifest": "568c5d782a847e4f8d6b7061d51d34fc527a466b872ede27032104293df3bf8f", + "hash": "62f77e46a310ae6dbfcd73d096126f0f6a16c61b98842d826a0b26783f733981", + "title": "lib/source/equations.tri" + }, + { + "name": "tricu-lib-source-export-cache", + "version": "0.7.0", + "manifest": "adb94daf05edaab172bf385632665cf03122ff71a288199243062f0a6c72b573", + "hash": "ef1971af4ec38386365ef2fc2a9e4fe05f2dd8af3c8ca887a91789d9959a0ed2", + "title": "lib/source/export-cache.tri" + }, + { + "name": "tricu-lib-source-index", + "version": "0.7.0", + "manifest": "c2946fe3ac0daf1a06d6f7deab9f7ad08d679c241b04ad9c0c6d3b6038cf091d", + "hash": "1f18232479e776982515edc3fe2c0f39fad79e2b428c2ff040d92edf5441ac6a", + "title": "lib/source/index.tri" + }, + { + "name": "tricu-lib-compiler-io", + "version": "0.7.0", + "manifest": "5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90", + "hash": "4872d3ee67db34a9ce6844e5a80befe217d6e248441d875d335da0e706e2b501", + "title": "lib/source/io-v2.tri" + }, + { + "name": "tricu-lib-source-lexer", + "version": "0.7.0", + "manifest": "4cc4ed214d6e474b339c970d04cc09ced20c6472758b58897aabbd6f3a5bbb42", + "hash": "b75e428ce6d587893829792a87c3a374993d22c900eea793f2713e822440cb9f", + "title": "lib/source/lexer.tri" + }, + { + "name": "tricu-lib-source-lock", + "version": "0.7.0", + "manifest": "4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad", + "hash": "cdda8a8efdd40b9f2d5beac446646831d11a6f1cf916a40d68f8064b90dda45a", + "title": "lib/source/lock.tri" + }, + { + "name": "tricu-lib-source-map", + "version": "0.7.0", + "manifest": "5b59e01e15cb66ae42014dea08143de5856b4ddc38858057ba57e44eebdca91d", + "hash": "1bced95d1d42116194b26c53779cc68900fd7c03263266603966243ae91f8545", + "title": "lib/source/map.tri" + }, + { + "name": "tricu-lib-source-module", + "version": "0.7.0", + "manifest": "c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e", + "hash": "54d1e5652a001bf19668bda2f660e7ede120ce6e8670e02a95daf18469ab9d0d", + "title": "lib/source/module.tri" + }, + { + "name": "tricu-lib-source-object", + "version": "0.7.0", + "manifest": "c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f", + "hash": "eb3c997bcd9d2cd2be93191865894f9627946462526ed3cffe3fbbdad6e3f682", + "title": "lib/source/object.tri" + }, + { + "name": "tricu-lib-source-order", + "version": "0.7.0", + "manifest": "1d55b1616e892be989d2c987099c84bef61686fc67f6a3eb5b1a373626b0ca5e", + "hash": "7c5ec2c441d6ab4ef23edc3e9941dbf7bb4a7718d9e6e93f8826171e3f92f3bc", + "title": "lib/source/order.tri" + }, + { + "name": "tricu-lib-source-parser", + "version": "0.7.0", + "manifest": "425b921ef62e99dd93f6d3389c82fdbb583eaeb857975717441c5469f98f3a73", + "hash": "9f63810fee2e20762f1a198535448a2187a90065a35428ec82a11aad85575643", + "title": "lib/source/parser.tri" + }, + { + "name": "tricu-lib-source-pattern", + "version": "0.7.0", + "manifest": "d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123", + "hash": "440618420eda88e9d8867ac5f99ade97dd8d678c44aa120995a96062e57c2045", + "title": "lib/source/pattern.tri" + }, + { + "name": "tricu-lib-source-program", + "version": "0.7.0", + "manifest": "447aa7d321344146772a2693e83b2ab87a0058e281c42780e1bb5b24e0ea995d", + "hash": "070c4d26248d75ce9d1d66c19a4a7ad92558b9e14a837791b72f50e07afc5463", + "title": "lib/source/program.tri" + }, + { + "name": "tricu-lib-source-resolve", + "version": "0.7.0", + "manifest": "ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe", + "hash": "2f6f3a1a9615134165e44ebb529d73608983f421bf2b483bc3f11e85cad27fa8", + "title": "lib/source/resolve.tri" + }, + { + "name": "tricu-lib-source-stable", + "version": "0.7.0", + "manifest": "5189315a72af93ee9fe3dbc8c73e07e97b980b4c91b9c2f6244d447461db55dd", + "hash": "3670141b6ee280efe14f6f0dd2ef9469560cdbfaa08d2510ca37ddc9802fd18d", + "title": "lib/source/stable.tri" + }, + { + "name": "tricu-lib-source-staged-conditional-experiment", + "version": "0.7.0", + "manifest": "43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682", + "hash": "3f8b3fc627479635890fa038e5fbac100db0d3014ed2f63c41ce1c4f856fb9bf", + "title": "lib/source/staged-conditional-experiment.tri" + }, + { + "name": "tricu-lib-source-staged-list-experiment", + "version": "0.7.0", + "manifest": "c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98", + "hash": "275c92fc65c5f9ae4caa0a5d4183c5ccd6055c815b17f82da4b952761f8ad987", + "title": "lib/source/staged-list-experiment.tri" + }, + { + "name": "tricu-lib-source-staging-experiment", + "version": "0.7.0", + "manifest": "db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790", + "hash": "09d6d5f32aa2f27b2d1864e7ee19955df8692190821072b67bbf75d3babe146a", + "title": "lib/source/staging-experiment.tri" + }, + { + "name": "tricu-lib-source-unit", + "version": "0.7.0", + "manifest": "1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9", + "hash": "16de0d621776b79ff676b9415405b19e3088a2409d51f2efac850acca249b119", + "title": "lib/source/unit.tri" + }, + { + "name": "tricu-lib-arboricx-inspect", + "version": "0.7.0", + "manifest": "fe9b420e1e776fa1dda7b77857e322c0509ed308bb18441b76a3b03023f93468", + "hash": "f571888c5aa3f7a91e9d5d6c45c674851ea8e3598514ad8d535fd3e9056d0314", + "title": "lib/arboricx/inspect.tri" + }, + { + "name": "tricu-lib-arboricx-streaming", + "version": "0.7.0", + "manifest": "7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17", + "hash": "08e1b5eb98d8314fdea7216819f0481df4354ee7a56192e692828f60e2b759a2", + "title": "lib/arboricx/streaming.tri" + }, + { + "name": "tricu-lib-handler-testing", + "version": "0.7.0", + "manifest": "4a7b39b868bc30f46460b5cee2bb225a9db665775353aa5c71aafa567de7881e", + "hash": "e56378259592a4867829ca5447904b30ac5e21bd496eb68ad4d872c1af973d69", + "title": "lib/handlers/testing.tri" + }, + { + "name": "tricu-lib-handler-upload", + "version": "0.7.0", + "manifest": "45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3", + "hash": "035a709a17feb361abac0844c73f9b5f3c664549e5d81567963c3193816f9808", + "title": "lib/handlers/upload.tri" + }, + { + "name": "tricu-lib-io-v2-objects", + "version": "0.7.0", + "manifest": "1ed153be17b754f1a058e419d84a39e2169d5d6518bb47e8ee8785b02860e424", + "hash": "bb3135842e3cebfe6bd10e0766a6711e4d46a8cf157af1dcc9f97e25795ac0a1", + "title": "lib/io-v2/objects.tri" + }, + { + "name": "tricu-lib-packages-base", + "version": "0.7.0", + "manifest": "fa9d4df0f82cac2754fdd3e92009cb48064856ff4cc5b18f092309aef7ff5ed4", + "hash": "f9f0ab0e44f6ae39479d74c6c66a2bdaba3f40bd26781c3ec95300c2ee3c07f3", + "title": "lib/packages/base.tri" + }, + { + "name": "tricu-lib-packages-io-v2", + "version": "0.7.0", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "title": "lib/packages/io-v2.tri" + }, + { + "name": "tricu-lib-packages-io", + "version": "0.7.0", + "manifest": "804da21f47bd43c3921c775237c3898b92fea4961cc9fd8e3dcebbedb34e9296", + "hash": "a17edf554730f72d60e545eed6fac3c099790186f32b0a4f7fd4f1524b9ed03d", + "title": "lib/packages/io.tri" + }, + { + "name": "tricu-lib-recursion-list", + "version": "0.7.0", + "manifest": "4c79b7ad124027147b8fddd98c2b66cfd776c36d915c95dfb1fbf9b31a51fb88", + "hash": "60afc41f04fa65130326eeed4ca9e128bee5e90c7041f9641bbf9cfe87c142cd", + "title": "lib/recursion/list.tri" + }, + { + "name": "tricu-lib-source-bundle-stable", + "version": "0.7.0", + "manifest": "a06948780686a4432f97ce0a7ab5f8ed66a535a6d5563275e5f32375a679cb59", + "hash": "3de401378f33dd8f93e9ca11c1c2bd73a458dabcd8d631a33ba17306f83d7d3b", + "title": "lib/source/bundle-stable.tri" + }, + { + "name": "tricu-lib-tools-format-tri", + "version": "0.7.0", + "manifest": "05e75a9f4d3c40db786f64820b78774566b8d51d77d11e6e1e244dad9360568e", + "hash": "48ae69290f1858d9534fe1f60f7540ecaca466b6ec1f48a208c7b73981277fef", + "title": "lib/tools/format-tri.tri" } ] } diff --git a/ext/demo-site/parser-demo.json b/ext/demo-site/parser-demo.json @@ -3,7 +3,7 @@ "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export parseNames\n!export parsePrefix\n\n-- Lowercase names separated by commas. Try \"red,green\", \"red,,blue\",\n-- \"red,\" and \"red green\". A comma commits to another name.\n-- parseNames requires the entire input; parsePrefix exposes any remainder.\n-- Edit the parser or labels below; valid edits compile locally.\n-- Imports pin complete library interfaces, fetched only when compiling.\nnameParser = parserSatisfy (c : N.inRange c 97 122)\n |> parserSome\n |> parserLabel \"lowercase name\"\ncommaParser = parserThen (parserExpect 44) parserCommit\nnamesPrefix = parserSepBy1 nameParser commaParser\nnamesComplete = parserBefore namesPrefix <| parserLabel \"end of input\" parserEof\n\nreport reply = parserMatchReply\n (diagnostic rest consumed committed : [\n [\"status\" 0 \"error\"]\n [\"expected\" 0 (parserDiagnosticExpected diagnostic)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n (names rest consumed committed : [\n [\"status\" 0 \"ok\"]\n [\"names\" 0 (intercalate \", \" names)]\n [\"count\" 1 (length names)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n reply\nparseNames text = namesComplete text |> report\nparsePrefix text = namesPrefix text |> report\n", "hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e", "mode": "pure", - "compilerSha256": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e", + "compilerSha256": "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4", "sourceSha256": "9c005993cf43c07ec5c7e4f7f420b2f63c66e605d9522ab4b7e044de5e6cb8df", "haskellCases": [ { diff --git a/ext/demo-site/prepare-libraries.py b/ext/demo-site/prepare-libraries.py @@ -23,15 +23,38 @@ from fixture_sources import source_rows, verify, current_mismatches from shipped_compiler import verify as verify_compiler -def library_sources(): +def workspace_aliases(text): + """Canonical authored aliases; filesystem coverage still supplies all sources.""" + rows = text.splitlines() + if not rows or rows[0] != 'tricu.workspace.v1': + raise ValueError('Invalid workspace header') + aliases, paths = set(), {} + for line in rows[1:]: + if not line.strip() or line.startswith('#'): + continue + fields = line.split('\t') + if len(fields) != 2: + raise ValueError('Invalid workspace row') + name, path = fields + if (not re.fullmatch(r'[A-Za-z_][A-Za-z0-9_-]*', name) or name in aliases or + path in paths or Path(path).is_absolute() or '..' in Path(path).parts): + raise ValueError('Invalid or duplicate workspace alias/path') + aliases.add(name); paths[path] = name + return paths + + +def library_sources(workspace_text=None): """Coverage is filesystem-derived; newly added libraries cannot be omitted.""" fixtures = ROOT / 'test/fixtures' retained = {row[3]: row for row in source_rows(fixtures)} + declared = workspace_aliases((ROOT / 'tricu.workspace').read_text() if workspace_text is None else workspace_text) result = [] for path in sorted((ROOT / 'lib').rglob('*.tri')): relative = str(path.relative_to(ROOT)) row = retained.get(relative) - alias = row[2] if row else re.sub(r'[^A-Za-z0-9_]', '_', str(path.relative_to(ROOT / 'lib').with_suffix(''))) + if row and relative in declared and declared[relative] != row[2]: + raise ValueError('Fixture/workspace alias mismatch: ' + relative) + alias = row[2] if row else declared.get(relative, re.sub(r'[^A-Za-z0-9_]', '_', str(path.relative_to(ROOT / 'lib').with_suffix('')))) result.append((alias, relative, row[0] if row else None)) if len({r[0] for r in result}) != len(result): raise ValueError('Library module alias collision') @@ -123,6 +146,7 @@ def main(): if cohort is not None: continue bundle = work / (alias + '.arboricx') + print('Compiling ' + relative + ' as ' + alias, flush=True) run('arboricx', 'compile', '--file', workspace / (alias + '.tri'), '--store', work / 'store', '--toolchain', compiler_manifest, '--tool-store', work / 'tools', '--output', bundle, '--compile-timeout', '270') diff --git a/ext/demo-site/prepare.py b/ext/demo-site/prepare.py @@ -216,7 +216,7 @@ def main(): bundle_hash = digest(bundle) package_bytes[bundle_hash] = bundle cases = [] - def check(export, arguments, expected, terminal='', line=None): + def check(export, arguments, expected, terminal='', line=None, eof=False): argv = [] for i, argument in enumerate(arguments): path = temp / f'argument{i}.tree' @@ -225,7 +225,7 @@ def main(): output = run('arboricx', 'run', '--module', module_pin, '--name', export, '--store', store, '--format', 'ternary', *argv, *(['--io'] if setting['mode'] == 'io' else []), - input=(line + '\n').encode() if line is not None else None) + input=b'' if eof else (line + '\n').encode() if line is not None else None) assert output == terminal + expected, (name, export, arguments, output) captured = dict(export=export, arguments=arguments, input=line, terminal=terminal, result=expected) cases.append(captured) @@ -247,6 +247,7 @@ def main(): elif name == 'greet': for value in ('James', '', 'λ雪🙂', '<img src=x onerror=alert(1)>'): check('main', [], of_string(value), f'What is your name?\nHello, {value}!\n', value) + check('main', [], of_string(''), 'What is your name?\nEnd of input.\n', eof=True) elif name == 'microscope': for tree, shape, size in [('0', 'Leaf', 1), ('10', 'Stem', 2), ('200', 'Fork', 3), ('21100', 'Fork', 5)]: check('shape', [tree], of_string(shape)) diff --git a/ext/demo-site/src/app.ts b/ext/demo-site/src/app.ts @@ -1,3 +1,4 @@ +import { terminalView } from './io-v2-terminal-view.js'; import { SourceEditor } from './source-editor.js'; import { definitionInspector } from './definition-inspector.js'; import { exampleGroups, exampleGroup, libraryLabel } from './program-catalogue.js'; @@ -31,7 +32,8 @@ let sourceAvailable = false, editRevision = 0, composing = false, idleDue = 0, c let entries: CatalogueEntry[] = [], releases: Release[] = [], selected: CatalogueEntry | undefined; let selection: { hash: BundleHash; localId?: string } | undefined, versionPackage: string | undefined; let preferredExport: string | undefined, currentName = '', publishedNames: string[] = []; -let worker: Worker | undefined, pending: { kind: 'input' | 'step'; id: RequestId } | undefined; +let worker: Worker | undefined, pending: { id: RequestId } | undefined; +let ioVersion: 0 | 2 | undefined, v2Run: string | undefined, v2Widget: ReturnType<typeof terminalView> | undefined, stoppingV2 = false; let operation: Operation | undefined, idleTimer: ReturnType<typeof setTimeout> | undefined; let compilerTimer: ReturnType<typeof setInterval> | undefined; const deadline = new ActiveClock(() => finish(operationTimeout(operation ?? 'loading'), true)); @@ -58,14 +60,14 @@ const definitions = definitionInspector(sourceEditor, (id, name, output) => { run(name, output, step) { if (busy || !worker || draft && (!compiled || sourceEditor.getSource() !== compiledSource || editRevision !== compiledRevision)) return; currentName = name; operation = 'running'; controls(true); deadline.reset(Infinity); deadline.start(); diagnostic(); + ioVersion = 0; stoppingV2 = false; el('status').textContent = `Running IO · ${name}`; worker.postMessage({ type: 'run-io', name, output, step }); }, stop: () => stopButton.click(), - resume(value) { + resume() { if (!worker || !pending) return; - if (value === undefined && pending.kind !== 'step' || value !== undefined && pending.kind !== 'input') return; - worker.postMessage(value === undefined ? { type: 'resume', id: pending.id } : { type: 'resume', id: pending.id, value }); + worker.postMessage({ type: 'resume', id: pending.id }); pending = undefined; deadline.start(); }, }); @@ -118,10 +120,11 @@ el('stored-download').onclick = () => { worker.postMessage({type:'export',export:el<HTMLSelectElement>('stored-definition').value,format:el<HTMLSelectElement>('stored-format').value}); }; function dispose(): void { + v2Widget?.abandon(); v2Widget = undefined; v2Run = undefined; ioVersion = undefined; stoppingV2 = false; cancelIdleCompile(); stopCompilerActivity(); deadline.pause(); operation = undefined; worker?.terminate(); worker = undefined; compiled = false; compiledSource = undefined; compiledRevision = undefined; pending = undefined; controls(false); } -function idle(): void { stopCompilerActivity(); deadline.pause(); operation = undefined; pending = undefined; controls(false); } +function idle(): void { ioVersion = undefined; stoppingV2 = false; stopCompilerActivity(); deadline.pause(); operation = undefined; pending = undefined; controls(false); } function finish(message: string, error = false, cancelled = false): void { const previous = operation; if (error) message = operationFailure(operation, message); @@ -133,6 +136,10 @@ function finish(message: string, error = false, cancelled = false): void { if (!loaded && !draft) el('package-info').textContent = `Package not loaded. ${message}`; } stopButton.onclick = () => { + if (operation === 'running' && ioVersion !== undefined) { + if (!stoppingV2) { stoppingV2 = true; worker?.postMessage({ type: 'io-stop' }); } + stopButton.disabled = true; el('status').textContent = 'Cancelling; waiting for cleanup…'; return; + } imports.cancel(); finish(draft ? 'Stopped. Compile the draft again to inspect or run it.' : 'Stopped. Reload the package to inspect or run it.', false, true); el('status').textContent = 'Stopped'; @@ -159,9 +166,22 @@ function receive(data: CompilerMessage): void { } definitions.event(data); switch (data.type) { + case 'io-version': ioVersion = data.version; break; + case 'v2-open': { + if (v2Widget) throw new Error('Overlapping terminal runs'); + const current = worker; v2Run = data.run; + v2Widget = terminalView(el('terminal-v2'), data.run, + reply => { if (worker === current && v2Run === reply.run) current?.postMessage({ type: 'v2-command', command: { run: reply.run, kind: 'reply', reply } }); }, + () => stopButton.click()); + break; + } + case 'v2-event': if (data.event.run === v2Run) v2Widget?.handle(data.event); break; + case 'v2-close': + if (data.run === v2Run) { ioVersion = undefined; if (data.settled) v2Widget?.finish(); else v2Widget?.abandon(); v2Widget = undefined; v2Run = undefined; el('status').textContent = data.settled ? 'IO settled; interpreting returned value' : 'Runtime lost'; } + break; case 'state': - if (data.state === 'running') { pending = undefined; deadline.start(); if (operation === 'running') el('status').textContent = `Running IO · ${currentName}`; } - else { deadline.pause(); pending = {kind:data.state,id:data.id}; el('status').textContent = data.state === 'input' ? 'Waiting for input' : 'Paused before action'; } + if (data.state === 'running') { pending = undefined; deadline.start(); if (operation === 'running' && !stoppingV2) el('status').textContent = `Running IO · ${currentName}`; } + else { deadline.pause(); pending = {id:data.id}; el('status').textContent = 'Paused before action'; } break; case 'done': idle(); el('status').textContent = 'Finished'; el('compiler-stats').textContent = `${compilationStats} · IO · ${runtimeStats(data.stats)}`; break; case 'error': @@ -385,11 +405,20 @@ const examples={ contracts:'id x =@nat? x\nanswer = id 42', pair:'pair a b = t a b\nanswer = pair 7 "hi"', definitions:'id = (x : x)\nanswer = id 42', - input:'io=t "tricuIO" (t 1 (t 11 t))', }; for(const button of document.querySelectorAll<HTMLButtonElement>('[data-compiler-example]'))button.onclick=()=>{ const math=packageReleases(releases,'tricu-lib-native-math')[0]; - const source=button.dataset['compilerExample']==='module'&&math?`!import ${math.manifest} as N\n!export answer\nanswer = N.add 40 2`:examples[button.dataset['compilerExample'] as keyof typeof examples]; + const choice=button.dataset['compilerExample']; + const io=packageReleases(releases,'tricu-lib-io')[0],text=packageReleases(releases,'tricu-lib-io-v2-text')[0]; + const source=choice==='input'&&io&&text?`!import ${io.manifest} as IO +!import ${text.manifest} as Text +!export io +io = IO.io <| IO.require IO.stdin (reader : + IO.require (Text.readLine (Text.lineCursor reader) 4096) (reply : case reply of + [["line" value _] _] = IO.pure value + [["eof"] _] = IO.pure "" + _ = IO.fail "Invalid line reply"))` + :choice==='module'&&math?`!import ${math.manifest} as N\n!export answer\nanswer = N.add 40 2`:examples[choice as keyof typeof examples]; if(!source)return; definitions.reset();dispose();diagnostic();chooseProgram('');scratch=draft=true;originalSource=undefined;loaded=false;selection=undefined;selected=undefined;editRevision++; sourceEditor.dom.hidden=false;el('source-notes').hidden=true;sourceEditor.replaceSource(source);controls(false);el('package-info').textContent='Scratch program · no published bundle selected.';showRelease();el('source-label').textContent='Scratch program · explicit modules or contract helpers';el('status').textContent='Changes pending';queueCompile(); diff --git a/ext/demo-site/src/conformance/compiler-worker.ts b/ext/demo-site/src/conformance/compiler-worker.ts @@ -1,29 +1,19 @@ // Test-only JS evaluator driver; excluded from the production site. import { compileExpression } from './expression.js'; -import { resumeValue } from '../io.js'; import { environmentProfile } from '../packages.js'; import { BROWSER_BUDGET, collectionPolicy } from '../execution.js'; import { contractEnvironment } from '../contracts.js'; import { Graph, type NodeId } from '../graph.js'; import { COMPILER_BUNDLE_LIMIT, fetchBundle, fetchBytes, loadBundle } from '../bundle.js'; import { literals, makeLiteral, outputType, view, list, text } from '../codecs.js'; -import { runIO } from '../io.js'; import { COMPILER_EXPORT, compilerDescriptor, sourceLiteral, sourceText, compiledDefinitions } from '../compiler.js'; import type { CompilerClient, CompilerMessage } from './compiler-protocol.js'; -import type { RequestId } from '../protocol.js'; const scope = globalThis as unknown as { postMessage(message: CompilerMessage): void; onmessage: ((event: MessageEvent<unknown>) => void) | null }; const base = new URL('../../', import.meta.url); const send = (message: CompilerMessage): void => scope.postMessage(message); const running = (): void => send({ type: 'state', state: 'running' }); let phase: 'empty' | 'compiling' | 'ready' | 'running' | 'done' = 'empty'; let graph: Graph | undefined, exports: Map<string, NodeId> | undefined; -let requestId = 0; -let pending: { id: RequestId; kind: 'step' | 'input'; resolve(value: string): void; reject(error: Error): void } | undefined; -function request(kind: 'step' | 'input'): Promise<string> { - if (pending) throw new Error('Driver already waiting'); - const id = ++requestId as RequestId; - return new Promise((resolve, reject) => { pending = { id, kind, resolve, reject }; send({ type: 'state', state: kind, id }); }); -} async function compile(source: unknown, profileName?: string): Promise<void> { phase = 'compiling'; running(); const start = performance.now(); const budget = BROWSER_BUDGET; @@ -65,11 +55,7 @@ async function run(message: Extract<CompilerClient, { type: 'run' }>): Promise<v if (message.mode !== 'pure' && message.mode !== 'io') throw new Error('Unsupported execution mode'); const type = outputType(message.output), args = literals(message.arguments).map(value => makeLiteral(graph!, value)); let result = message.expression === undefined ? graph.apply(fn, args) : fn; - if (message.mode === 'io') result = await runIO(graph, result, { - async before(event) { send({ type: 'trace', phase: 'before', event }); if (message.step) await request('step'); running(); }, - after(event) { send({ type: 'trace', phase: 'after', event }); }, write(text) { send({ type: 'output', text }); }, - read() { return request('input'); }, running, - }, true, exports.values()); + if (message.mode === 'io') throw new Error('Reference graph IO execution is retired; use a native runtime'); const displayed = view(graph, result, type); graph.collect(exports.values()); // Drop this run's temporaries, retaining every definition. phase = 'ready'; send({ type: 'done', result: displayed, stats: graph.stats }); @@ -77,13 +63,7 @@ async function run(message: Extract<CompilerClient, { type: 'run' }>): Promise<v scope.onmessage = ({ data }) => { if (!data || typeof data !== 'object' || Array.isArray(data)) return; const message = data as Record<string, unknown>; - if (message['type'] === 'resume') { - if (!pending || message['id'] !== pending.id) return; - const value = resumeValue(pending.kind, message['value']); - if (value === undefined) return; - const waiter = pending; pending = undefined; waiter.resolve(value); - return; - } + let operation: Promise<void>; try { if (message['type'] === 'restore' && phase === 'empty') { diff --git a/ext/demo-site/src/conformance/worker.ts b/ext/demo-site/src/conformance/worker.ts @@ -1,28 +1,19 @@ // Test-only JS evaluator driver; excluded from the production site. import { sourceText } from '../compiler.js'; import { compileExpression } from './expression.js'; -import { resumeValue } from '../io.js'; import { publishedBundle } from '../published-bundle.js'; import { exportTree, treeFormat } from '../tree-export.js'; import { BROWSER_BUDGET, collectionPolicy, PACKAGE_COLLECTION } from '../execution.js'; import { Graph } from '../graph.js'; import { bundleHash, loadBundle, type Bundle, type BundleHash } from '../bundle.js'; import { literals, makeLiteral, outputType, view } from '../codecs.js'; -import { runIO } from '../io.js'; -import type { ClientMessage, WorkerMessage, RequestId } from '../protocol.js'; +import type { ClientMessage, WorkerMessage } from '../protocol.js'; const scope = globalThis as unknown as { postMessage(message: WorkerMessage): void; onmessage: ((event: MessageEvent<unknown>) => void) | null }; const send = (message: WorkerMessage): void => scope.postMessage(message); const running = (): void => send({ type: 'state', state: 'running' }); let phase: 'empty' | 'loading' | 'ready' | 'running' | 'done' = 'empty'; let graph: Graph | undefined, exports: Map<string, import('../graph.js').NodeId> | undefined; let stored: { hash: BundleHash; bundle: Bundle; bytes: Uint8Array } | undefined; -let requestId = 0; -let pending: { id: RequestId; kind: 'step' | 'input'; resolve(value: string): void; reject(error: Error): void } | undefined; -function request(kind: 'step' | 'input'): Promise<string> { - if (pending) throw new Error('Driver already waiting'); - const id = ++requestId as RequestId; - return new Promise((resolve, reject) => { pending = { id, kind, resolve, reject }; send({ type: 'state', state: kind, id }); }); -} async function load(hash: BundleHash, localId: string | undefined): Promise<void> { phase = 'loading'; running(); const loaded = await publishedBundle(hash, localId); @@ -42,24 +33,14 @@ async function run(message: Extract<ClientMessage, { type: 'run' }>): Promise<vo const type = outputType(message.output), args = literals(message.arguments).map(value => makeLiteral(graph!, value)); let result = message.expression === undefined ? graph.apply(fn, args) : fn; graph.collect([result]); // Exclusive one-run session: only this application remains owned. - if (message.mode === 'io') result = await runIO(graph, result, { - async before(event) { send({ type: 'trace', phase: 'before', event }); if (message.step === true) await request('step'); running(); }, - after(event) { send({ type: 'trace', phase: 'after', event }); }, - write(text) { send({ type: 'output', text }); }, read() { return request('input'); }, running, - }, true); + if (message.mode === 'io') throw new Error('Reference graph IO execution is retired; use a native runtime'); const displayed = view(graph, result, type); phase = 'done'; send({ type: 'done', result: displayed, stats: graph.stats }); } scope.onmessage = ({ data }) => { if (!data || typeof data !== 'object' || Array.isArray(data)) return; const message = data as Record<string, unknown>; - if (message['type'] === 'resume') { - if (!pending || message['id'] !== pending.id) return; - const value = resumeValue(pending.kind, message['value']); - if (value === undefined) return; - const waiter = pending; pending = undefined; waiter.resolve(value); - return; - } + if (message['type'] === 'download' && phase === 'ready' && stored) { send({ type: 'downloaded', hash: stored.hash, bytes: stored.bytes }); return; } diff --git a/ext/demo-site/src/definition-inspector.ts b/ext/demo-site/src/definition-inspector.ts @@ -7,7 +7,7 @@ import type { CompilerMessage } from './compiler-protocol.js'; interface IOControls { run(name: string, output: OutputType, step: boolean): void; stop(): void; - resume(value?: string): void; + resume(): void; } export { valueText as inspectionText } from './codecs.js'; @@ -30,16 +30,14 @@ export function definitionInspector(input: SourceEditor, request: (id: number, n actions.append(copy, download); card.append(actions, edit); edit.className = 'edit-definition-name'; edit.hidden = true; const ioPanel = document.createElement('section'); ioPanel.className = 'inline-io'; ioPanel.hidden = true; ioPanel.innerHTML = `<div class="inline-io-actions"><button type="button" id="run-io">Run IO</button><button type="button" id="stop-io" disabled>Stop</button></div> - <p class="hint">Recognized tricuIO v1 envelope. Only Run IO executes actions.</p> - <pre id="terminal" tabindex="0" aria-label="Program output"></pre> - <form id="input-form" hidden><label for="line-input">One line, up to 256 characters</label><div class="input-row"><input id="line-input" maxlength="512" autocomplete="off"><button>Send ↵</button></div><p id="input-error" role="status"></p></form> - <details><summary>IO details</summary><label><input id="step-mode" type="checkbox"> Pause before each IO action</label><button id="next" type="button" disabled>Next IO action</button> + <p class="hint">Recognized tricuIO v2 envelope. Only Run IO executes actions.</p> + <div id="terminal-v2"></div> + <details><summary>IO details</summary><label><input id="step-mode" type="checkbox"> Pause before each scheduler turn</label><button id="next" type="button" disabled>Next scheduler turn</button> <label for="io-return-format">Returned value (next run)</label><select id="io-return-format"><option value="tricu">tricu source</option><option value="string">Text</option><option value="number">Natural number</option><option value="values">List · text / numbers</option><option value="bool">Boolean</option><option value="bytes">Bytes</option><option value="triangle">△ source</option><option value="ternary">Ternary</option><option value="dag">DAG</option></select><ol id="trace"></ol></details>`; card.append(ioPanel); const get = <T extends HTMLElement>(id: string) => ioPanel.querySelector<T>('#' + id)!; const run = get<HTMLButtonElement>('run-io'), stop = get<HTMLButtonElement>('stop-io'); - const inputForm = get<HTMLFormElement>('input-form'), lineInput = get<HTMLInputElement>('line-input'); - const next = get<HTMLButtonElement>('next'), terminal = get('terminal'), trace = get('trace'); + const next = get<HTMLButtonElement>('next'), trace = get('trace'); const returnFormat = get<HTMLSelectElement>('io-return-format'), step = get<HTMLInputElement>('step-mode'); let saved: { text: string; filename: string } | undefined, ioDetected = false, ioRunning = false, suspended = false; function save(value: ValueView): void { @@ -64,17 +62,10 @@ export function definitionInspector(input: SourceEditor, request: (id: number, n }; run.onclick = () => { if (!selected || !ready || busy || !ioDetected) return; - ioRunning = true; clear(); lineInput.value = ''; get('input-error').textContent = ''; terminal.textContent = ''; trace.replaceChildren(); inputForm.hidden = true; + ioRunning = true; clear(); get('terminal-v2').replaceChildren(); trace.replaceChildren(); status.textContent = 'Running IO…'; io.run(selected, outputType(returnFormat.value), step.checked); }; stop.onclick = () => io.stop(); next.onclick = () => io.resume(); - inputForm.onsubmit = event => { - event.preventDefault(); const value = lineInput.value; - if (/[\r\n\uD800-\uDFFF]/u.test(value) || Array.from(value).length > 256) { - get('input-error').textContent = 'Enter one line of at most 256 Unicode scalar characters.'; return; - } - get('input-error').textContent = ''; inputForm.hidden = true; io.resume(value); lineInput.value = ''; - }; clear(); let selected: string | undefined, source = '', ready = false, busy = false, serial = 0; let pending: number | undefined, last = '', generation = 0, wasReady = false, scheduled = false; @@ -145,7 +136,7 @@ export function definitionInspector(input: SourceEditor, request: (id: number, n } function choose(name: string): void { if (selected === name && !card.hidden && !suspended) { hide(); return; } - selected = name; suspended = false; ioDetected = false; lineInput.value = ''; get('input-error').textContent = ''; inputForm.hidden = true; next.disabled = true; ioPanel.hidden = true; terminal.textContent = ''; trace.replaceChildren(); clear(); format.value = 'auto'; title.textContent = name; card.hidden = false; last = ''; result.textContent = ''; + selected = name; suspended = false; ioDetected = false; next.disabled = true; ioPanel.hidden = true; get('terminal-v2').replaceChildren(); trace.replaceChildren(); clear(); format.value = 'auto'; title.textContent = name; card.hidden = false; last = ''; result.textContent = ''; position(); schedule(); } function hide(): void { @@ -189,14 +180,13 @@ export function definitionInspector(input: SourceEditor, request: (id: number, n open(name: string): void { card.classList.toggle('external-definition', input.dom.hidden); if (input.dom.hidden) { card.style.top = ''; document.getElementById('package-definition-view')!.append(card); } else wrapper.append(card); choose(name); }, event(message: WorkerMessage | CompilerMessage): void { if (!ioRunning) return; - if (message.type === 'output') { terminal.textContent += message.text; terminal.scrollTop = terminal.scrollHeight; } - if (message.type === 'trace') { const row = document.createElement('li'); row.textContent = message.phase === 'before' ? `${message.event.step} ${message.event.name}: ${message.event.detail}` : `→ ${message.event.value}`; trace.append(row); } - if (message.type === 'state') { inputForm.hidden = message.state !== 'input'; next.disabled = message.state !== 'step'; status.textContent = message.state === 'input' ? 'Waiting for input' : message.state === 'step' ? 'Paused before action' : 'Running IO…'; if (message.state === 'input') lineInput.focus(); } - if (message.type === 'done') { ioRunning = false; inputForm.hidden = true; next.disabled = true; status.textContent = 'IO finished · Returned value'; save(message.result); } + if (message.type === 'trace') { const row = document.createElement('li'); row.textContent = `${message.event.step} ${message.event.name}: ${message.event.detail}`; trace.append(row); } + if (message.type === 'state') { next.disabled = message.state !== 'step'; status.textContent = message.state === 'step' ? 'Paused before action' : 'Running IO…'; } + if (message.type === 'done') { ioRunning = false; next.disabled = true; status.textContent = 'IO finished · Returned value'; save(message.result); } position(); }, // Stop suspends observation until the user reopens the definition. - stopped(message: string, suspend = false): void { suspended = suspend; ioRunning = false; inputForm.hidden = true; next.disabled = true; stop.disabled = true; status.textContent = message; }, + stopped(message: string, suspend = false): void { suspended = suspend; ioRunning = false; next.disabled = true; stop.disabled = true; status.textContent = message; }, reset(): void { suspended = false; ioRunning = false; ioDetected = false; ioPanel.hidden = true; clear(); selected = undefined; pending = undefined; last = ''; card.hidden = true; result.textContent = ''; position(); }, update, reply(message: InspectionReply): boolean { @@ -204,7 +194,7 @@ export function definitionInspector(input: SourceEditor, request: (id: number, n pending = undefined; if (!selected || !ready) return true; if (message.type === 'inspection-error') { status.textContent = `Cannot interpret this tree: ${message.message}`; clear(); } - else if ('io' in message && message.io) { ioDetected = true; ioPanel.hidden = false; status.textContent = 'IO program · tricuIO v1'; clear(); } + else if ('io' in message && message.io) { ioDetected = true; ioPanel.hidden = false; status.textContent = 'IO program · authenticated tricuIO envelope'; clear(); } else if ('result' in message) { status.textContent = format.value === 'auto' ? 'Auto → ' + (interpretationLabels[message.result.type] ?? message.result.type) : format.selectedOptions[0]!.textContent; save(message.result); } position(); return true; }, diff --git a/ext/demo-site/src/execution.ts b/ext/demo-site/src/execution.ts @@ -1,6 +1,7 @@ import type { Budget, CollectionPolicy } from './graph.js'; // Execution has no application node/work/frame ceilings. Browser memory remains -// finite; Stop terminates the owning Worker. GC thresholds are not budgets. +// finite. Pure/v1 Stop terminates the Worker; v2 Stop drains its structured +// session at scheduler boundaries. GC thresholds are not budgets. export const BROWSER_BUDGET: Readonly<Budget> = Object.freeze({ nodes: Infinity, fuel: Infinity, stack: Infinity }); export const PACKAGE_LOAD_MS = 60_000; // Export is bounded transport/rendering, not program execution. diff --git a/ext/demo-site/src/haskell-session.ts b/ext/demo-site/src/haskell-session.ts @@ -1,3 +1,4 @@ +import { drive, type Report } from './io-v2-driver.js'; import type { ModuleStage } from './module-stage.js'; import { moduleSnapshot } from './module-bundle.js'; import type { LocalModuleInput } from './local-modules.js'; @@ -13,12 +14,14 @@ import type { IOHost } from './io.js'; import type { RuntimeStats } from './runtime-backends.js'; interface Reply { text: string; linearBytes: number; compilerHash?: string | null; scopeNames?: string[]; } interface Client { + readonly signal: AbortSignal; disposed: boolean; request(message: object, timeout?: number | null): Promise<Reply>; dispose(reason?: string): void; } export class HaskellSession { private linearMemoryBytes = 0; + private v2Active = false; private constructor(private readonly client: Client) {} static async create(compiler: boolean): Promise<HaskellSession> { const module = await import(new URL('../runtime/haskell/client.mjs', import.meta.url).href) as { CompilerWorker: new () => Client }; @@ -38,6 +41,7 @@ export class HaskellSession { private names: string[] = []; get disposed(): boolean { return this.client.disposed; } private async request(message: object): Promise<Reply> { + if (this.v2Active && !['io-v2-session-turn', 'io-v2-public-view', 'io-v2-public-clear'].includes((message as { type: string }).type)) throw new Error('IO v2 session is pending'); const reply = await this.client.request(message, null); this.linearMemoryBytes = reply.linearBytes; return reply; @@ -45,6 +49,7 @@ export class HaskellSession { private async json<T>(message: object): Promise<T> { return JSON.parse((await this.request(message)).text) as T; } async load(bytes: Uint8Array, hash: BundleHash): Promise<void> { this.names = await this.json<string[]>({ type: 'load', bytes: new Uint8Array(bytes).buffer, hash }); } async compile(source: string, profileName?: string, localModules: LocalModuleInput[] = []): Promise<string[]> { + if (this.v2Active) throw new Error('IO v2 session is pending'); this.names = []; const reply = await this.request({ type: 'compile', source, localModules, base: new URL('../', import.meta.url).href, ...(profileName === undefined ? {} : { profileName }) }); @@ -98,26 +103,22 @@ export class HaskellSession { return this.json<ValueView>(message); }; if (mode === 'pure') return displayed(request); - type Event = { kind: string; token: number; step: number; name: string; detail: string; depth: number; value: string; text: string }; + let run: string | undefined; try { - let event = await this.json<Event>(request); - while (event.kind !== 'done') { - let value = ''; - switch (event.kind) { - case 'before': await host.before({ step: event.step, name: event.name, detail: event.detail, depth: event.depth }); break; - case 'read': value = await host.read(); break; - case 'write': host.write(event.text); break; - case 'after': host.after({ step: event.step, name: event.name, value: event.value }); break; - default: throw new Error('Unknown runtime IO event'); - } - host.running(); - event = await this.json<Event>({ type: 'io-resume', token: event.token, value }); - } - return await displayed({ type: 'io-view', output }); + const event = await this.json<{ kind: string; run: string }>({ ...request, selectIO: true, v2Step: host.v2?.stepping === true }); + if (event.kind !== 'v2') throw new Error('Unsupported tricuIO version'); + this.v2Active = true; run = event.run; + if (!host.v2) throw new Error('IO version 2 requires a structured terminal host'); + host.selected?.(2); + return await drive({ run, signal: this.client.signal, discard: () => this.dispose(), + turn: command => this.json<Report>({ type: 'io-v2-session-turn', ...command }), + view: () => displayed({ type: 'io-v2-public-view', run, output }), + }, host); } finally { // Release result/continuations even when decoding or a host hook fails. // A poisoned instance cannot be recovered by cancelling the interaction. - if (!this.disposed) await this.request({ type: 'io-cancel' }); + if (run !== undefined && !this.disposed) await this.request({ type: 'io-v2-public-clear', run }); + this.v2Active = false; } } stats(): RuntimeStats { return { backend: 'haskell-wasm', linearMemoryBytes: this.linearMemoryBytes }; } diff --git a/ext/demo-site/src/io-v2-core.ts b/ext/demo-site/src/io-v2-core.ts @@ -0,0 +1,233 @@ +// Private resumable tree interpreter. No public session dispatch or capabilities. +// Adapters must retain roots() between turns and keep temporary evaluator handles +// alive for a whole turn. Provider promises acknowledge physical completion; +// providers return deferred Result encoders, never task outcomes. Encoding runs +// in an interpreter turn, not in a promise/GC gap; cancelled encoders never run. +import { canonicalNat, decodeAction, ProtocolError, type Reader, type Action } from './io-v2-protocol.js'; +import { lifecycle, type Resource } from './io-v2-resources.js'; +import { HostWork } from './io-v2-host-work.js'; +import type { Token, Outcome, Diagnostics } from './io-v2-tasks.js'; +export interface Trees<H> extends Reader<H> { + readonly leaf: H; fork(a: H, b: H): H; stem(a: H): H; app(f: H, x: H): H; + natural(value: bigint): H; text(value: string): H; +} +type Request<H> = Extract<Action<H>, { kind: 'request' }>; +type Completion<H> = { kind: 'body'; encode: () => H } | { kind: 'release'; errors: Diagnostics<H> }; +type Body<H> = { action: H; frames: H[]; mode: 'runnable' | 'lexical' | 'join' | 'host' | 'done' }; +const operationErrors = new Set(['stale-task', 'join-cycle', 'out-of-scope', 'stale-handle', 'not-owner', + 'borrowed-resource', 'internal-resource', 'closed-handle', 'busy', 'duplicate-handle', 'closed-scope', 'invalid-argument']); +export class OperationError extends Error {} +function logical<T>(operation: () => T): T { + try { return operation(); } + catch (error) { + if (error instanceof Error && operationErrors.has(error.message)) throw new OperationError(error.message); + throw error; + } +} +export class Wire<H> { + constructor(readonly tree: Trees<H>, readonly identity: bigint) {} + pair(value: H): [H, H] { + const node = this.tree.observe(value).node; + if (node.kind !== 'fork') throw new OperationError('invalid-argument'); + return [node.left, node.right]; + } + list(values: readonly H[]): H { let out = this.tree.leaf; for (let i = values.length - 1; i >= 0; i--) out = this.tree.fork(values[i]!, out); return out; } + result(ok: boolean, value: H): H { return this.tree.fork(ok ? this.tree.stem(this.tree.leaf) : this.tree.leaf, this.tree.fork(value, this.tree.leaf)); } + problem(category: string, operation: string, detail = '', progress: 'none' | 'unknown' = 'none'): H { + return this.list([category, operation, detail, progress].map(s => this.tree.text(s))); + } + marker(name: string, key: number): H { return this.list([this.tree.text(name), this.tree.natural(this.identity), this.tree.natural(BigInt(key))]); } + token(value: H, name: string): Token { + const [tag, rest] = this.pair(value), [run, tail] = this.pair(rest), [key, end] = this.pair(tail); + if (this.tree.observe(end).node.kind !== 'leaf') throw new OperationError('invalid-argument'); + let cursor = tag; + for (const char of name) { + const [head, next] = this.pair(cursor); + if (canonicalNat(this.tree, head, 7) !== BigInt(char.charCodeAt(0))) throw new OperationError('invalid-argument'); + cursor = next; + } + if (this.tree.observe(cursor).node.kind !== 'leaf') throw new OperationError('invalid-argument'); + const r = canonicalNat(this.tree, run, 64), k = canonicalNat(this.tree, key, 64); + if (r === undefined || k === undefined) throw new OperationError('invalid-argument'); + // Unrepresentable keys cannot alias a live JS identity. Keep the ordinary + // stale-handle/task path rather than rounding into a different live handle. + return { run: r, key: k > BigInt(Number.MAX_SAFE_INTEGER) ? -1 : Number(k) }; + } + outcome(value: Outcome<H>): H { + const name = this.tree.text(value.kind); + if (value.kind === 'completed') return this.list([name, value.value]); + const errors = [...value.cleanup?.items ?? []], omitted = value.cleanup?.omitted ?? 0; + if (omitted) errors.push(this.problem('cleanup-omitted', 'close', String(omitted))); + return this.list([name, value.value, this.list(errors)]); + } +} +export interface Provider<H, B> { + request(engine: Core<H, B>, scope: number, request: Request<H>, signal: AbortSignal): (() => H) | Promise<() => H>; + release(resource: Resource<B>): void | Promise<void>; + roots?(): Iterable<H>; +} +export class Core<H, B> { + readonly wire: Wire<H>; + readonly lifecycle: ReturnType<typeof lifecycle<H, B>>; + readonly work: HostWork<H, Completion<H>>; + private readonly bodies = new Map<number, Body<H>>(); + private readonly constants: { stopped: H; cycle: H; fault: H; cleanup: H; budget: H }; + private readonly pendingArguments = new Map<number, H>(); + private cursor = -1; + private actions = 0; + result: Outcome<H> | null = null; + // Embedding/evaluator failure is retained separately and never stringified or + // mistaken for an ordinary Result. Drain cleanup before rethrowing at boundary. + fault: unknown = undefined; + hasFault = false; + constructor(readonly tree: Trees<H>, identity: bigint, action: H, standard: readonly [B, B, B], + private readonly provider: Provider<H, B>, private readonly actionLimit = 65536) { + if (!Number.isSafeInteger(actionLimit) || actionLimit < 1) throw new Error('invalid action limit'); + this.wire = new Wire(tree, identity); + this.constants = { + stopped: this.wire.problem('cancelled', 'scope', 'owner stopped'), + cycle: this.wire.problem('join-cycle', 'scope'), fault: this.wire.problem('runtime-error', 'core'), + cleanup: this.wire.problem('cleanup-failed', 'resource/release', '', 'unknown'), budget: this.wire.problem('limit', 'core', 'IO action limit exceeded') }; + this.lifecycle = lifecycle(identity, this.constants.stopped, this.constants.cycle, standard); + this.work = new HostWork(this.lifecycle.tasks); + this.bodies.set(0, this.body(action)); + } + private body(action: H): Body<H> { return { action, frames: [], mode: 'runnable' }; } + stop(reason: H): void { if (!this.result) { this.lifecycle.tasks.stop(0, { kind: 'cancelled', value: reason }); this.work.cancelStopped(); } } + private done(scope: number, outcome: Outcome<H>): void { + this.lifecycle.tasks.bodyDone(scope, outcome); + const body = this.bodies.get(scope)!; body.mode = 'done'; body.action = this.tree.leaf; body.frames = []; + } + private value(scope: number, value: H): void { + const body = this.bodies.get(scope)!; + if (body.frames.length) { body.action = this.tree.app(body.frames.pop()!, value); body.mode = 'runnable'; } + else this.done(scope, { kind: 'completed', value }); + } + private operation(scope: number, name: string, action: () => void): void { + try { action(); } + catch (error) { + if (!(error instanceof OperationError)) throw error; + this.value(scope, this.wire.result(false, this.wire.problem(error.message, name))); + } + } + private step(scope: number): boolean { + const { tasks, resources } = this.lifecycle, body = this.bodies.get(scope)!, state = tasks.state(scope); + if (body.mode === 'done' || body.mode === 'lexical') return false; + if (state.reason) { this.done(scope, state.reason); return true; } + if (body.mode === 'host') return false; + if (body.mode === 'join') { + const outcome = tasks.pollJoin(scope); if (!outcome) return false; + this.value(scope, this.wire.result(true, this.wire.outcome(outcome))); return true; + } + if (++this.actions > this.actionLimit) { tasks.stop(0, { kind: 'failed', value: this.constants.budget }); return true; } + let action: Action<H>; + try { action = decodeAction(this.tree, body.action); } + catch (error) { + if (!(error instanceof ProtocolError)) throw error; + this.done(scope, { kind: 'failed', value: this.wire.problem('protocol-error', 'core/decode', error.message) }); return true; + } + if (action.kind === 'request') { + // Keep action/arguments rooted until provider acknowledgement, including + // cancellation. The abandoned continuation is cleared by done(). + body.mode = 'host'; + const request = action; this.pendingArguments.set(scope, request.arguments); + this.work.start(scope, async signal => ({ kind: 'body', encode: await this.provider.request(this, scope, request, signal) })); + return true; + } + const p = action.payload; + switch (action.code) { + case 0: this.value(scope, p); break; + case 1: { + const node = this.tree.observe(p).node; + if (node.kind !== 'fork') this.done(scope, { kind: 'failed', value: this.wire.problem('protocol-error', 'core/decode', 'invalid v2 computation') }); + else { body.frames.push(node.right); body.action = node.left; } + break; + } + case 2: this.done(scope, { kind: 'failed', value: p }); break; + case 9: this.done(scope, { kind: 'cancelled', value: p }); break; + case 3: { + const child = tasks.nested(scope); this.bodies.set(child, this.body(this.tree.leaf)); body.mode = 'lexical'; body.action = this.tree.leaf; + this.bodies.get(child)!.action = this.tree.app(p, this.wire.marker('io.v2.scope', child)); break; + } + case 4: this.value(scope, this.wire.marker('io.v2.scope', scope)); break; + case 5: this.operation(scope, 'core/spawn', () => { + const [sup, a] = this.wire.pair(p), [keep, b] = this.wire.pair(a), [handles, callback] = this.wire.pair(b); + const supervised = canonicalNat(this.tree, sup, 1), retained = canonicalNat(this.tree, keep, 1); + if (supervised === undefined || retained === undefined) throw new OperationError('invalid-argument'); + const tokens: Token[] = []; let rest = handles; + while (this.tree.observe(rest).node.kind !== 'leaf') { + if (tokens.length === 256) throw new OperationError('invalid-argument'); + const [head, tail] = this.wire.pair(rest); tokens.push(this.wire.token(head, 'io.v2.resource')); rest = tail; + } + const moved = logical(() => resources.spawn(scope, supervised === 1n, retained === 1n, tokens)), child = moved.task.key; + this.bodies.set(child, this.body(this.tree.leaf)); + this.bodies.get(child)!.action = this.tree.app(callback, this.wire.list(moved.handles.map(t => this.wire.marker('io.v2.resource', t.key)))); + this.value(scope, this.wire.result(true, this.wire.list([this.wire.marker('io.v2.task', child), retained === 1n ? this.wire.marker('io.v2.join', child) : this.tree.leaf]))); + }); break; + case 6: case 7: this.operation(scope, action.code === 6 ? 'core/join' : 'core/discard', () => { + const token = this.wire.token(p, 'io.v2.join'); + if (action.code === 7) { logical(() => tasks.discard(scope, token)); this.value(scope, this.wire.result(true, this.tree.leaf)); } + else { logical(() => tasks.claimJoin(scope, token)); body.mode = 'join'; body.action = this.tree.leaf; } + }); break; + case 8: this.operation(scope, 'core/cancel', () => { + const [handle, reason] = this.wire.pair(p), token = this.wire.token(handle, 'io.v2.task'); logical(() => tasks.cancel(scope, token, reason)); + this.value(scope, this.wire.result(true, this.tree.leaf)); + }); break; + } + return true; + } + private settled(scope: number, errors: Diagnostics<H>): void { + const { tasks } = this.lifecycle, state = tasks.state(scope), outcome = tasks.settle(scope, errors); + this.bodies.delete(scope); + if (scope === 0) this.result = outcome; + else if (state.kind === 'lexical') { + const parent = state.parent!, reason = tasks.state(parent).reason; + this.bodies.get(parent)!.mode = 'runnable'; + if (reason) this.done(parent, reason); + else if (outcome.kind === 'completed') this.value(parent, outcome.value); + else this.done(parent, outcome); + } + } + // One progress turn, never waits for a provider. False means wait for host + // acknowledgement. The embedding must yield to its event loop between batches. + advance(): boolean { + if (this.result) return false; + try { + this.work.cancelStopped(); + for (const scope of this.pendingArguments.keys()) + if (this.lifecycle.tasks.state(scope).hostCalls === 0) this.pendingArguments.delete(scope); + const reply = this.work.take(); + if (reply) { + if (!reply.outcome.ok) throw reply.outcome.error; + const value = reply.outcome.value; + if (value.kind === 'release') this.settled(reply.token.scope, value.errors); + else this.value(reply.token.scope, value.encode()); + return true; + } + const { tasks, resources } = this.lifecycle; + for (const scope of tasks.scopeIds()) if (tasks.state(scope).phase === 'joining' && tasks.beginRelease(scope)) { + this.work.release(scope, async () => ({ kind: 'release', errors: await resources.releaseScope(scope, + resource => this.provider.release(resource), () => this.constants.cleanup, this.constants.cleanup) })); + return true; + } + const ids = tasks.scopeIds(), ordered = [...ids.filter(id => id > this.cursor), ...ids.filter(id => id <= this.cursor)]; + for (const scope of ordered) if (this.step(scope)) { this.cursor = scope; return true; } + return false; + } catch (error) { + if (!this.hasFault) this.fault = error; + this.hasFault = true; + this.lifecycle.tasks.stop(0, { kind: 'failed', value: this.constants.fault }); + this.work.cancelStopped(); return true; + } + } + wait(): Promise<void> { return this.work.wait(); } + *roots(): Iterable<H> { + yield this.tree.leaf; yield* Object.values(this.constants); yield* this.lifecycle.tasks.payloads(); + yield* this.pendingArguments.values(); yield* this.provider.roots?.() ?? []; + for (const body of this.bodies.values()) { yield body.action; yield* body.frames; } + for (const value of this.work.values()) { + if (value.kind === 'release') yield* value.errors.items; + } + if (this.result) { yield this.result.value; yield* this.result.cleanup?.items ?? []; } + } +} diff --git a/ext/demo-site/src/io-v2-driver.ts b/ext/demo-site/src/io-v2-driver.ts @@ -0,0 +1,82 @@ +import type { IOHost } from './io.js'; +import type { Command, Session } from './io-v2-session.js'; +import type { ValueView } from './codecs.js'; +import type { Event } from './io-v2-events.js'; +export interface V2Host { + readonly stepping?: boolean; + // These transport callbacks must not throw or synchronously reenter a turn. + open(run: string, command: (command: Command) => void): void; + event(event: Event): void; + close(run: string, settled: boolean): void; +} +export type Report = Awaited<ReturnType<Session<unknown>['turn']>>; +export interface Driver { + run: string; + // Emergency runtime loss, distinct from structured stop/acknowledgement. + signal?: AbortSignal; + turn(command: Command): Promise<Report>; + view(): Promise<ValueView>; + discard?(): void; +} +// Unlike repeated Promise.race against a lifetime-long promise, listeners are +// removed on EVERY turn/step settlement, not retained until runtime disposal. +function interrupt<T>(promise: Promise<T>, signal: AbortSignal | undefined, aborted: () => T): Promise<T> { + if (!signal) return promise; + let onAbort = () => {}; + return new Promise<T>((resolve, reject) => { + onAbort = () => { try { resolve(aborted()); } catch (error) { reject(error); } }; + if (signal.aborted) onAbort(); else signal.addEventListener('abort', onAbort, { once: true }); + void promise.then(resolve, reject); + }).finally(() => signal.removeEventListener('abort', onAbort)); +} +// The queue stays alive while a read or step prompt is waiting. Stop is sticky, +// independent of the resume queue, and never implemented by worker destruction. +export async function drive(driver: Driver, host: IOHost): Promise<ValueView> { + const transport = host.v2; + if (!transport) throw new Error('IO version 2 requires a structured terminal host'); + let settled = false; + const alive = <T>(promise: Promise<T>): Promise<T> => interrupt(promise, driver.signal, () => { throw driver.signal!.reason; }); + let active = true, stopping = false, stopSent = false, wake = () => {}, step = 0; + const replies: Command[] = []; + let boundaryFault = false, fault: unknown; + const stopped = new AbortController(); + const enqueue = (command: Command) => { + if (!active || !command || command.run !== driver.run) return; + if (command.kind === 'stop') { stopping = true; stopped.abort(); } + else if (command.kind === 'reply') replies.push(command); + wake(); + }; + const close = () => { + if (!active) return; + active = false; stopped.abort(); wake(); wake = () => {}; replies.length = 0; + // A failed transport/runtime cannot be reused with admitted work abandoned. + if (!settled) driver.discard?.(); + transport.close(driver.run, settled); + }; + try { + transport.open(driver.run, enqueue); + for (;;) { + if (!stopping && transport.stepping === true) { + try { await alive(interrupt(host.before({ step: ++step, name: 'v2 scheduler turn', detail: 'one scheduler advance; all tasks paused', depth: 0 }), stopped.signal, () => {})); } + catch (error) { if (driver.signal?.aborted) throw error; boundaryFault = true; fault = error; enqueue({ run: driver.run, kind: 'stop' }); } + } + const command: Command = stopping && !stopSent ? (stopSent = true, { run: driver.run, kind: 'stop' }) : + replies.shift() ?? { run: driver.run, kind: 'poll' }; + const report = await alive(driver.turn(command)); + for (const event of report.events) transport.event(event); + if (report.done) { + if (!Object.values(report.counts).every(group => Object.values(group).every(value => value === 0))) throw new Error('IO v2 settlement retains work'); + settled = true; close(); + if (boundaryFault) throw fault; + if (report.outcome?.fault) throw new Error('IO v2 interpreter fault'); + if (report.outcome?.kind !== 'completed') throw new Error(`IO v2 ${report.outcome?.kind ?? 'failed'}`); + return await alive(driver.view()); // Full codec, only after physical settlement. + } + // Yield even during pure scheduler progress so the outer worker can receive + // stop/replies. This is not a hard deadline for native tree reduction. + if (report.waiting && !replies.length && (!stopping || stopSent)) await alive(new Promise<void>(resolve => { wake = resolve; })); + else await alive(new Promise<void>(resolve => { const timer = setTimeout(resolve, 0); wake = () => { clearTimeout(timer); resolve(); }; })); + wake = () => {}; + } + } finally { close(); } +} diff --git a/ext/demo-site/src/io-v2-events.ts b/ext/demo-site/src/io-v2-events.ts @@ -0,0 +1,79 @@ +// Cloneable transport only: no evaluator handles, task tokens or continuations. +// A reply acknowledges physical provider completion, not merely abort delivery. +export type Event = { run: string; id: number } & ( + { kind: 'read'; limit: number } | { kind: 'write'; stream: 'output' | 'error'; bytes: number[] } | + { kind: 'cancel'; operation: 'read' | 'write' }); +export type Completion = { kind: 'read'; bytes: number[]; eof: boolean } | + { kind: 'write'; count: number } | { kind: 'cancelled' } | { kind: 'error' }; +interface Pending { + event: Event; delivered: boolean; cancelling: boolean; + resolve(value: Completion): void; remove(): void; +} +function bytes(value: unknown, limit: number): value is number[] { + return Array.isArray(value) && value.length <= limit && value.every(x => Number.isInteger(x) && x >= 0 && x <= 255); +} +export class Events { + private next = 1; + private readonly pending = new Map<number, Pending>(); + private queue: Event[] = []; + constructor(readonly run: string, private readonly limit = Number.MAX_SAFE_INTEGER) { + if (!/^[1-9][0-9]*$/.test(run) || run.length > 20 || BigInt(run) > 0xffffffffffffffffn || + !Number.isSafeInteger(limit) || limit < 1) throw new Error('invalid event identity'); + } + get counts() { return { pending: this.pending.size, queued: this.queue.length }; } + read(limit: number, signal: AbortSignal): Promise<Completion> { + if (!Number.isInteger(limit) || limit < 1 || limit > 4096) throw new Error('invalid read bound'); + return this.start({ run: this.run, id: 0, kind: 'read', limit }, signal); + } + write(stream: 'output' | 'error', value: readonly number[], signal: AbortSignal): Promise<Completion> { + if (!['output', 'error'].includes(stream) || !bytes(value, 4096) || !value.length) throw new Error('invalid write chunk'); + return this.start({ run: this.run, id: 0, kind: 'write', stream, bytes: [...value] }, signal); + } + private start(event: Event, signal: AbortSignal): Promise<Completion> { + if (signal.aborted) return Promise.resolve({ kind: 'cancelled' }); + if (this.next > this.limit) throw new Error('event identity exhausted'); + event.id = this.next++; + return new Promise(resolve => { + const cancel = () => { + const entry = this.pending.get(event.id); if (!entry || entry.cancelling) return; + entry.cancelling = true; + if (!entry.delivered) this.finish(event.id, { kind: 'cancelled' }); + else this.queue.push({ run: this.run, id: event.id, kind: 'cancel', operation: event.kind as 'read' | 'write' }); + }; + this.pending.set(event.id, { event, delivered: false, cancelling: false, resolve, + remove: () => signal.removeEventListener('abort', cancel) }); + signal.addEventListener('abort', cancel, { once: true }); this.queue.push(event); + }); + } + take(): Event[] { + const out = this.queue; this.queue = []; + for (const event of out) this.pending.get(event.id)!.delivered = true; + // Never expose the mutable validation record or its byte array to the peer. + return out.map(event => event.kind === 'write' ? { ...event, bytes: [...event.bytes] } : { ...event }); + } + private finish(id: number, value: Completion): void { + const entry = this.pending.get(id)!; + this.pending.delete(id); this.queue = this.queue.filter(event => event.id !== id); entry.remove(); + entry.resolve(entry.cancelling ? { kind: 'cancelled' } : value); + } + // Wrong run/kind/bounds and stale/duplicate replies never consume a live wait. + // A valid normal completion can race with cancellation: it acknowledges work, + // but its data is discarded. No extra cancellation acknowledgement is needed. + reply(value: unknown): boolean { + if (!value || typeof value !== 'object') return false; + const v = value as Record<string, unknown>; + if (v.run !== this.run || typeof v.id !== 'number' || !Number.isSafeInteger(v.id)) return false; + const entry = this.pending.get(v.id); if (!entry || !entry.delivered) return false; + let completion: Completion; + if (v.kind === 'cancelled' && entry.cancelling) completion = { kind: 'cancelled' }; + else if (v.kind === 'error') completion = { kind: 'error' }; + else if (v.kind === 'read' && entry.event.kind === 'read' && bytes(v.bytes, entry.event.limit) && + typeof v.eof === 'boolean' && (v.eof ? v.bytes.length === 0 : v.bytes.length > 0)) + completion = { kind: 'read', bytes: [...v.bytes], eof: v.eof }; + else if (v.kind === 'write' && entry.event.kind === 'write' && typeof v.count === 'number' && + Number.isInteger(v.count) && v.count > 0 && v.count <= entry.event.bytes.length) + completion = { kind: 'write', count: v.count }; + else return false; + this.finish(v.id, completion); return true; + } +} diff --git a/ext/demo-site/src/io-v2-host-work.ts b/ext/demo-site/src/io-v2-host-work.ts @@ -0,0 +1,80 @@ +import { Tasks, hostKey, type HostToken } from './io-v2-tasks.js'; +export type Reply<T> = { readonly token: HostToken; readonly outcome: + { readonly ok: true; readonly value: T } | { readonly ok: false; readonly error: unknown } }; +// Adapter-side asynchronous work. Starting one callback never waits for it, so +// sibling actions/cancellation can proceed. The provider must settle its promise +// AFTER cancellation cleanup; abort delivery alone is never an acknowledgement. +export class HostWork<P, T> { + private readonly active = new Map<string, { token: HostToken; controller: AbortController }>(); + private readonly replies = new Map<string, Reply<T>>(); + private notification: { promise: Promise<void>; resolve: () => void } | null = null; + constructor(private readonly tasks: Tasks<P>) {} + start(scope: number, operation: (signal: AbortSignal) => T | Promise<T>): HostToken { + return this.register(this.tasks.beginHost(scope), operation); + } + // Cleanup is not cancelled by a new stop request. Its result (including a + // failure) must still reach the driver to preserve primary/secondary outcomes. + release(scope: number, operation: () => T | Promise<T>): HostToken { + return this.register(this.tasks.beginReleaseHost(scope), operation); + } + private register(token: HostToken, operation: (signal: AbortSignal) => T | Promise<T>): HostToken { + const controller = new AbortController(); + this.active.set(hostKey(token), { token, controller }); + const complete = (outcome: Reply<T>['outcome']) => { + // Settle first; a cancelled reply is discarded without observing its value. + const disposition = this.tasks.endHost(token); + this.active.delete(hostKey(token)); + if (disposition === 'reply') this.replies.set(hostKey(token), { token, outcome }); + const notice = this.notification; this.notification = null; notice?.resolve(); + }; + try { Promise.resolve(operation(controller.signal)).then( + value => complete({ ok: true, value }), error => complete({ ok: false, error })); } + catch (error) { complete({ ok: false, error }); } + return token; + } + private usable(token: HostToken): boolean { + if (!this.tasks.within(token.scope, token.scope)) return false; + const state = this.tasks.state(token.scope); + return token.kind === 'release' ? state.phase === 'releasing' : state.phase === 'open' && state.reason === null; + } + // Resource close may interrupt one operation without cancelling its task. + // The resulting provider reply still reaches that body as an ordinary error. + // Never abort a cleanup batch or acknowledge work merely by sending this signal. + cancelBody(token: HostToken): boolean { + const work = this.active.get(hostKey(token)); + if (token.kind !== 'body' || !work || work.token.run !== token.run || + work.token.scope !== token.scope || work.controller.signal.aborted) return false; + work.controller.abort(); return true; + } + cancelStopped(): void { + for (const token of this.tasks.takeCancellations()) { + const work = this.active.get(hostKey(token)); + if (!work || work.token.scope !== token.scope) throw new Error('host work ownership mismatch'); + work.controller.abort(); + } + // Completion may have won the race with cancellation. Drop undelivered data + // too; a queued reply cannot revive a stopped/settled body. + for (const [key, reply] of this.replies) if (!this.usable(reply.token)) this.replies.delete(key); + } + take(): Reply<T> | null { + for (const [key, reply] of this.replies) { + this.replies.delete(key); + if (this.usable(reply.token)) return reply; + } + return null; + } + wait(): Promise<void> { + if (this.replies.size || !this.active.size) return Promise.resolve(); + if (!this.notification) { + let resolve!: () => void; + const promise = new Promise<void>(done => { resolve = done; }); + this.notification = { promise, resolve }; + } + return this.notification.promise; + } + get counts() { return { pending: this.active.size, replies: this.replies.size }; } + // Reply data roots are the embedding's responsibility until taken/discarded. + *values(): Iterable<T> { + for (const reply of this.replies.values()) if (this.usable(reply.token) && reply.outcome.ok) yield reply.outcome.value; + } +} diff --git a/ext/demo-site/src/io-v2-output.ts b/ext/demo-site/src/io-v2-output.ts @@ -0,0 +1,61 @@ +// Presentation only. Byte acceptance and strict source UTF-8 Results are separate. +// No evaluator handles, host callbacks, IO acknowledgements or terminal escapes. +import { decodeUTF8, UTF8Error } from './io-v2-utf8.js'; +export type OutputStream = 'output' | 'error'; +export type OutputToken = { kind: 'text'; offset: number; text: string } | + { kind: 'bytes'; offset: number; bytes: number[]; reason: 'invalid' | 'incomplete' | 'pending' }; +export interface OutputView { bytes: number[]; hex: string; tokens: OutputToken[] } + +function view(bytes: number[], final: boolean): OutputView { + const tokens: OutputToken[] = []; + for (let offset = 0; offset < bytes.length;) { + const lead = bytes[offset]!; + const width = lead <= 0x7f ? 1 : lead >= 0xc2 && lead <= 0xdf ? 2 : + lead >= 0xe0 && lead <= 0xef ? 3 : lead >= 0xf0 && lead <= 0xf4 ? 4 : 1; + const candidate = bytes.slice(offset, offset + width); + try { + const decoded = decodeUTF8(candidate, false); + if (decoded.carry.length) { + tokens.push({ kind: 'bytes', offset, bytes: candidate, reason: final ? 'incomplete' : 'pending' }); + break; + } + const previous = tokens.at(-1); + if (previous?.kind === 'text') previous.text += decoded.text; + else tokens.push({ kind: 'text', offset, text: decoded.text }); + offset += width; + } catch (error) { + if (!(error instanceof UTF8Error)) throw error; + // Escape one offending byte; reconsider following bytes independently. + // Thus invalid prefixes cannot swallow subsequent valid text. + tokens.push({ kind: 'bytes', offset, bytes: [lead], reason: 'invalid' }); + offset++; + } + } + return { bytes, hex: bytes.map(b => b.toString(16).padStart(2, '0')).join(' '), tokens }; +} + +// Owned by one UI run. The session separately enforces attempted-output budgets; +// this buffer bounds retained accepted bytes, without refunds or history. +export class OutputBuffer { + private readonly streams: Record<OutputStream, number[]> = { output: [], error: [] }; + private size = 0; + private ended = false; + constructor(readonly limit = 16384) { + if (!Number.isSafeInteger(limit) || limit < 0 || limit > 16384) throw new Error('invalid output limit'); + } + append(stream: OutputStream, bytes: readonly number[]): void { + if (this.ended) throw new Error('output presentation ended'); + if (stream !== 'output' && stream !== 'error') throw new Error('invalid output stream'); + if (!Array.isArray(bytes) || bytes.length === 0 || bytes.length > 4096) throw new Error('invalid output bytes'); + for (const byte of bytes) if (!Number.isInteger(byte) || byte < 0 || byte > 255) throw new Error('invalid output bytes'); + if (bytes.length > this.limit - this.size) throw new Error('output presentation limit'); + this.streams[stream].push(...bytes); this.size += bytes.length; + } + // End-of-presentation, including after structured cancellation, not closing a + // borrowed stream. Incomplete suffixes become visible bytes, never IO failures. + finish(): void { this.ended = true; } + snapshot(): { done: boolean; size: number; output: OutputView; error: OutputView } { + return { done: this.ended, size: this.size, + output: view([...this.streams.output], this.ended), error: view([...this.streams.error], this.ended) }; + } +} diff --git a/ext/demo-site/src/io-v2-private.ts b/ext/demo-site/src/io-v2-private.ts @@ -0,0 +1,37 @@ +// Private native-adapter validation runner, not RuntimeSession IO dispatch. +// Bare computations only; no facility implementation/capability advertisement. +import { Core, type Trees } from './io-v2-core.js'; +import { canonicalNat, type Reader } from './io-v2-protocol.js'; +export function constructors<H>(base: Reader<H> & Pick<Trees<H>, 'leaf' | 'fork' | 'stem' | 'app'>): Trees<H> { + const natural = (value: bigint): H => { + const bits: H[] = []; + if (value < 0n) throw new Error('negative natural'); + for (; value; value >>= 1n) bits.push(value & 1n ? base.stem(base.leaf) : base.leaf); + let out = base.leaf; for (let i = bits.length - 1; i >= 0; i--) out = base.fork(bits[i]!, out); return out; + }; + return { ...base, natural, text(value: string): H { + let out = base.leaf; const chars = Array.from(value); + for (let i = chars.length - 1; i >= 0; i--) out = base.fork(natural(BigInt(chars[i]!.codePointAt(0)!)), out); + return out; + } }; +} +export async function runPrivate<H>(tree: Trees<H>, action: H, identity: bigint, sweep: (roots: Iterable<H>) => void) { + const engine = new Core(tree, identity, action, [null, null, null], { + request: (core, _scope, request) => () => core.wire.result(false, core.wire.problem('unsupported', `${request.group}/${request.operation}`)), + release: () => { throw new Error('unexpected owned backend in private core probe'); } }); + sweep(engine.roots()); + for (let turns = 0; !engine.result; turns++) { + const progress = engine.advance(); sweep(engine.roots()); + if (!progress) { + if (!engine.work.counts.pending) throw new Error('private interpreter stalled'); + await engine.wait(); + } + if (turns % 32 === 0) await new Promise<void>(resolve => setTimeout(resolve, 0)); + } + if (engine.hasFault) throw engine.fault; + const out = engine.result; + // Terminal diagnostics stay opaque, including when the test requests a number. + const value = out.kind === 'completed' ? canonicalNat(tree, out.value, 64)?.toString() ?? null : null; + return { kind: out.kind, value, cleanup: out.cleanup?.items.length ?? 0, + tasks: engine.lifecycle.tasks.counts, resources: engine.lifecycle.resources.counts, host: engine.work.counts }; +} diff --git a/ext/demo-site/src/io-v2-profile.ts b/ext/demo-site/src/io-v2-profile.ts @@ -0,0 +1,43 @@ +// This table is the implemented browser provider, not the native catalogue. +import { canonicalNat, decodeAction, ProtocolError, type Reader } from './io-v2-protocol.js'; +import type { Wire } from './io-v2-core.js'; +export const coreNames = ['pure', 'bind', 'fail', 'scope', 'current-scope', 'spawn', 'join', 'discard', 'cancel', 'cancelled'] as const; +const groups: Readonly<Record<string, readonly string[]>> = Object.freeze({ + runtime: ['capabilities', 'inspect'], standard: ['input', 'output', 'error'], + stream: ['read', 'write'], resource: ['close'], utf8: ['encode', 'decode'], +}); +export function available(group: string, revision: number, operation: string): boolean { + return revision === 1 && Object.hasOwn(groups, group) && groups[group]!.includes(operation); +} +export function capabilities<H>(wire: Wire<H>, backend: string): H { + const { tree } = wire, text = (s: string) => tree.text(s), number = (n: number) => tree.natural(BigInt(n)); + const strings = (xs: readonly string[]) => wire.list(xs.map(text)), row = (name: string, value: H) => wire.list([text(name), value]); + return wire.list([ + row('schema', number(1)), row('io-version', number(2)), row('backend', text(backend)), row('authority', text('browser-terminal')), + row('core', strings(coreNames)), row('groups', wire.list(Object.entries(groups).map(([name, ops]) => wire.list([text(name), number(1), strings(ops)])))), + row('limits', wire.list(Object.entries({ 'io-actions': 65536, 'chunk-bytes': 4096, 'transfer-handles': 256, 'utf8-encode-scalars': 1024, 'input-line-scalars': 256, 'output-attempted-bytes': 16384 }).map(([key, value]) => row(key, number(value))))), + row('restrictions', strings(['action-boundary-cancellation', 'no-hard-deadlines', 'borrowed-standard-streams', 'standard-stream-leases-per-run', 'utf8-strict-scalars', 'text-line-input', 'byte-preserving-output', 'no-filesystem', 'no-network', 'no-processes'])), + ]); +} +export function inspect<H>(wire: Wire<H>, value: H): H { + const action = decodeAction(wire.tree, value), t = wire.tree; + return action.kind === 'core' ? wire.list([t.text('core'), t.text(coreNames[action.code]!)]) : + wire.list([t.text('facility'), t.text(action.group), t.natural(BigInt(action.revision)), t.text(action.operation), t.text(available(action.group, action.revision, action.operation) ? 'available' : 'unsupported')]); +} +// Authenticate framing before selecting an interpreter. No action is observed. +export function envelope<H>(tree: Reader<H>, value: H): { version: 2; action: H } { + const root = tree.observe(value).node; + if (root.kind !== 'fork') throw new ProtocolError('invalid IO envelope'); + let rest = root.left; + for (const char of 'tricuIO') { + const node = tree.observe(rest).node; + if (node.kind !== 'fork' || canonicalNat(tree, node.left, 7) !== BigInt(char.charCodeAt(0))) throw new ProtocolError('Missing tricuIO sentinel'); + rest = node.right; + } + if (tree.observe(rest).node.kind !== 'leaf') throw new ProtocolError('Missing tricuIO sentinel'); + const body = tree.observe(root.right).node; + if (body.kind !== 'fork') throw new ProtocolError('invalid IO envelope'); + const version = canonicalNat(tree, body.left, 2); + if (version !== 2n) throw new ProtocolError('Unsupported tricuIO version'); + return { version: 2, action: body.right }; +} diff --git a/ext/demo-site/src/io-v2-protocol.ts b/ext/demo-site/src/io-v2-protocol.ts @@ -0,0 +1,59 @@ +// Inert wire decoding, not execution, resolution or a capability declaration. +// The caller retains the action and returned borrowed handles across collection. +export type Node<H> = { kind: 'leaf' } | { kind: 'stem'; child: H } | { kind: 'fork'; left: H; right: H }; +export interface Reader<H> { observe(handle: H): { node: Node<H> }; } +export type Action<H> = { kind: 'core'; code: number; payload: H } + | { kind: 'request'; group: string; revision: number; operation: string; arguments: H }; + +export function canonicalNat<H>(reader: Reader<H>, handle: H, bits: number): bigint | undefined { + if (!Number.isInteger(bits) || bits < 0 || bits > 64) throw new Error('Invalid natural bit bound'); + let rest = handle, value = 0n, count = 0, last = 0n; + for (;;) { + const node = reader.observe(rest).node; + if (node.kind === 'leaf') return count === 0 || last === 1n ? value : undefined; + if (count === bits || node.kind !== 'fork') return undefined; + const bit = reader.observe(node.left).node; + if (bit.kind === 'leaf') last = 0n; + else if (bit.kind === 'stem' && reader.observe(bit.child).node.kind === 'leaf') last = 1n; + else return undefined; + value |= last << BigInt(count++); rest = node.right; + } +} +function name<H>(reader: Reader<H>, handle: H): string | undefined { + let rest = handle, value = ''; + for (;;) { + const node = reader.observe(rest).node; + if (node.kind === 'leaf') return value || undefined; + // Observe only the excess spine cell, never its head or tail. + if (value.length === 64 || node.kind !== 'fork') return undefined; + const code = canonicalNat(reader, node.left, 7); + if (code === undefined || !(code >= 97n && code <= 122n || value.length > 0 && (code >= 48n && code <= 57n || code === 45n))) return undefined; + value += String.fromCharCode(Number(code)); rest = node.right; + } +} +export class ProtocolError extends Error {} +export function decodeAction<H>(reader: Reader<H>, handle: H): Action<H> { + const action = reader.observe(handle).node; + if (action.kind !== 'fork') throw new ProtocolError('invalid computation shape'); + const code = canonicalNat(reader, action.left, 8); + if (code !== undefined && code <= 9n) return { kind: 'core', code: Number(code), payload: action.right }; + if (code !== 10n) throw new ProtocolError('unknown computation constructor'); + const group = reader.observe(action.right).node; + if (group.kind !== 'fork') throw new ProtocolError('invalid facility header'); + const revision = reader.observe(group.right).node; + if (revision.kind !== 'fork') throw new ProtocolError('invalid facility header'); + const operation = reader.observe(revision.right).node; + if (operation.kind !== 'fork') throw new ProtocolError('invalid facility header'); + const g = name(reader, group.left); + if (g === undefined) throw new ProtocolError('invalid facility header'); + const v = canonicalNat(reader, revision.left, 16); + if (v === undefined || v === 0n) throw new ProtocolError('invalid facility header'); + const op = name(reader, operation.left); + if (op === undefined) throw new ProtocolError('invalid facility header'); + return { kind: 'request', group: g, revision: Number(v), operation: op, arguments: operation.right }; +} +export function header<H>(reader: Reader<H>, handle: H): object { + const action = decodeAction(reader, handle); + return action.kind === 'core' ? { kind: action.kind, code: action.code } + : { kind: action.kind, group: action.group, revision: action.revision, operation: action.operation }; +} diff --git a/ext/demo-site/src/io-v2-resources.ts b/ext/demo-site/src/io-v2-resources.ts @@ -0,0 +1,169 @@ +import { Tasks, type Token, type Diagnostics } from './io-v2-tasks.js'; +export interface Flags { readonly borrowed?: boolean; readonly internal?: boolean; readonly duplex?: boolean; } +export interface Resource<B> { + readonly owner: number; readonly kind: string; readonly backend: B; readonly flags: Flags; + readonly busy: number; readonly closing: boolean; readonly releasing: boolean; +} +type Entry<B> = { -readonly [K in keyof Resource<B>]: Resource<B>[K] }; +export type Access = 'exclusive' | 'read' | 'write'; +// State only. Construct via lifecycle() to bind task settlement to registry guards. +// Single owning event loop; host effects occur AFTER admission/lease/close claims. +class Resources<P, B> { + private next = 4; + private readonly resources = new Map<number, Entry<B>>(); + private readonly admissions = new Map<number, number>(); + private readonly leases = new Map<number, { scope: number; resource: number; mask: number }>(); + constructor(private readonly tasks: Tasks<P>, standard: readonly [B, B, B], private readonly limit: number) { + if (!Number.isSafeInteger(limit) || limit < 3) throw new Error('invalid resource identity limit'); + standard.forEach((backend, index) => this.resources.set(index + 1, { + owner: 0, kind: index === 0 ? 'reader' : 'writer', backend, flags: Object.freeze({ borrowed: true }), + busy: 0, closing: false, releasing: false })); + } + get counts() { return { resources: this.resources.size, admissions: this.admissions.size, leases: this.leases.size }; } + private token(key: number): Token { return Object.freeze({ run: this.tasks.identity, key }); } + private key(token: Token): number { + if (token.run !== this.tasks.identity) throw new Error('stale-handle'); + return token.key; + } + private room(count: number): void { + if (count > this.limit - this.next + 1) throw new Error('resource-identity-exhausted'); + } + private open(scope: number): void { + const state = this.tasks.state(scope); + if (state.phase !== 'open' || state.reason) throw new Error('closed-scope'); + } + private entry(current: number, token: Token): Entry<B> { + const resource = this.resources.get(this.key(token)); + if (!resource) throw new Error('stale-handle'); + if (!this.tasks.within(current, resource.owner)) throw new Error('out-of-scope'); + return resource; + } + lookup(current: number, token: Token): Resource<B> { return { ...this.entry(current, token) }; } + beginAcquire(current: number, owner = current): Token { + this.tasks.checkpoint(current); this.open(owner); + if (!this.tasks.within(current, owner)) throw new Error('out-of-scope'); + this.room(1); const token = this.token(this.next++); + this.admissions.set(token.key, owner); return token; + } + abortAcquire(token: Token): void { + if (!this.admissions.delete(this.key(token))) throw new Error('invalid-admission'); + } + // An admitted acquisition can finish after owner stop/admission closure. + // Publication failure leaves the admission live: release raw backend, then abort. + publish(admission: Token, kind: string, backend: B, flags: Flags = {}): Token { + const key = this.key(admission), owner = this.admissions.get(key); + if (owner === undefined) throw new Error('invalid-admission'); + if (!kind || Object.values(flags).some(value => typeof value !== 'boolean')) throw new Error('invalid-argument'); + this.room(1); + const savedFlags = Object.freeze({ ...flags }), token = this.token(this.next); + this.resources.set(token.key, { owner, kind, backend, flags: savedFlags, busy: 0, closing: false, releasing: false }); + this.next++; this.admissions.delete(key); return token; + } + // All recoverable validation and preparation precede task admission. No host + // callback/await separates admission from rekeying. JS OOM is fatal, not rollback. + spawn(owner: number, supervised: boolean, keep: boolean, tokens: readonly Token[]) { + this.tasks.checkpoint(owner); this.open(owner); + if (typeof supervised !== 'boolean' || typeof keep !== 'boolean') throw new Error('invalid-argument'); + if (tokens.length > 256) throw new Error('transfer-limit'); + const seen = new Set<number>(); + const entries = tokens.map(token => { + const resource = this.entry(owner, token); + if (seen.has(token.key)) throw new Error('duplicate-handle'); + seen.add(token.key); + if (resource.owner !== owner) throw new Error('not-owner'); + if (resource.flags.borrowed) throw new Error('borrowed-resource'); + if (resource.flags.internal) throw new Error('internal-resource'); + if (resource.closing) throw new Error('closed-handle'); + if (resource.busy) throw new Error('busy'); + return { old: token.key, resource }; + }).sort((a, b) => a.old - b.old); + this.room(tokens.length); + const fresh = new Map(entries.map((entry, i) => [entry.old, this.token(this.next + i)])); + const handles = tokens.map(token => fresh.get(token.key)!); + const task = this.tasks.spawn(owner, supervised, keep); + for (const entry of entries) { + this.resources.delete(entry.old); entry.resource.owner = task.key; + this.resources.set(fresh.get(entry.old)!.key, entry.resource); + } + this.next += tokens.length; + return { task, handles }; + } + beginLease(current: number, token: Token, kind: string, access: Access, internal = false) { + this.tasks.checkpoint(current); + const resource = this.entry(current, token); + if (resource.kind !== kind) throw new Error('wrong-kind'); + if (resource.closing) throw new Error('closed-handle'); + if (resource.flags.internal && !internal) throw new Error('internal-resource'); + if (!['exclusive', 'read', 'write'].includes(access)) throw new Error('invalid-argument'); + const mask = !resource.flags.duplex || access === 'exclusive' ? 3 : access === 'read' ? 1 : 2; + if (resource.busy & mask) throw new Error('busy'); + this.room(1); const lease = this.token(this.next++); + this.leases.set(lease.key, { scope: current, resource: token.key, mask }); resource.busy |= mask; + return { token: lease, backend: resource.backend }; + } + endLease(token: Token): void { + const key = this.key(token), lease = this.leases.get(key); + if (!lease) throw new Error('invalid-lease'); + this.resources.get(lease.resource)!.busy &= ~lease.mask; this.leases.delete(key); + } + claimClose(current: number, token: Token, settling = false): Resource<B> | null { + const resource = this.entry(current, token); + if (settling) { + if (resource.owner !== current || this.tasks.state(current).phase !== 'releasing') throw new Error('invalid-close'); + } else { + this.tasks.checkpoint(current); + if (resource.flags.internal) throw new Error('internal-resource'); + if (resource.flags.borrowed) throw new Error('borrowed-resource'); + } + resource.closing = true; + if (resource.busy || resource.releasing) return null; + resource.releasing = true; return { ...resource }; + } + // Once, including failed release. Never retry a physically ambiguous close. + finishClose(token: Token): void { + const key = this.key(token), resource = this.resources.get(key); + if (!resource) throw new Error('stale-handle'); + if (!resource.releasing) throw new Error('invalid-close'); + this.resources.delete(key); + } + newestOwned(scope: number): Token | null { + let newest = -1; + for (const [key, resource] of this.resources) if (resource.owner === scope && key > newest) newest = key; + return newest < 0 ? null : this.token(newest); + } + readyForRelease(scope: number): boolean { + return ![...this.admissions.values()].includes(scope) && ![...this.leases.values()].some(lease => lease.scope === scope); + } + finishScope(scope: number): void { + if (!this.readyForRelease(scope) || this.newestOwned(scope)) throw new Error('busy'); + } + // Use inside HostWork.release, after Tasks.beginRelease. Each provider is + // attempted once, sequentially in reverse publication order; borrowed backends + // stay open. Diagnostics construction failure must not skip remaining releases. + async releaseScope(scope: number, release: (resource: Resource<B>) => void | Promise<void>, + problem: (error: unknown) => P, fallback: P): Promise<Diagnostics<P>> { + if (this.tasks.state(scope).phase !== 'releasing') throw new Error('invalid-transition'); + const items: P[] = []; let omitted = 0; + for (let token = this.newestOwned(scope); token; token = this.newestOwned(scope)) { + const resource = this.claimClose(scope, token, true); + if (!resource) throw new Error('busy'); + try { if (!resource.flags.borrowed) await release(resource); } + catch (error) { + if (items.length === 16) omitted++; + else { + let value = fallback; try { value = problem(error); } catch { /* retain prebuilt fallback */ } + items.push(value); + } + } finally { this.finishClose(token); } + } + return { items, omitted }; + } +} +export function lifecycle<P, B>(identity: bigint, ownerStopped: P, cycle: P, standard: readonly [B, B, B], + limits: { taskIdentities?: number; resourceIdentities?: number } = {}) { + let resources!: Resources<P, B>; + const tasks = new Tasks(identity, ownerStopped, cycle, limits.taskIdentities ?? Number.MAX_SAFE_INTEGER, { + readyForRelease: scope => resources.readyForRelease(scope), finishScope: scope => resources.finishScope(scope) }); + resources = new Resources(tasks, standard, limits.resourceIdentities ?? Number.MAX_SAFE_INTEGER); + return { tasks, resources }; +} diff --git a/ext/demo-site/src/io-v2-session.ts b/ext/demo-site/src/io-v2-session.ts @@ -0,0 +1,157 @@ +// Cloneable event session shared by private probes and public native adapters. +import { Core, OperationError, type Trees, type Provider } from './io-v2-core.js'; +import { available, capabilities, inspect } from './io-v2-profile.js'; +import { ProtocolError, decodeAction, canonicalNat } from './io-v2-protocol.js'; +import { encodeUTF8, decodeUTF8, UTF8Error } from './io-v2-utf8.js'; +import { Events, type Completion } from './io-v2-events.js'; +interface Slot { name: 'input' | 'output' | 'error'; poisoned: boolean; eof: boolean; } +const faults = new Set(['stale-handle', 'out-of-scope', 'closed-handle', 'wrong-kind', 'internal-resource', 'borrowed-resource', 'busy']); +function logical<T>(f: () => T): T { + try { return f(); } catch (error) { + if (error instanceof Error && faults.has(error.message)) throw new OperationError(error.message); + throw error; + } +} +function provider<H>(events: Events, backend: string): Provider<H, Slot> { + let outputBudget = 16384; // Attempted bytes across both slots; no replay/refund on uncertainty. + return { + release: () => { throw new Error('unexpected owned terminal resource'); }, + async request(engine, scope, request, signal) { + const { tree, wire } = engine, registry = engine.lifecycle.resources, op = `${request.group}/${request.operation}`; + const bad = (category: string, progress: 'none' | 'unknown' = 'none') => () => wire.result(false, wire.problem(category, op, '', progress)); + // Unsupported names/revisions leave arguments completely opaque. + if (!available(request.group, request.revision, request.operation)) return bad('unsupported'); + try { + if (request.group === 'runtime') { + if (request.operation === 'capabilities') { + if (tree.observe(request.arguments).node.kind !== 'leaf') return bad('invalid-argument'); + return () => wire.result(true, capabilities(wire, backend)); + } + // Classify in a live turn; defer construction so no newly allocated + // handles can be swept between promise resolution and consumption. + try { decodeAction(tree, request.arguments); } + catch (error) { if (error instanceof ProtocolError) return bad('invalid-argument'); throw error; } + return () => wire.result(true, inspect(wire, request.arguments)); + } + if (request.group === 'utf8') { + const readList = (value: H, limit: number, bits: number, scalar: boolean): number[] => { + const values: number[] = []; let rest = value; + for (;;) { + const node = tree.observe(rest).node; + if (node.kind === 'leaf') return values; + // Reject the excess spine cell before observing its head or tail. + if (values.length === limit || node.kind !== 'fork') throw new OperationError('invalid-argument'); + const number = canonicalNat(tree, node.left, bits); + if (number === undefined || scalar && (number > 0x10ffffn || number >= 0xd800n && number <= 0xdfffn)) throw new OperationError('invalid-argument'); + values.push(Number(number)); rest = node.right; + } + }; + if (request.operation === 'encode') { + const scalars = readList(request.arguments, 1024, 21, true); + const bytes = encodeUTF8(String.fromCodePoint(...scalars)); + return () => wire.result(true, wire.list(bytes.map(byte => tree.natural(BigInt(byte))))); + } + const [final, rest] = wire.pair(request.arguments), [bytes, end] = wire.pair(rest); + if (tree.observe(end).node.kind !== 'leaf') throw new OperationError('invalid-argument'); + const last = canonicalNat(tree, final, 1); + if (last === undefined) throw new OperationError('invalid-argument'); + const input = readList(bytes, 4096, 8, false); + try { + const decoded = decodeUTF8(input, last === 1n); + return () => wire.result(true, wire.list([tree.text(decoded.text), wire.list(decoded.carry.map(byte => tree.natural(BigInt(byte))))])); + } catch (error) { + if (!(error instanceof UTF8Error)) throw error; + return () => wire.result(false, wire.problem('invalid-data', op, error.message)); + } + } + if (request.group === 'standard') { + if (tree.observe(request.arguments).node.kind !== 'leaf') throw new OperationError('invalid-argument'); + const key = ['input', 'output', 'error'].indexOf(request.operation) + 1; + return () => wire.result(true, wire.marker('io.v2.resource', key)); + } + if (request.group === 'resource') { + const token = wire.token(request.arguments, 'io.v2.resource'); + logical(() => registry.claimClose(scope, token)); + throw new Error('unexpected owned terminal close'); + } + const [handle, argument] = wire.pair(request.arguments), token = wire.token(handle, 'io.v2.resource'); + const read = request.operation === 'read'; + const lease = logical(() => registry.beginLease(scope, token, read ? 'reader' : 'writer', read ? 'read' : 'write')); + try { + const slot = lease.backend; + if (slot.poisoned) return bad('invalid-state', 'unknown'); + let reply: Completion; + if (read) { + const limit = canonicalNat(tree, argument, 16); + if (limit === undefined) throw new OperationError('invalid-argument'); + if (limit < 1n || limit > 4096n) return bad('invalid-bound'); + if (slot.eof) return () => wire.result(true, wire.list([tree.text('eof')])); + reply = await events.read(Number(limit), signal); + if (reply.kind === 'read') { + const received = reply; slot.eof = received.eof; + return () => wire.result(true, received.eof ? wire.list([tree.text('eof')]) : + tree.fork(tree.text('data'), wire.list(received.bytes.map(byte => tree.natural(BigInt(byte)))))); + } + } else { + const bytes: number[] = []; let rest = argument; + while (tree.observe(rest).node.kind !== 'leaf') { + if (bytes.length === 4096) throw new OperationError('invalid-argument'); + const [head, tail] = wire.pair(rest), byte = canonicalNat(tree, head, 8); + if (byte === undefined) throw new OperationError('invalid-argument'); + bytes.push(Number(byte)); rest = tail; + } + if (!bytes.length) return bad('invalid-bound'); + if (bytes.length > outputBudget) return bad('invalid-bound'); + outputBudget -= bytes.length; + reply = await events.write(slot.name === 'error' ? 'error' : 'output', bytes, signal); + if (reply.kind === 'write') { const count = reply.count; return () => wire.result(true, tree.natural(BigInt(count))); } + } + if (!read) slot.poisoned = true; + return bad(reply.kind === 'cancelled' ? 'cancelled' : 'io-error', 'unknown'); + } finally { registry.endLease(lease.token); } + } catch (error) { + if (!(error instanceof OperationError)) throw error; + return bad(error.message); + } + } + }; +} +export type Command = { run: string; kind: 'poll' | 'stop' } | { run: string; kind: 'reply'; reply: unknown }; +export class Session<H> { + readonly events: Events; + readonly core: Core<H, Slot>; + private busy = false; + private finished = false; + constructor(readonly tree: Trees<H>, action: H, readonly run: string, private readonly sweep: (roots: Iterable<H>) => void, private readonly options: { summarize?: boolean; steps?: number; backend?: string } = {}) { + if (options.steps !== undefined && (!Number.isInteger(options.steps) || options.steps < 1 || options.steps > 128)) throw new Error('invalid session step bound'); + this.events = new Events(run); + this.core = new Core(tree, BigInt(run), action, + ['input', 'output', 'error'].map(name => ({ name, poisoned: false, eof: false })) as [Slot, Slot, Slot], provider(this.events, options.backend ?? 'browser')); + sweep(this.core.roots()); + } + async turn(command: Command) { + if (this.busy) throw new Error('session turn already active'); + if (this.finished || command.run !== this.run) throw new Error('stale session run'); + if (!['poll', 'stop', 'reply'].includes(command.kind)) throw new Error('invalid session command'); + this.busy = true; + try { + const accepted = command.kind === 'reply' ? this.events.reply(command.reply) : null; + if (command.kind === 'stop') this.core.stop(this.tree.leaf); + // Never await a terminal operation. Bounded progress batches leave the + // transport available for multiple replies, polls and structured stop. + let progress = false; + for (let turns = 0; turns < (this.options.steps ?? 128) && !this.core.result; turns++) { + progress = this.core.advance(); this.sweep(this.core.roots()); + await Promise.resolve(); + if (!progress) break; + } + const outcome = this.core.result; + if (outcome) this.finished = true; + return { run: this.run, accepted, waiting: !progress && this.events.counts.pending > 0 && this.core.work.counts.pending === this.events.counts.pending && this.core.work.counts.replies === 0, done: !!outcome, events: this.events.take(), + outcome: outcome ? { kind: outcome.kind, value: outcome.kind === 'completed' && this.options.summarize !== false ? canonicalNat(this.tree, outcome.value, 64)?.toString() ?? null : null, + cleanup: outcome.cleanup?.items.length ?? 0, fault: this.core.hasFault } : null, + counts: { tasks: this.core.lifecycle.tasks.counts, resources: this.core.lifecycle.resources.counts, + work: this.core.work.counts, events: this.events.counts } }; + } finally { this.busy = false; } + } +} diff --git a/ext/demo-site/src/io-v2-tasks.ts b/ext/demo-site/src/io-v2-tasks.ts @@ -0,0 +1,271 @@ +// Browser structured lifecycle, independent of tree evaluation and host facilities. +// One owning event loop. No promises/callbacks execute inside these transitions. +// Host cancellation is a request: release waits for explicit completion acknowledgement. +// Resource admission/transfer/reverse release belong to the embedding registry; +// this machine is not, by itself, an executable IO interpreter. +export interface ResourceGuard { readyForRelease(scope: number): boolean; finishScope(scope: number): void; } +export type Kind = 'completed' | 'failed' | 'cancelled'; +export interface Diagnostics<T> { readonly items: readonly T[]; readonly omitted: number; } +export interface Outcome<T> { readonly kind: Kind; readonly value: T; readonly cleanup?: Diagnostics<T>; } +export interface Token { readonly run: bigint; readonly key: number; } +export interface HostToken extends Token { readonly scope: number; readonly kind: 'body' | 'release'; } +export const hostKey = (token: HostToken): string => `${token.kind}:${token.key}`; +type Phase = 'open' | 'joining' | 'releasing'; +type ScopeKind = 'root' | 'lexical' | 'spawned'; +interface Group<T> { + parent: number | null; task: number; kind: ScopeKind; propagate: boolean; + phase: Phase; reason: Outcome<T> | null; body: Outcome<T> | null; + cleanup: Diagnostics<T>; lexical: number | null; wait: number | null; + hostCalls: Set<string>; releaseStarted: boolean; +} +interface Task<T> { + owner: number; supervised: boolean; ticket: boolean; + waiter: number | null; outcome: Outcome<T> | null; +} +const empty = <T>(): Diagnostics<T> => ({ items: [], omitted: 0 }); +const primary = <T>(outcome: Outcome<T>): Outcome<T> => ({ kind: outcome.kind, value: outcome.value }); +function append<T>(left: Diagnostics<T>, right: Diagnostics<T>): Diagnostics<T> { + if (!Number.isSafeInteger(right.omitted) || right.omitted < 0) throw new Error('invalid diagnostics'); + const available = 16 - left.items.length; + return { items: [...left.items, ...right.items.slice(0, available)], + omitted: Math.min(Number.MAX_SAFE_INTEGER, left.omitted + right.omitted + Math.max(0, right.items.length - available)) }; +} +const capture = <T>(outcome: Outcome<T>): Outcome<T> => { + if (!['completed', 'failed', 'cancelled'].includes(outcome.kind)) throw new Error('invalid outcome'); + return { ...primary(outcome), cleanup: append(empty(), outcome.cleanup ?? empty()) }; +}; +export class Tasks<T> { + private readonly groups = new Map<number, Group<T>>(); + private readonly tasks = new Map<number, Task<T>>(); + private readonly hosts = new Map<string, { token: HostToken; cancelling: boolean }>(); + private nextScope = 1; + private nextHost = 1; + constructor(readonly identity: bigint, private readonly ownerStopped: T, private readonly cycle: T, + private readonly identityLimit = Number.MAX_SAFE_INTEGER, private readonly resources?: ResourceGuard) { + if (typeof identity !== 'bigint' || identity <= 0n || identity > 0xffffffffffffffffn || + !Number.isSafeInteger(identityLimit) || identityLimit < 1) throw new Error('invalid run identity or limit'); + this.groups.set(0, this.group(null, 0, 'root', false)); + } + private group(parent: number | null, task: number, kind: ScopeKind, propagate: boolean): Group<T> { + return { parent, task, kind, propagate, phase: 'open', reason: null, body: null, + cleanup: empty(), lexical: null, wait: null, hostCalls: new Set(), releaseStarted: false }; + } + private get(scope: number): Group<T> { + const group = this.groups.get(scope); + if (!group) throw new Error('closed-scope'); + return group; + } + // Read-only copies: embeddings cannot mutate lifecycle state or diagnostic arrays. + state(scope: number) { + const g = this.get(scope); + return { parent: g.parent, task: g.task, kind: g.kind, phase: g.phase, + reason: g.reason && primary(g.reason), lexical: g.lexical, wait: g.wait, hostCalls: g.hostCalls.size }; + } + get counts() { return { scopes: this.groups.size, tasks: this.tasks.size, hosts: this.hosts.size }; } + scopeIds(): number[] { return [...this.groups.keys()]; } + within(current: number, owner: number): boolean { + for (let scope: number | null = current; scope !== null;) { + const g = this.groups.get(scope); + if (!g) return false; + if (scope === owner) return true; + scope = g.parent; + } + return false; + } + checkpoint(scope: number): void { + const g = this.get(scope); + if (g.reason) throw new Error('stopped'); + if (g.phase !== 'open' || g.lexical !== null || g.wait !== null) throw new Error('invalid-transition'); + } + private scopeId(): number { + if (this.nextScope > this.identityLimit) throw new Error('scope-identity-exhausted'); + return this.nextScope++; + } + nested(parent: number): number { + this.checkpoint(parent); + const id = this.scopeId(), child = this.group(parent, this.get(parent).task, 'lexical', true); + this.groups.set(id, child); this.get(parent).lexical = id; + return id; + } + spawn(owner: number, supervised: boolean, keep: boolean): Token { + this.checkpoint(owner); + if (typeof supervised !== 'boolean' || typeof keep !== 'boolean') throw new Error('invalid-argument'); + const id = this.scopeId(), child = this.group(owner, id, 'spawned', !supervised); + this.groups.set(id, child); + this.tasks.set(id, { owner, supervised, ticket: keep, waiter: null, outcome: null }); + return { run: this.identity, key: id }; + } + private recordStop(scope: number, reason: Outcome<T>): boolean { + const g = this.groups.get(scope); + if (!g || g.reason) return false; + g.reason = primary(reason); return true; + } + stop(scope: number, reason: Outcome<T>): void { + if (reason.kind !== 'failed' && reason.kind !== 'cancelled') throw new Error('invalid stop'); + if (!this.recordStop(scope, reason)) return; + // Fixed point propagates first failure upward and owner cancellation downward. + for (let changed = true; changed;) { + changed = false; + for (const [id, g] of this.groups) if (g.parent !== null) { + if (g.reason?.kind === 'failed' && g.propagate) changed = this.recordStop(g.parent, g.reason) || changed; + if (this.get(g.parent).reason) changed = this.recordStop(id, { kind: 'cancelled', value: this.ownerStopped }) || changed; + } + } + } + private checked(current: number, token: Token): Task<T> { + if (token.run !== this.identity) throw new Error('stale-task'); + const task = this.tasks.get(token.key); + if (!task) throw new Error('stale-task'); + if (!this.within(current, task.owner)) throw new Error('out-of-scope'); + return task; + } + // Edges are task identities, not lexical scope IDs. Normal scope exit adds + // implicit edges to unfinished children; explicit join cycle checks include them. + private reaches(start: number, wanted: number): boolean { + const pending = [start], seen = new Set<number>(); + while (pending.length) { + const task = pending.pop()!; + if (task === wanted) return true; + if (seen.has(task)) continue; + seen.add(task); + for (const [id, g] of this.groups) if (g.task === task) { + if (g.wait !== null) pending.push(g.wait); + if (g.phase === 'joining') for (const [child, t] of this.tasks) + if (t.owner === id && t.outcome === null) pending.push(child); + } + } + return false; + } + claimJoin(current: number, token: Token): void { + this.checkpoint(current); + const task = this.checked(current, token); + if (!task.ticket) throw new Error('stale-task'); + if (this.within(current, token.key) || this.reaches(token.key, this.get(current).task)) throw new Error('join-cycle'); + task.ticket = false; task.waiter = current; this.get(current).wait = token.key; + } + pollJoin(current: number): Outcome<T> | null { + const g = this.get(current); + if (g.wait === null) throw new Error('invalid-transition'); + const task = this.tasks.get(g.wait)!; + if (!task.outcome) return null; + const outcome = capture(task.outcome); + this.tasks.delete(g.wait); g.wait = null; + return outcome; + } + abandonJoin(current: number): void { + const g = this.groups.get(current); + if (!g || g.wait === null) return; + const task = this.tasks.get(g.wait)!; + task.waiter = null; + if (task.outcome) this.tasks.delete(g.wait); + g.wait = null; + } + discard(current: number, token: Token): void { + this.checkpoint(current); + const task = this.checked(current, token); + if (!task.ticket) throw new Error('stale-task'); + task.ticket = false; + if (task.outcome) this.tasks.delete(token.key); + } + cancel(current: number, token: Token, reason: T): void { + this.checkpoint(current); + const task = this.checked(current, token); + if (task.outcome) throw new Error('stale-task'); + this.stop(token.key, { kind: 'cancelled', value: reason }); + } + bodyDone(scope: number, outcome: Outcome<T>): void { + const g = this.get(scope), captured = capture(outcome); + if (g.phase !== 'open' || g.lexical !== null) throw new Error('invalid-transition'); + this.abandonJoin(scope); + if (captured.kind !== 'completed') this.stop(scope, captured); + g.body = captured; + for (const [id, t] of this.tasks) if (t.owner === scope && !t.outcome && this.reaches(id, g.task)) { + this.stop(scope, { kind: 'failed', value: this.cycle }); break; + } + g.phase = 'joining'; + } + beginHost(scope: number): HostToken { + this.checkpoint(scope); + return this.registerHost(scope, 'body'); + } + beginReleaseHost(scope: number): HostToken { + const group = this.get(scope); + if (group.phase !== 'releasing' || group.releaseStarted) throw new Error('invalid-transition'); + // One cleanup batch per scope uses its already-reserved identity, in a + // separate namespace. Body-call identity exhaustion cannot prevent cleanup. + const token = this.registerHost(scope, 'release'); + group.releaseStarted = true; + return token; + } + private registerHost(scope: number, kind: HostToken['kind']): HostToken { + if (kind === 'body' && this.nextHost > this.identityLimit) throw new Error('host-identity-exhausted'); + const token = Object.freeze({ run: this.identity, key: kind === 'body' ? this.nextHost++ : scope, scope, kind }); + const key = hostKey(token); + this.hosts.set(key, { token, cancelling: false }); this.get(scope).hostCalls.add(key); + return token; + } + // Delivery is separate from acknowledgement. Issuing AbortController.abort() + // cannot settle a task whose host callback/lease is still outstanding. + takeCancellations(): HostToken[] { + const pending: HostToken[] = []; + for (const call of this.hosts.values()) if (call.token.kind === 'body' && !call.cancelling && this.get(call.token.scope).reason) { + call.cancelling = true; pending.push(call.token); + } + return pending; + } + endHost(token: HostToken): 'reply' | 'ignored' | 'stale' { + const key = hostKey(token), call = this.hosts.get(key); + if (token.run !== this.identity || !call || call.token.scope !== token.scope || call.token.kind !== token.kind) return 'stale'; + const g = this.get(token.scope); + this.hosts.delete(key); g.hostCalls.delete(key); + return token.kind === 'release' || !g.reason && !g.body ? 'reply' : 'ignored'; + } + beginRelease(scope: number): boolean { + const g = this.get(scope); + if (g.phase !== 'joining') throw new Error('invalid-transition'); + if (g.hostCalls.size || [...this.tasks.values()].some(t => t.owner === scope && !t.outcome) || + this.resources && !this.resources.readyForRelease(scope)) return false; + g.phase = 'releasing'; return true; + } + // Call only after the embedding has physically released this scope's resources. + settle(scope: number, ownCleanup: Diagnostics<T> = empty()): Outcome<T> { + const g = this.get(scope); + if (g.phase !== 'releasing' || !g.body || g.hostCalls.size) throw new Error('invalid-transition'); + let final = primary(g.reason ?? g.body); + let errors = append<T>(append<T>(g.body.cleanup ?? empty<T>(), g.cleanup), ownCleanup); + if (final.kind === 'completed' && errors.items.length) { + final = { kind: 'failed', value: errors.items[0]! }; + errors = { items: errors.items.slice(1), omitted: errors.omitted }; + } + this.resources?.finishScope(scope); + final = { ...final, cleanup: errors }; + if (final.kind === 'failed') this.stop(scope, final); + for (const [id, task] of this.tasks) if (task.owner === scope) this.tasks.delete(id); + if (g.parent !== null) { + const parent = this.get(g.parent); + if (g.kind === 'lexical') { + parent.cleanup = append(parent.cleanup, errors); parent.lexical = null; + } else { + const task = this.tasks.get(scope)!; + if (!task.supervised) parent.cleanup = append(parent.cleanup, errors); + task.outcome = final; + if (!task.ticket && task.waiter === null) this.tasks.delete(scope); + } + } + this.groups.delete(scope); + return g.kind === 'lexical' ? primary(final) : capture(final); + } + // Evaluator roots, in addition to embedding-owned bodies, continuations and + // pending host arguments. No task/result history remains after consumption. + *payloads(): Iterable<T> { + yield this.ownerStopped; yield this.cycle; + for (const g of this.groups.values()) { + if (g.reason) yield g.reason.value; + if (g.body) { yield g.body.value; yield* g.body.cleanup?.items ?? []; } + yield* g.cleanup.items; + } + for (const task of this.tasks.values()) if (task.outcome) { + yield task.outcome.value; yield* task.outcome.cleanup?.items ?? []; + } + } +} diff --git a/ext/demo-site/src/io-v2-terminal-view.ts b/ext/demo-site/src/io-v2-terminal-view.ts @@ -0,0 +1,82 @@ +import { Terminal, type Reply } from './io-v2-terminal.js'; +import type { Event } from './io-v2-events.js'; +// One identity-bound UI per run. Callbacks enqueue cloneable replies/stop requests; +// they must not synchronously re-enter this widget or throw. +export function terminalView(parent: HTMLElement, run: string, reply: (value: Reply) => void, stop: () => void) { + const terminal = new Terminal(run), doc = parent.ownerDocument; + const root = doc.createElement('section'), status = doc.createElement('p'); + const outputs = ['output', 'error'].map(name => { + const section = doc.createElement('section'), title = doc.createElement('h4'); title.textContent = name === 'output' ? 'Standard output' : 'Standard error'; + const text = doc.createElement('pre'), details = doc.createElement('details'), summary = doc.createElement('summary'), hex = doc.createElement('pre'); + summary.textContent = 'Exact bytes (hexadecimal)'; details.append(summary, hex); section.append(title, text, details); root.append(section); + text.dataset.stream = name; hex.dataset.hex = name; + return { text, hex }; + }); + let form = doc.createElement('form'), clearInput = () => {}, waitingId: number | undefined; + const stopping = doc.createElement('button'); + stopping.textContent = 'Stop (cancel and wait for cleanup)'; stopping.type = 'button'; + root.append(form, stopping, status); parent.append(root); + let stopped = false, finished = false; + const onStop = () => { if (stopped || finished) return; stopped = true; stopping.disabled = true; status.textContent = 'Cancelling; waiting for cleanup…'; stop(); }; + stopping.addEventListener('click', onStop); + function inputView(): void { + const wait = terminal.waiting; + if (wait?.id === waitingId) { form.hidden = !wait; return; } + clearInput(); + const fresh = doc.createElement('form'); form.replaceWith(fresh); form = fresh; form.hidden = !wait; waitingId = wait?.id; + if (!wait) return; + const label = doc.createElement('label'), input = doc.createElement('input'), send = doc.createElement('button'), eof = doc.createElement('button'); + label.textContent = 'One line, at most 256 Unicode scalars (Send appends LF)'; input.maxLength = 512; label.append(input); + send.textContent = 'Send'; send.type = 'submit'; eof.textContent = 'End input (EOF)'; eof.type = 'button'; + form.append(label, send, eof); + function submit(end: boolean): void { + const result = end ? terminal.endInput(wait!.run, wait!.id) : terminal.line(wait!.run, wait!.id, input.value); + if (!result) { status.textContent = 'Enter at most 256 Unicode scalars without CR or LF.'; return; } + render(); reply(result); + } + const onSubmit = (event: SubmitEvent) => { event.preventDefault(); submit(false); }, onEOF = () => submit(true); + form.addEventListener('submit', onSubmit); eof.addEventListener('click', onEOF); + const owned = form; + clearInput = () => { owned.removeEventListener('submit', onSubmit); eof.removeEventListener('click', onEOF); input.value = ''; }; + } + function render(): void { + const snapshot = terminal.output.snapshot(); + [snapshot.output, snapshot.error].forEach((view, index) => { + const target = outputs[index]!; target.text.replaceChildren(); target.hex.textContent = view.hex; + for (const token of view.tokens) { + if (token.kind === 'text') target.text.append(doc.createTextNode(token.text)); + else { + const span = doc.createElement('mark'); span.dataset.byteReason = token.reason; + span.title = `${token.reason} UTF-8 bytes at stream offset ${token.offset}; see exact hex view`; + span.textContent = token.bytes.map(b => `\\x${b.toString(16).padStart(2, '0')}`).join(''); target.text.append(span); + } + } + }); + inputView(); + if (!stopped) status.textContent = terminal.waiting ? 'Waiting for a line or explicit EOF.' : 'Running…'; + } + render(); + return { + root, + handle(event: Event): void { + if (finished) throw new Error('terminal view retired'); + const result = terminal.handle(event); + // Physically hide/clear an abandoned input BEFORE acknowledging cancel. + render(); if (result) reply(result); + }, + finish(): void { + terminal.finish(); finished = true; render(); + clearInput(); stopping.removeEventListener('click', onStop); + form.remove(); stopping.disabled = true; status.textContent = 'Settled.'; + }, + // Fatal runtime loss/navigation is NOT a physical cleanup acknowledgement. + // Retire DOM callbacks without fabricating a settled interpreter outcome. + abandon(): void { + if (finished) return; + finished = true; clearInput(); form.remove(); + stopping.removeEventListener('click', onStop); stopping.disabled = true; + status.textContent = 'Runtime lost; cleanup was not acknowledged.'; + }, + snapshot: () => terminal.output.snapshot() + }; +} diff --git a/ext/demo-site/src/io-v2-terminal.ts b/ext/demo-site/src/io-v2-terminal.ts @@ -0,0 +1,60 @@ +// Terminal provider; cloneable identities only, independent of evaluators. +import type { Event, Completion } from './io-v2-events.js'; +import { OutputBuffer } from './io-v2-output.js'; +export type Reply = Completion & { run: string; id: number }; +export class Terminal { + readonly output = new OutputBuffer(); + private nextAfter = 0; + private reader: Extract<Event, { kind: 'read' }> | undefined; + private buffered: number[] = []; + private eof = false; + private ended = false; + constructor(readonly run: string) { + if (!/^[1-9][0-9]*$/.test(run) || run.length > 20 || BigInt(run) > 0xffffffffffffffffn) throw new Error('invalid terminal run'); + } + get waiting(): { run: string; id: number } | undefined { + return this.reader ? { run: this.run, id: this.reader.id } : undefined; + } + private read(): Reply | undefined { + const reader = this.reader; + if (!reader || !this.buffered.length && !this.eof) return; + const bytes = this.buffered.splice(0, reader.limit); this.reader = undefined; + return { run: this.run, id: reader.id, kind: 'read', bytes, eof: bytes.length === 0 }; + } + handle(event: Event): Reply | undefined { + if (this.ended || event.run !== this.run || !Number.isSafeInteger(event.id) || event.id < 1) throw new Error('invalid terminal event'); + if (event.kind === 'cancel') { + if (event.id > this.nextAfter || !['read', 'write'].includes(event.operation)) throw new Error('invalid terminal cancellation'); + // Completed synchronous writes/reads already have a normal acknowledgement + // queued. A racing abort does not erase it or manufacture a second reply. + if (this.reader?.id !== event.id) return; + if (event.operation !== 'read') throw new Error('invalid terminal cancellation'); + this.reader = undefined; + return { run: this.run, id: event.id, kind: 'cancelled' }; + } + if (event.id <= this.nextAfter) throw new Error('stale terminal event'); + if (event.kind === 'read') { + if (this.reader || !Number.isInteger(event.limit) || event.limit < 1 || event.limit > 4096) throw new Error('invalid terminal read'); + this.reader = { ...event }; + } else if (event.kind === 'write') this.output.append(event.stream, event.bytes); + else throw new Error('invalid terminal operation'); + this.nextAfter = event.id; + return event.kind === 'read' ? this.read() : { run: this.run, id: event.id, kind: 'write', count: event.bytes.length }; + } + // Explicit one-line UI: no CR/LF in the submitted text, append ONE LF; an + // empty line is data, never EOF. Scalars, not UTF-16 code units, are bounded. + line(run: string, id: number, value: unknown): Reply | undefined { + if (run !== this.run || this.reader?.id !== id || typeof value !== 'string' || value.length > 512 || + /[\r\n\uD800-\uDFFF]/u.test(value) || Array.from(value).length > 256) return; + this.buffered = [...new TextEncoder().encode(value), 10]; + return this.read(); + } + endInput(run: string, id: number): Reply | undefined { + if (run !== this.run || this.reader?.id !== id) return; + this.eof = true; return this.read(); + } + finish(): void { + if (this.reader) throw new Error('terminal input still pending'); + this.ended = true; this.buffered = []; this.output.finish(); + } +} diff --git a/ext/demo-site/src/io-v2-utf8.ts b/ext/demo-site/src/io-v2-utf8.ts @@ -0,0 +1,39 @@ +// Strict stateless UTF-8. No BOM stripping, replacement or normalization. +// Source carries the copied <=3-byte suffix between requests. +export class UTF8Error extends Error {} +export function decodeUTF8(bytes: readonly number[], final: boolean): { text: string; carry: number[] } { + if (bytes.length > 4096 || bytes.some(b => !Number.isInteger(b) || b < 0 || b > 255)) throw new UTF8Error('invalid byte chunk'); + const chars: string[] = []; + const bad = (offset: number, why: string): never => { throw new UTF8Error(`${why} at byte ${offset}`); }; + for (let offset = 0; offset < bytes.length;) { + const lead = bytes[offset]!; + if (lead <= 0x7f) { chars.push(String.fromCodePoint(lead)); offset++; continue; } + let count: number, low = 0x80, high = 0xbf, code: number; + if (lead >= 0xc2 && lead <= 0xdf) { count = 2; code = lead & 0x1f; } + else if (lead >= 0xe0 && lead <= 0xef) { + count = 3; code = lead & 0x0f; if (lead === 0xe0) low = 0xa0; if (lead === 0xed) high = 0x9f; + } else if (lead >= 0xf0 && lead <= 0xf4) { + count = 4; code = lead & 7; if (lead === 0xf0) low = 0x90; if (lead === 0xf4) high = 0x8f; + } else return bad(offset, 'invalid UTF-8 leading byte'); + const available = Math.min(count - 1, bytes.length - offset - 1); + for (let i = 1; i <= available; i++) { + const b = bytes[offset + i]!; + if (b < (i === 1 ? low : 0x80) || b > (i === 1 ? high : 0xbf)) return bad(offset, 'invalid UTF-8 sequence'); + code = code * 64 + (b & 63); + } + if (available < count - 1) { + if (final) return bad(offset, 'incomplete UTF-8 sequence'); + return { text: chars.join(''), carry: bytes.slice(offset) }; + } + chars.push(String.fromCodePoint(code)); offset += count; + } + return { text: chars.join(''), carry: [] }; +} +export function encodeUTF8(text: string): number[] { + let count = 0; + for (const char of text) { + const scalar = char.codePointAt(0)!; + if (++count > 1024 || scalar >= 0xd800 && scalar <= 0xdfff) throw new UTF8Error('invalid scalar chunk'); + } + return [...new TextEncoder().encode(text)]; +} diff --git a/ext/demo-site/src/io.ts b/ext/demo-site/src/io.ts @@ -1,56 +1,10 @@ -import { Graph, type NodeId } from './graph.js'; -import { readNatural, readText, line } from './codecs.js'; export interface Trace { step: number; name: string; detail: string; depth: number; } export interface IOHost { + selected?(version: 2): void; + readonly v2?: import('./io-v2-driver.js').V2Host; before(event: Trace): Promise<void>; - after(event: { step: number; name: string; value: string }): void; - write(text: string): void; - read(): Promise<string>; - running(): void; -} -// collectActions requires exclusive ownership: unrelated caller handles may be -// invalidated unless included in retainedRoots. Normal callers retain the -// non-collecting API by default. Snapshot extra roots before the first action. -export async function runIO(graph: Graph, handle: NodeId, host: IOHost, collectActions = false, retainedRoots: Iterable<NodeId> = []): Promise<NodeId> { - const retained = [...retainedRoots]; - const [sentinel, body] = graph.pair(handle, 'IO envelope'); - if (readText(graph, sentinel, 7) !== 'tricuIO') throw new Error('Missing tricuIO sentinel'); - const [version, initial] = graph.pair(body, 'IO version/action'); - if (readNatural(graph, version) !== 1n) throw new Error('Unsupported tricuIO version'); - const frames: NodeId[] = []; let action = initial, output = 0; - const names = new Map([[0n, 'pure'], [1n, 'bind'], [2n, 'abortIO'], [10n, 'putStr'], [11n, 'getLine']]); - for (let step = 1; step <= 128; step++) { - if (collectActions) graph.collect([action, ...frames, ...retained]); - const [tag, payload] = graph.pair(action, 'action'); - const code = readNatural(graph, tag), name = names.get(code); - if (!name) throw new Error(`Unsupported browser IO action ${code}`); - // Don't inspect arbitrary pure/abort payloads merely to build a trace. - const detail = code === 10n ? readText(graph, payload) : `node #${payload}`; - await host.before({ step, name, detail, depth: frames.length }); - if (code === 1n) { - const [left, continuation] = graph.pair(payload, 'bind'); - frames.push(continuation); action = left; - host.after({ step, name, value: 'continuation handle saved' }); continue; - } - if (code === 2n) throw new Error(`Execution aborted (diagnostic node #${payload}; no payload forcing).`); - let value: NodeId; - if (code === 0n) value = payload; - else if (code === 10n) { - output += Array.from(detail).length; - if (output > 16_384) throw new Error('Terminal output limit exceeded'); - host.write(detail); value = graph.leaf; - } else value = line(graph, await host.read()); - host.after({ step, name, value: `node #${value}` }); - if (!frames.length) return value; - host.running(); action = graph.app(frames.pop()!, value); - } - throw new Error('IO action limit exceeded'); -} -// Ignore malformed/wrong-kind/stale resumes without consuming the waiting token. -// The page validates interactively; the Worker must also retain a retryable wait. -export function resumeValue(kind: 'input' | 'step', value: unknown): string | undefined { - if (kind === 'step') return value === undefined ? '' : undefined; - if (typeof value !== 'string' || value.length > 512 || /[\r\n\uD800-\uDFFF]/u.test(value) || Array.from(value).length > 256) return undefined; - return value; } +// A scheduler-step acknowledgement carries no terminal text. Malformed replies +// leave the waiting token live; terminal input uses run-bound v2 byte replies. +export function validStepResume(value: unknown): boolean { return value === undefined; } diff --git a/ext/demo-site/src/protocol.ts b/ext/demo-site/src/protocol.ts @@ -12,21 +12,20 @@ export interface CatalogueEntry { presets?: Record<string, { arguments: unknown[]; output: OutputType; mode: 'pure' | 'io' }>; } export type InspectionReply = { type: 'inspected'; id: number; io: true } | { type: 'inspected'; id: number; result: ValueView } | { type: 'inspection-error'; id: number; message: string; fatal: boolean }; -export type ClientMessage = { type: 'inspect'; id: number; name: string; output: import('./auto-interpretation.js').InspectionFormat } +export type ClientMessage = { type: 'v2-command'; command: import('./io-v2-session.js').Command } | { type: 'io-stop' } | { type: 'inspect'; id: number; name: string; output: import('./auto-interpretation.js').InspectionFormat } | { type: 'run-io'; name: string; output: OutputType; step: boolean } | { type: 'load'; hash: BundleHash; localId?: string } | { type: 'run'; export: string; arguments: unknown; expression?: string; output: OutputType; mode: 'pure' | 'io'; step: boolean } | { type: 'download' } | { type: 'export'; export: string; format: TreeFormat } - | { type: 'resume'; id: RequestId; value?: string }; -export type WorkerMessage = InspectionReply | { type: 'module-exported'; bytes: Uint8Array; hash: BundleHash; manifest: string } + | { type: 'resume'; id: RequestId }; +export type WorkerMessage = InspectionReply | { type: 'io-version'; version: 2 } + | { type: 'v2-open'; run: string } | { type: 'v2-event'; event: import('./io-v2-events.js').Event } | { type: 'v2-close'; run: string; settled: boolean } | { type: 'module-exported'; bytes: Uint8Array; hash: BundleHash; manifest: string } | { type: 'module-export-error'; message: string } | { type: 'state'; state: 'running' } - | { type: 'state'; state: 'input' | 'step'; id: RequestId } + | { type: 'state'; state: 'step'; id: RequestId } | { type: 'loaded'; hash: BundleHash; exports: ExportInfo[]; nodes: number; metadata: Record<string, string>; source: string | null } | { type: 'trace'; phase: 'before'; event: Trace } - | { type: 'trace'; phase: 'after'; event: { step: number; name: string; value: string } } - | { type: 'output'; text: string } | { type: 'done'; result: ValueView; stats: RuntimeStats } | { type: 'exported'; hash: BundleHash; export: string; format: TreeFormat; file: TreeFile } | { type: 'downloaded'; hash: BundleHash; bytes: Uint8Array } diff --git a/ext/demo-site/src/wasm-worker.ts b/ext/demo-site/src/wasm-worker.ts @@ -1,7 +1,7 @@ import { autoInterpretation } from './auto-interpretation.js'; import { importPlan } from './import-plan.js'; import { localModuleInputs, validateLocalModule } from './local-modules.js'; -import { resumeValue } from './io.js'; +import { validStepResume } from './io.js'; import { createRuntimeSession, type RuntimeSession } from './runtime-session.js'; const selectedBackend = new URL(globalThis.location.href).searchParams.get('backend'); import { publishedBundle } from './published-bundle.js'; @@ -16,20 +16,46 @@ const send = (m: CompilerMessage | WorkerMessage) => scope.postMessage(m); const running = () => send({ type: 'state', state: 'running' }); let session: RuntimeSession | undefined, stored: Awaited<ReturnType<typeof publishedBundle>> | undefined; let busy = false, requestId = 0; -let pending: { id: RequestId; resolve(value: string): void; reject(error: Error): void; kind: 'input' | 'step' } | undefined; -function request(kind: 'input' | 'step'): Promise<string> { +let pending: { id: RequestId; resolve(): void; reject(error: Error): void } | undefined; +function requestStep(): Promise<void> { if (pending) throw new Error('Driver already waiting'); + if (requestId >= Number.MAX_SAFE_INTEGER) throw new Error('Driver request identity exhausted'); const id = ++requestId as RequestId; - return new Promise((resolve, reject) => { pending = { id, resolve, reject, kind }; send({ type: 'state', state: kind, id }); }); + return new Promise((resolve, reject) => { pending = { id, resolve, reject }; send({ type: 'state', state: 'step', id }); }); +} +let v2Command: ((command: import('./io-v2-session.js').Command) => void) | undefined, v2Run: string | undefined, stopRequested = false; +function host(stepping: boolean): import('./io.js').IOHost { + stopRequested = false; + return { + selected(version) { send({ type: 'io-version', version }); }, + async before(event) { send({ type: 'trace', phase: 'before', event }); if (stepping) await requestStep(); running(); }, + v2: { + stepping, + open(run, command) { v2Run = run; v2Command = command; send({ type: 'v2-open', run }); if (stopRequested) command({ run, kind: 'stop' }); }, + event(event) { send({ type: 'v2-event', event }); }, + close(run, settled) { v2Command = undefined; v2Run = undefined; pending = undefined; send({ type: 'v2-close', run, settled }); }, + }, + }; } scope.onmessage = ({ data }) => { if (!data || typeof data !== 'object' || Array.isArray(data)) return; const m = data as Record<string, unknown>; + if (m['type'] === 'io-stop') { + if (!busy) return; + stopRequested = true; + if (v2Run) v2Command?.({ run: v2Run, kind: 'stop' }); + if (pending) { const waiter = pending; pending = undefined; waiter.resolve(); } + return; + } + if (m['type'] === 'v2-command') { + const command = m['command']; + if (command && typeof command === 'object') v2Command?.(command as import('./io-v2-session.js').Command); + return; + } if (m['type'] === 'resume') { if (!pending || m['id'] !== pending.id) return; - const value = resumeValue(pending.kind, m['value']); - if (value === undefined) return; - const waiter = pending; pending = undefined; waiter.resolve(value); + if (!validStepResume(m['value'])) return; + const waiter = pending; pending = undefined; waiter.resolve(); return; } if (busy) return; @@ -82,25 +108,17 @@ scope.onmessage = ({ data }) => { } else if (m['type'] === 'run-io') { if (!session || typeof m['name'] !== 'string' || m['name'].length > 4096) throw new Error('Invalid IO definition'); const output = outputType(m['output']); running(); - const result = await session.runDefinitionIO(m['name'], output, { - async before(event) { send({ type: 'trace', phase: 'before', event }); if (m['step'] === true) await request('step'); running(); }, - after(event) { send({ type: 'trace', phase: 'after', event }); }, write(text) { send({ type: 'output', text }); }, - read() { return request('input'); }, running, - }); + const result = await session.runDefinitionIO(m['name'], output, host(m['step'] === true)); send({ type: 'done', result, stats: session.stats() }); } else if (m['type'] === 'run') { if (!session) throw new Error('Load or compile first'); if (typeof m['export'] !== 'string' || m['export'].length > 512) throw new Error('Invalid export'); if (m['mode'] !== 'pure' && m['mode'] !== 'io') throw new Error('Unsupported execution mode'); const args = literals(m['arguments']), output = outputType(m['output']); running(); - const host: import('./io.js').IOHost = { - async before(event) { send({ type: 'trace', phase: 'before', event }); if (m['step'] === true) await request('step'); running(); }, - after(event) { send({ type: 'trace', phase: 'after', event }); }, write(text) { send({ type: 'output', text }); }, - read() { return request('input'); }, running, - }; + const ioHost = host(m['step'] === true); const result = m['expression'] === undefined - ? await session.run(m['export'], args, output, m['mode'], host) - : await session.runExpression(sourceText(m['expression']), output, m['mode'], host); + ? await session.run(m['export'], args, output, m['mode'], ioHost) + : await session.runExpression(sourceText(m['expression']), output, m['mode'], ioHost); send({ type: 'done', result, stats: session.stats() }); } else if (m['type'] === 'export' && stored) { try { diff --git a/ext/demo-site/src/zig-helpers.ts b/ext/demo-site/src/zig-helpers.ts @@ -8,3 +8,9 @@ export * as expressions from './expression.js'; export * as treeResults from './tree-result.js'; export * as modules from './module-stage.js'; export * as moduleBundle from './module-bundle.js'; +export * as ioV2Protocol from './io-v2-protocol.js'; +export * as ioV2Private from './io-v2-private.js'; +export * as ioV2Session from './io-v2-session.js'; + +export * as ioV2Profile from './io-v2-profile.js'; +export * as ioV2Driver from './io-v2-driver.js'; diff --git a/ext/demo-site/style.css b/ext/demo-site/style.css @@ -4,7 +4,7 @@ .workspace{max-width:1064px;margin:0 auto;border:1px solid var(--line);border-radius:12px;background:var(--panel);overflow:clip;scroll-margin-top:16px}.editor-toolbar{position:sticky;top:0;z-index:5;display:flex;align-items:end;gap:12px;flex-wrap:wrap;padding:16px 20px;border-bottom:1px solid var(--line);background:var(--toolbar)}.toolbar-selection{flex:1 1 220px;min-width:0}.toolbar-runtime{flex:0 1 150px;min-width:115px}.toolbar-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;max-width:100%}.editor-toolbar #status{flex-basis:100%;font-size:12px;color:var(--muted);min-height:18px}.editor-body{display:grid;grid-template-columns:minmax(0,1fr)}.editor-body.settings-open{grid-template-columns:minmax(0,1fr) 320px}.editor-main{min-width:0}.source-panel{background:var(--code);color:#e3edde;min-width:0}.source-heading{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px 20px 8px}.source-heading h2{font-size:14px;letter-spacing:0;margin:0}.source-heading span{font:11px ui-monospace,monospace;color:#b1c1b7}#authorship{font-size:12px;color:#b1c1b7;margin:0;padding:0 20px 12px}#authorship.modified{color:#ffda98}#source-editor{display:block;width:100%;height:460px;min-height:240px;max-height:none;border:0;border-radius:0;padding:18px 20px;margin:0;color:inherit;background:transparent;font:14px/24px ui-monospace,monospace;tab-size:2}#source-editor.modified{background:#273c32;box-shadow:inset 4px 0 #e5b96b}#source-editor:disabled{opacity:.75}#source-notes{margin:0;padding:18px 20px;max-height:460px;overflow:auto;white-space:pre;tab-size:2}#source-availability,#compile-diagnostic,#compile-activity{margin:0;padding:10px 20px;color:#e2d5af}#compile-diagnostic{color:#ffc5b9}.source-actions{display:flex;gap:12px;align-items:center;flex-wrap:wrap;padding:12px 20px;border-top:1px solid #385045}.source-actions .hint{color:#b1c1b7;margin:0}.source-actions button{font-size:12px;padding:6px 10px;background:transparent;color:#e3edde;border-color:#385045} -#settings-panel{min-width:0;padding:18px;background:var(--paper);border-left:1px solid var(--line);font-size:13px}#settings-panel section{padding-block:20px;border-bottom:1px solid var(--line)}#settings-panel section:last-child{border:0}.inspector-heading{display:flex;align-items:center;justify-content:space-between;gap:8px}.inspector-heading h2{font-size:16px;margin:0;letter-spacing:0}#settings-panel button{font-size:12px}#settings-panel .hint{margin-bottom:0}#settings-panel details>p{margin-top:8px}#package-info,#release-info,#compiler-stats{overflow-wrap:anywhere}#download-bundle{margin-top:12px;width:100%}#bundle-download-status:empty{display:none}#terminal{max-height:260px;overflow:auto;padding:12px;border-left:3px solid var(--green);background:var(--dock)}#terminal:empty{display:none}#trace{max-height:260px;overflow:auto;list-style:none;padding:0;font:12px/1.6 ui-monospace,monospace}#trace li{padding-block:8px;border-bottom:1px solid var(--line);overflow-wrap:anywhere;white-space:pre-wrap}#trace li.pending{color:var(--green)}.trace-panel{border-top:1px solid var(--line);padding-top:12px} +#settings-panel{min-width:0;padding:18px;background:var(--paper);border-left:1px solid var(--line);font-size:13px}#settings-panel section{padding-block:20px;border-bottom:1px solid var(--line)}#settings-panel section:last-child{border:0}.inspector-heading{display:flex;align-items:center;justify-content:space-between;gap:8px}.inspector-heading h2{font-size:16px;margin:0;letter-spacing:0}#settings-panel button{font-size:12px}#settings-panel .hint{margin-bottom:0}#settings-panel details>p{margin-top:8px}#package-info,#release-info,#compiler-stats{overflow-wrap:anywhere}#download-bundle{margin-top:12px;width:100%}#bundle-download-status:empty{display:none}#trace{max-height:260px;overflow:auto;list-style:none;padding:0;font:12px/1.6 ui-monospace,monospace}#trace li{padding-block:8px;border-bottom:1px solid var(--line);overflow-wrap:anywhere;white-space:pre-wrap}#trace li.pending{color:var(--green)}.trace-panel{border-top:1px solid var(--line);padding-top:12px} .explanation{padding-block:64px}.explanation-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:36px;margin-top:28px}.explanation-grid h3{font-size:17px}.explanation-grid p{font-size:14px;line-height:1.8;color:var(--muted)}footer{padding-block:25px 35px;border-top:1px solid var(--line);font-size:12px;line-height:1.8;color:var(--muted)} @media(min-width:950px){.shapes{position:absolute;right:38px;top:70px;flex-direction:column;gap:15px}} @media(max-width:1119px) and (min-width:741px){.workspace{margin-inline:28px}} @@ -75,7 +75,7 @@ header nav{align-items:center;gap:12px}#theme-separator{color:var(--muted)} .definition-card.external-definition{position:relative;inset:auto;max-height:540px} #operation-diagnostic{padding:10px 20px} -.inline-io #terminal{background:#182b22;color:#e3edde;padding:8px;border-left:3px solid #98c879} + #package-definitions label{color:inherit}#operation-diagnostic{color:#ffc5b9} /* Cards share the source's dark surface even when the surrounding page is light. */ .definition-card{color-scheme:dark;--green:#8bd3ad} diff --git a/ext/demo-site/test/backend-browser.mjs b/ext/demo-site/test/backend-browser.mjs @@ -61,12 +61,12 @@ try { console.log('Test phases:',JSON.stringify(marks.slice(-4))); void evaluate("({status:document.getElementById('status')?.textContent,activity:document.getElementById('compile-activity')?.textContent,card:document.querySelector('.definition-card [role=status]')?.textContent})").then(state=>console.log('Page:',JSON.stringify(state))).catch(()=>{}); },30000); - const report=await evaluate(`import(${JSON.stringify(process.env.TEST_IMPORT_FOLDS?'/import-folds-probe.mjs':process.env.TEST_DEFINITION_NAMES?'/definition-names-probe.mjs':process.env.TEST_INLINE_IO?'/inline-io-probe.mjs':process.env.TEST_BASIC_EXAMPLES?'/basic-examples-probe.mjs':process.env.TEST_IMPORT_PICKER?'/import-picker-probe.mjs':process.env.TEST_COLD_LOAD?'/cold-load-probe.mjs':process.env.TEST_ASSET_CACHE?'/asset-cache-probe.mjs':process.env.TEST_LOCAL_MODULES?'/local-module-probe.mjs':process.env.TEST_MODULES?'/module-probe.mjs':process.env.TEST_IDE?'/ide-probe.mjs':process.env.TEST_LIVE_UX?'/live-ux-probe.mjs':process.env.TEST_BENCHMARK?'/backend-benchmark.mjs':'/backend-probe.mjs')}).then(m=>m.run(${JSON.stringify(control)},${JSON.stringify(process.env.TEST_BACKEND||'haskell-wasm')},${!!process.env.BENCHMARK_PSS}))`); + const report=await evaluate(`import(${JSON.stringify(process.env.TEST_IO_V2_PUBLIC?'/io-v2-public-probe.mjs':process.env.TEST_IO_V2_SOURCE?'/io-v2-source-probe.mjs':process.env.TEST_IO_V2_SESSION?'/io-v2-session-probe.mjs':process.env.TEST_IO_V2_CORE?'/io-v2-core-probe.mjs':process.env.TEST_IO_V2_LIFECYCLE?'/io-v2-lifecycle-probe.mjs':process.env.TEST_IO_V2_PROTOCOL?'/io-v2-protocol-probe.mjs':process.env.TEST_IMPORT_FOLDS?'/import-folds-probe.mjs':process.env.TEST_IO_VERSION?'/io-version-probe.mjs':process.env.TEST_DEFINITION_NAMES?'/definition-names-probe.mjs':process.env.TEST_INLINE_IO?'/inline-io-probe.mjs':process.env.TEST_BASIC_EXAMPLES?'/basic-examples-probe.mjs':process.env.TEST_IMPORT_PICKER?'/import-picker-probe.mjs':process.env.TEST_COLD_LOAD?'/cold-load-probe.mjs':process.env.TEST_ASSET_CACHE?'/asset-cache-probe.mjs':process.env.TEST_LOCAL_MODULES?'/local-module-probe.mjs':process.env.TEST_MODULES?'/module-probe.mjs':process.env.TEST_IDE?'/ide-probe.mjs':process.env.TEST_LIVE_UX?'/live-ux-probe.mjs':process.env.TEST_BENCHMARK?'/backend-benchmark.mjs':'/backend-probe.mjs')}).then(m=>m.run(${JSON.stringify(control)},${JSON.stringify(process.env.TEST_BACKEND||'haskell-wasm')},${!!(process.env.BENCHMARK_PSS||process.env.TEST_IO_V2_TERMINAL)}))`); if(process.env.TEST_IMPORT_FOLDS)await (await import('./import-fold-inputs.mjs')).importFoldInputs({send,evaluate,report,prefix:process.env.TEST_SCREENSHOT_PREFIX}); if(process.env.TEST_DEFINITION_NAMES)await (await import('./definition-name-inputs.mjs')).definitionNameInputs({send,evaluate,report,prefix:process.env.TEST_SCREENSHOT_PREFIX}); if(process.env.TEST_INLINE_IO&&process.env.TEST_SCREENSHOT_PREFIX){ await evaluate("document.getElementById('run-io').click()"); - for(let i=0;!await evaluate("document.getElementById('status').textContent==='Waiting for input'");i++){assert(i<3600);await new Promise(r=>setTimeout(r,25));} + for(let i=0;!await evaluate("!!document.querySelector('#terminal-v2 form input')");i++){assert(i<3600);await new Promise(r=>setTimeout(r,25));} for(const width of [390,1440]){ await send('Emulation.setDeviceMetricsOverride',{width,height:1000,deviceScaleFactor:1,mobile:width<741}); await evaluate("new Promise(r=>{document.activeElement?.blur();window.scrollTo({top:0,behavior:'instant'});requestAnimationFrame(()=>requestAnimationFrame(r));})"); @@ -75,7 +75,7 @@ try { const shot=await send('Page.captureScreenshot',{format:'png',captureBeyondViewport:true,clip:{x:0,y:0,width:r.width,height:r.height,scale:1}}); await writeFile(process.env.TEST_SCREENSHOT_PREFIX+'-io-'+width+'.png',Buffer.from(shot.data,'base64')); } - await evaluate("document.getElementById('line-input').value='Screenshot';document.getElementById('input-form').requestSubmit()"); + await evaluate("document.querySelector('#terminal-v2 form input').value='Screenshot';document.querySelector('#terminal-v2 form').requestSubmit()"); for(let i=0;!await evaluate("document.getElementById('status').textContent==='Finished'");i++){assert(i<3600);await new Promise(r=>setTimeout(r,25));} } if(process.env.TEST_RELOAD){ diff --git a/ext/demo-site/test/backend-probe.mjs b/ext/demo-site/test/backend-probe.mjs @@ -37,6 +37,6 @@ export async function run(control,backend='haskell-wasm'){ const source=globalThis.__sourceEditor.getSource();change('runtime-backend',backend==='haskell-wasm'?'zig-wasm':'haskell-wasm');await status('Compiled');assert(globalThis.__sourceEditor.getSource()===source,'Backend switching preserves source'); assert(await inspect('answer','number')==='42','Backend switch rebuilds independent definitions'); await(await import('/backend-resume-probe.mjs')).probeResume(backend); - const parity=backend==='zig-wasm'?await(await import('/zig-session-probe.mjs')).run():null; + const parity=backend==='zig-wasm'?await(await import('/zig-session-probe.mjs')).run(control):null; return {...report,parity,checks:[...report.checks,'real Haskell default; package/practical compilation; native statistics; decoder recovery; artifact/pin failures without fallback; CPU-bound Stop; backend isolation; stale/malformed resume rejection']}; } diff --git a/ext/demo-site/test/backend-resume-probe.mjs b/ext/demo-site/test/backend-resume-probe.mjs @@ -1,33 +1,55 @@ -// Exercise actual page-facing request IDs, independently of private runtime IO. +// Actual page-facing step IDs and run-bound v2 terminal replies. export async function probeResume(backend){ + const base=new URL('../',document.querySelector('script[type=module][src]').src); + const api=await import(new URL('dist/probe-api.js',base)),{Terminal}=api.ioV2Terminal; + const releases=api.packages.packageIndex(await(await fetch(new URL('packages.json',base))).json()); + const lib=name=>api.packages.packageReleases(releases,'tricu-lib-'+name)[0].manifest; const worker=new Worker(new URL('./wasm-worker.js?backend='+backend,document.querySelector('script[type=module][src]').src),{type:'module'}),events=[]; worker.onmessage=e=>events.push(e.data); - const assert=(ok,message)=>{if(!ok)throw Error(message);}; - const sleep=()=>new Promise(r=>setTimeout(r,40)); - const wait=async(predicate,start=0)=>{ - const deadline=performance.now()+10000; - while(performance.now()<deadline){const messages=events.slice(start);const failure=messages.find(x=>x.type==='error');if(failure)throw Error(failure.message);const found=messages.find(predicate);if(found)return found;await sleep();} + const assert=(ok,message)=>{if(!ok)throw Error(message);},sleep=()=>new Promise(r=>setTimeout(r,40)); + let consumedStep=0; + const wait=async(predicate,start=0,stepping=false)=>{ + const deadline=performance.now()+30000; + while(performance.now()<deadline){ + const messages=events.slice(start),failure=messages.find(x=>x.type==='error');if(failure)throw Error(failure.message); + const found=messages.find(predicate);if(found)return found; + if(stepping){const step=messages.find(x=>x.type==='state'&&x.state==='step'&&x.id>consumedStep);if(step){consumedStep=step.id;worker.postMessage({type:'resume',id:step.id});}} + await sleep(); + } throw Error('Resume probe timed out'); }; + const reply=value=>worker.postMessage({type:'v2-command',command:{run:value.run,kind:'reply',reply:value}}); try{ - worker.postMessage({type:'compile',source:'main = t "tricuIO" (t 1 (t 11 t))'}); + worker.postMessage({type:'compile',source:`!import ${lib('io')} as I +!import ${lib('io-v2-text')} as T +!export main +main = I.io (I.require I.stdin (reader : I.require (T.readLine (T.lineCursor reader) 256) (reply : case reply of + [["line" value _] _] = I.pure value + _ = I.fail "Invalid reply")))`}); await wait(x=>x.type==='compiled'); const run={type:'run-io',name:'main',output:'string',step:true}; worker.postMessage(run);const step=await wait(x=>x.type==='state'&&x.state==='step'); worker.postMessage({type:'resume',id:step.id,value:'wrong-kind'}); worker.postMessage({type:'resume',id:step.id+999});await sleep(); - assert(!events.some(x=>x.type==='error'||x.type==='done'||x.state==='input'),'Malformed/stale step consumed pending wait'); - worker.postMessage({type:'resume',id:step.id});const input=await wait(x=>x.type==='state'&&x.state==='input'); - for(const value of [undefined,'bad\nline','\ud800','a'.repeat(257)])worker.postMessage({type:'resume',id:input.id,value}); + assert(!events.some(x=>x.type==='error'||x.type==='done'||x.type==='v2-event'),'Malformed/stale step consumed wait'); + const input=(await wait(x=>x.type==='v2-event'&&x.event.kind==='read',0,true)).event; + const terminal=new Terminal(input.run);terminal.handle(input); + for(const value of [undefined,'bad\nline','\ud800','a'.repeat(257)])assert(!terminal.line(input.run,input.id,value),'Invalid UI line admitted'); + for(const invalid of [ + {run:input.run,id:input.id,kind:'write',count:1}, + {run:input.run,id:input.id,kind:'read',bytes:[256],eof:false}, + {run:input.run,id:input.id,kind:'read',bytes:[],eof:false}, + {run:input.run,id:input.id+999,kind:'read',bytes:[65],eof:false}, + ])reply(invalid); worker.postMessage({type:'resume',id:step.id,value:'stale'});await sleep(); - assert(!events.some(x=>x.type==='error'||x.type==='done'),'Invalid line consumed pending wait'); - worker.postMessage({type:'resume',id:input.id,value:'λ雪🙂'}); - assert((await wait(x=>x.type==='done')).result.value==='λ雪🙂','Resume value mismatch'); + assert(!events.some(x=>x.type==='done'||x.type==='error'),'Invalid reply consumed wait'); + const accepted=terminal.line(input.run,input.id,'λ雪🙂');reply(accepted); + assert((await wait(x=>x.type==='done',0,true)).result.value==='λ雪🙂','Fresh reply mismatch'); const mark=events.length;worker.postMessage({...run,step:false}); - const next=await wait(x=>x.type==='state'&&x.state==='input',mark);assert(next.id>input.id,'Request IDs reused'); - worker.postMessage({type:'resume',id:input.id,value:'stale'});await sleep(); - assert(!events.slice(mark).some(x=>x.type==='done'),'Previous run token accepted'); - worker.postMessage({type:'resume',id:next.id,value:'fresh'}); + const next=(await wait(x=>x.type==='v2-event'&&x.event.kind==='read',mark)).event; + assert(next.run!==input.run,'Run identity reused');reply(accepted);await sleep(); + assert(!events.slice(mark).some(x=>x.type==='done'||x.type==='error'),'Previous run reply accepted'); + const fresh=new Terminal(next.run);fresh.handle(next);reply(fresh.line(next.run,next.id,'fresh')); assert((await wait(x=>x.type==='done',mark)).result.value==='fresh','Fresh run failed'); }finally{worker.terminate();} } diff --git a/ext/demo-site/test/backend-server.mjs b/ext/demo-site/test/backend-server.mjs @@ -2,7 +2,7 @@ import {createServer} from 'node:http'; import {readFile} from 'node:fs/promises'; import {resolve,extname,sep} from 'node:path'; -import {randomBytes} from 'node:crypto'; +import {randomBytes,createHash} from 'node:crypto'; import {fixtures} from './zig-negative-fixtures.mjs'; export async function startSiteServer(directory,autoRun=false) { const root=resolve(process.env.ARBORICX_SITE_ROOT||'ext/demo-site'),runtime=resolve(directory),registry=resolve('ext/demo-site/bundles'); @@ -31,6 +31,7 @@ export async function startSiteServer(directory,autoRun=false) { else {data.ok?deliver(data.report):fail(Error(data.error));} res.end('ok');return; } + if(path==='/terminal-host.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./terminal-host.mjs',import.meta.url)));return;} if(path==='/source-probe-helpers.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./source-probe-helpers.mjs',import.meta.url)));return;} if(path==='/import-folds-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./import-folds-probe.mjs',import.meta.url)));return;} if(path==='/definition-names-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./definition-names-probe.mjs',import.meta.url)));return;} @@ -38,6 +39,15 @@ export async function startSiteServer(directory,autoRun=false) { if(path==='/asset-cache-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./asset-cache-probe.mjs',import.meta.url)));return;} if(path==='/zig-negative.json'){res.setHeader('Content-Type','application/json');res.end(JSON.stringify(fixtures()));return;} if(path==='/zig-fixtures.json'){res.setHeader('Content-Type','application/json');const data=JSON.parse(await readFile(new URL('../demos.json',import.meta.url)));for(const id of ['parser','lexer'])data.demos[id]=JSON.parse(await readFile(new URL('../'+id+'-demo.json',import.meta.url)));res.end(JSON.stringify(data));return;} + if(path==='/io-v2-runtime.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('../../wasm/runtime.mjs',import.meta.url)));return;} + if(path==='/io-v2-lifecycle-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./io-v2-lifecycle-probe.mjs',import.meta.url)));return;} + if(path==='/io-v2-source.json'&&process.env.IO_V2_SOURCE_BUNDLE){const bytes=await readFile(process.env.IO_V2_SOURCE_BUNDLE);res.setHeader('Content-Type','application/json');res.end(JSON.stringify({bytes:[...bytes],hash:createHash('sha256').update(bytes).digest('hex')}));return;} + if(path==='/io-v2-public-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./io-v2-public-probe.mjs',import.meta.url)));return;} + if(path==='/io-v2-source-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./io-v2-source-probe.mjs',import.meta.url)));return;} + if(path==='/io-v2-session-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./io-v2-session-probe.mjs',import.meta.url)));return;} + if(path==='/io-v2-core-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./io-v2-core-probe.mjs',import.meta.url)));return;} + if(path==='/io-v2-protocol-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./io-v2-protocol-probe.mjs',import.meta.url)));return;} + if(path==='/io-version-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./io-version-probe.mjs',import.meta.url)));return;} if(path==='/zig-session-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./zig-session-probe.mjs',import.meta.url)));return;} if(path==='/backend-resume-probe.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./backend-resume-probe.mjs',import.meta.url)));return;} if(path==='/backend-benchmark.mjs'){res.setHeader('Content-Type','text/javascript');res.end(await readFile(new URL('./backend-benchmark.mjs',import.meta.url)));return;} diff --git a/ext/demo-site/test/bundle.test.mjs b/ext/demo-site/test/bundle.test.mjs @@ -7,7 +7,8 @@ import {COMPILER_BUNDLE_LIMIT,decodeBundle,loadBundle,bundleHash,fetchBytes,fetc import {makeBundle,sizedBundle} from './bundle-fixture.mjs'; // Independently counted from the authenticated compiler manifest with the // Python bundle reader; older indexed compiler releases remain retained. -const COMPILER_EXPORT_COUNT=1194; +// Independently packed native fixture adds five existing source upload helpers. +const COMPILER_EXPORT_COUNT=1241; const data=JSON.parse(await readFile(new URL('../demos.json',import.meta.url))); test('stable packaged sources/pins and real compiler bundle decode',async()=>{ for(const [name,demo] of Object.entries(data.demos)){ diff --git a/ext/demo-site/test/compiler-conformance.json b/ext/demo-site/test/compiler-conformance.json @@ -1,6 +1,6 @@ { - "compilerBundleSha256": "91f78730b3623d019e33f96e9e2712d61b04837b21ef3eccec3071ac9260944d", - "hostSha256": "2bb21ddb1307e69bad141eb62f4e94b71efdf900614ba7333821e2eef2204547", + "compilerBundleSha256": "890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa", + "hostSha256": "bfcb3d738c845119e3b843c771c3df96297906b380a381f0338444421b94e77c", "backend": "stable", "export": "source_unit.compileStableModuleDefinitions", "cases": [ @@ -60,7 +60,8 @@ "definitions": { "io": "222020210202102102100220210202021021021002210202021020210210022102102020202102100221020210202102102100221020202102020210022102102102102020210002210022102102021000" }, - "result": "22102102021021021020210210210022021020210202102102102021021020210202021002202102020202021020202102102021021021021021000" + "compileOnly": true, + "executionRejected": "Unsupported tricuIO version" }, { "source": "main xs = (let rec\n last [h] = h\n last [_ | rest] = last rest\n last _ = 0\n in case xs of\n [] = 0\n _ = last xs)", diff --git a/ext/demo-site/test/compiler-conformance.mjs b/ext/demo-site/test/compiler-conformance.mjs @@ -7,7 +7,7 @@ import {contractEnvironment} from '../dist/contracts.js'; import {Graph} from '../dist/graph.js'; import {COMPILER_BUNDLE_LIMIT,decodeBundle,loadBundle} from '../dist/bundle.js'; import {COMPILER_EXPORT,COMPILER_COLLECTION,sourceLiteral,compiledDefinitions} from '../dist/compiler.js'; -import {runIO} from '../dist/io.js'; +// Pure reference execution only; IO goldens run through native browser probes. const vectors=JSON.parse(await readFile(new URL('./compiler-conformance.json',import.meta.url))); const descriptor=JSON.parse(await readFile(new URL('../compiler.json',import.meta.url))); const bytes=await readFile(new URL('../bundles/'+descriptor.hash+'.arboricx',import.meta.url)); @@ -23,9 +23,11 @@ for(const c of vectors.cases){ g.collect(definitions.values()); assert.deepEqual([...definitions.keys()],Object.keys(c.definitions)); for(const [name,tree] of Object.entries(c.definitions))assert.equal(g.toTernary(definitions.get(name),50000),tree); - let applied=g.apply(definitions.get(c.export),c.arguments.map(arg=>g.fromTernary(arg))); - if(c.mode==='io')applied=await runIO(g,applied,{before:async()=>{},after(){},running(){},read:async()=>c.input,write(){throw Error('unexpected output');}},true); - assert.equal(g.toTernary(applied,50000),c.result); + if(c.mode==='io')console.log('COMPILE ONLY historical IO tree; no reference IO execution'); + else { + const applied=g.apply(definitions.get(c.export),c.arguments.map(arg=>g.fromTernary(arg))); + assert.equal(g.toTernary(applied,50000),c.result); + } } assert.ok(g.stats.collections>0);assert.ok(g.stats.peakNodes<(c.extended?2_000_000:600_000)); if(c.source.includes('other ='))assert.ok(g.stats.allocations>6_000_000); diff --git a/ext/demo-site/test/compiler.test.mjs b/ext/demo-site/test/compiler.test.mjs @@ -11,6 +11,11 @@ test('compiler shipped pin matches authenticated reference and Haskell golden pr const shipped=await readFile(new URL('../bundles/'+descriptor.hash+'.arboricx',import.meta.url)); assert.equal(createHash('sha256').update(shipped).digest('hex'),descriptor.hash); assert.equal(goldens.compilerBundleSha256,descriptor.hash); + const historical=goldens.cases.filter(c=>c.mode==='io'); + assert.equal(historical.length,1); + assert.equal(historical[0].compileOnly,true); + assert.equal(historical[0].executionRejected,'Unsupported tricuIO version'); + assert.equal(historical[0].result,undefined); assert.match(descriptor.referenceCommit,/^[0-9a-f]{40}$/); // The site's accepted compiler is intentionally pinned independently of HEAD. // Authenticate it against its retained publication metadata below, not the @@ -20,7 +25,7 @@ test('compiler shipped pin matches authenticated reference and Haskell golden pr const fixtureDigest=createHash('sha256').update('tricu.compiler-library-fixture.v1\0').update(shipped).digest('hex'); assert.deepEqual(metadata.filter(row=>row.startsWith('bundle\t')),['bundle\t'+fixtureDigest]); const sources=metadata.filter(row=>row.startsWith('module\t')); - assert.equal(sources.length,28); + assert.equal(sources.length,31); assert.ok(sources.every(row=>row.split('\t')[2]!=='patterns')); for(const row of sources){ const pin=row.split('\t')[4];assert.match(pin,/^[0-9a-f]{64}$/); diff --git a/ext/demo-site/test/conformance.json b/ext/demo-site/test/conformance.json @@ -1,5 +1,5 @@ { - "hostSha256": "2bb21ddb1307e69bad141eb62f4e94b71efdf900614ba7333821e2eef2204547", + "hostSha256": "bfcb3d738c845119e3b843c771c3df96297906b380a381f0338444421b94e77c", "description": "All normal trees <=5 nodes, every ordered application pair; lazy regression and partial/multiple applications.", "cases": [ { diff --git a/ext/demo-site/test/contracts-conformance.json b/ext/demo-site/test/contracts-conformance.json @@ -1,6 +1,6 @@ { - "compilerBundleSha256": "91f78730b3623d019e33f96e9e2712d61b04837b21ef3eccec3071ac9260944d", - "hostSha256": "2bb21ddb1307e69bad141eb62f4e94b71efdf900614ba7333821e2eef2204547", + "compilerBundleSha256": "890254a27b2bb16c62eedc7b80daab58a33bc40b0bb2a7ff20400d9c3c0de7fa", + "hostSha256": "bfcb3d738c845119e3b843c771c3df96297906b380a381f0338444421b94e77c", "contractsSourceSha256": "8f69b32c50461edd5db536becb6faedd5ba33945bf774534592034350702600a", "bindings": { "matchResult": "base.matchResult", diff --git a/ext/demo-site/test/demo-cost.mjs b/ext/demo-site/test/demo-cost.mjs @@ -9,31 +9,29 @@ import {Graph} from '../dist/graph.js'; import {decodeBundle,loadBundle} from '../dist/bundle.js'; import {PACKAGE_BUDGET} from './budgets.mjs'; import {PACKAGE_COLLECTION} from '../dist/execution.js'; -import {runIO} from '../dist/io.js'; +// Pure reference execution only; IO goldens run through native browser probes. const current=fileURLToPath(new URL('../',import.meta.url)); if(!process.argv[2])throw Error('Select the retained reference site directory'); async function measure(directory){ - const data=JSON.parse(await readFile(join(directory,'demos.json'))),result={}; + const data=JSON.parse(await readFile(join(directory,'demos.json'))),result={},nativeOnly=[]; for(const [name,demo] of Object.entries(data.demos)){ + if(demo.mode==='io'){nativeOnly.push(name);continue;} const bytes=await readFile(join(directory,'bundles',demo.hash+'.arboricx')); assert.equal(createHash('sha256').update(bytes).digest('hex'),demo.hash); const bundle=decodeBundle(bytes);const cases=[]; for(const c of demo.haskellCases){ const g=new Graph(PACKAGE_BUDGET,PACKAGE_COLLECTION),exports=loadBundle(g,bundle); let output=g.apply(exports.get(c.export),c.arguments.map(tree=>g.fromTernary(tree))),terminal=''; - if(demo.mode==='io')output=await runIO(g,output,{ - before:async()=>{},after(){},running(){},read:async()=>c.input,write(text){terminal+=text;} - },true); assert.equal(g.toTernary(output),c.result);assert.equal(terminal,c.terminal); cases.push({key:JSON.stringify([c.export,c.arguments,c.input,c.result,c.terminal]),...g.stats}); } result[name]={hash:demo.hash,bytes:bytes.length,cases}; } - return {compiler:data.compilerBundleSha256,demos:result}; + return {compiler:data.compilerBundleSha256,demos:result,nativeOnly}; } const before=await measure(resolve(process.argv[2])),after=await measure(current),summary={}; for(const [name,old] of Object.entries(before.demos)){ - const next=after.demos[name];let workBefore=0,workAfter=0,allocationsBefore=0,allocationsAfter=0; + const next=after.demos[name];assert.ok(next,`missing pure reference demo: ${name}`);let workBefore=0,workAfter=0,allocationsBefore=0,allocationsAfter=0; for(const c of old.cases){ const n=next.cases.find(n=>n.key===c.key);assert.ok(n,`missing independent reference case: ${name}`); workBefore+=c.work;workAfter+=n.work;allocationsBefore+=c.allocations;allocationsAfter+=n.allocations; diff --git a/ext/demo-site/test/editable-conformance.mjs b/ext/demo-site/test/editable-conformance.mjs @@ -9,7 +9,7 @@ import {compiledDefinitions,sourceLiteral,COMPILER_EXPORT} from '../dist/compile import {stageModule,selectModuleExports} from '../dist/module-stage.js'; import {moduleIdentity} from '../dist/module-identity.js'; import {list,text,readText} from '../dist/codecs.js'; -import {runIO} from '../dist/io.js'; +// Pure reference execution only; IO goldens run through native browser probes. const root=new URL('../',import.meta.url); const catalogue=JSON.parse(await readFile(new URL('catalogue.json',root))); const environments=JSON.parse(await readFile(new URL('environments.json',root))); @@ -23,6 +23,7 @@ for(const id of ['parser','lexer'])goldens.demos[id]=JSON.parse(await readFile(n const releases=JSON.parse(await readFile(new URL('packages.json',root))).releases; const compiler=await bundle(environments.compiler,COMPILER_BUNDLE_LIMIT); for(const entry of catalogue.filter(e=>!process.argv[2]||e.id===process.argv[2])){ + if(entry.mode==='io'){console.log('NATIVE ONLY',entry.id,'IO execution is checked by module/inline/full-golden browser probes');continue;} const started=performance.now(); const g=new Graph({nodes:6_000_000,fuel:Infinity,stack:100_000},{threshold:250_000}); const library=loadBundle(g,compiler),stage=stageModule(g,library,entry.source+'\n-- Edited locally\n'); @@ -58,7 +59,6 @@ for(const entry of catalogue.filter(e=>!process.argv[2]||e.id===process.argv[2]) g.beginOperation(); let result=g.apply(definitions.get(entry.export),expected.arguments.map(a=>g.fromTernary(a))); let terminal=''; - if(entry.mode==='io')result=await runIO(g,result,{before:async()=>{},after(){},running(){},write(s){terminal+=s;},read:async()=>expected.input},true,definitions.values()); const actual=g.toTernary(result); // Compare small digests rather than permitting gigantic diagnostic diffs. const sha=s=>createHash('sha256').update(s).digest('hex'); diff --git a/ext/demo-site/test/gc.test.mjs b/ext/demo-site/test/gc.test.mjs @@ -3,7 +3,6 @@ import assert from 'node:assert/strict'; import {readFile} from 'node:fs/promises'; import {Graph} from '../dist/graph.js'; import {text,natural,readText} from '../dist/codecs.js'; -import {runIO} from '../dist/io.js'; test('tracing preserves sharing and stable handles; stale IDs are never reused',()=>{ const g=new Graph({fuel:10000}),garbage=g.stem(g.leaf);let root=g.leaf; for(let i=0;i<31;i++)root=g.fork(root,root); @@ -45,21 +44,6 @@ test('collection is budgeted and commits atomically; no high-survival trace stor for(let i=0;i<20;i++)root=high.stem(root); assert.equal(high.force(root),root);assert.ok(high.stats.collections<=2); }); -test('owned IO collection retains saved continuations and input across pauses',async()=>{ - const g=new Graph({fuel:100000},{threshold:32}); - const action=(tag,payload=g.leaf)=>g.fork(natural(g,BigInt(tag)),payload); - const pure=g.fork(g.leaf,natural(g,0n)); // K Leaf; the continuation ignores line data. - const bind=action(1,g.fork(action(11),g.fork(g.leaf,action(0,text(g,'done'))))); - const io=g.fork(text(g,'tricuIO'),g.fork(natural(g,1n),bind)); - let resolve;const read=new Promise(r=>{resolve=r;});let waiting=false; - const resultPromise=runIO(g,io,{before:async()=>{},after(){},write(){},running(){},read(){waiting=true;return read;}},true); - while(!waiting)await Promise.resolve(); - assert.ok(g.stats.collections>=2);resolve('λ雪🙂'); - const result=await resultPromise;assert.equal(readText(g,result),'done'); - // A previously unrelated caller handle is invalidated only by the explicit - // owned-IO contract, never by automatic reducer collections. - assert.throws(()=>g.observe(pure),/Invalid node handle/); -}); test('explicit Infinity profile removes graph/fuel/stack ceilings without changing defaults',()=>{ const bounded=new Graph({nodes:2,fuel:100,stack:1});bounded.stem(bounded.leaf); assert.throws(()=>bounded.stem(bounded.leaf),/allocation/); @@ -70,16 +54,3 @@ test('explicit Infinity profile removes graph/fuel/stack ceilings without changi assert.ok(g.stats.collections>0);assert.ok(g.stats.work>100); for(const fuel of [NaN,-Infinity,0,-1,1.5])assert.throws(()=>new Graph({fuel}),/budget/); }); -test('owned IO extra roots preserve compiled definitions for repeated input runs',async()=>{ - const g=new Graph({fuel:100000},{threshold:32}); - const definition=g.fork(text(g,'tricuIO'),g.fork(natural(g,1n),g.fork(natural(g,11n),g.leaf))); - const identity=g.fromTernary('21100');const roots=[definition,identity]; - let reads=0; - for(const input of ['first','λ雪🙂','third']){ - const result=await runIO(g,definition,{before:async()=>{},after(){},write(){},running(){},read:async()=>{reads++;return input;}},true,roots.values()); - assert.equal(readText(g,result),input);g.collect(roots); - assert.equal(g.toTernary(identity),'21100'); - } - assert.equal(reads,3);assert.ok(g.stats.collections>=6); - assert.ok(g.stats.fuel<100000); // Work stays cumulative across replays. -}); diff --git a/ext/demo-site/test/graph.test.mjs b/ext/demo-site/test/graph.test.mjs @@ -1,10 +1,15 @@ +import {terminalHost} from './terminal-host.mjs'; +import {Terminal} from '../dist/io-v2-terminal.js'; +import {constructors} from '../dist/io-v2-private.js'; +import {Session} from '../dist/io-v2-session.js'; +import {envelope} from '../dist/io-v2-profile.js'; +import {drive} from '../dist/io-v2-driver.js'; import test from 'node:test'; import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import {BROWSER_BUDGET,collectionPolicy,PACKAGE_COLLECTION} from '../dist/execution.js'; import { Graph } from '../dist/graph.js'; import { natural, readNatural, text, readText, line, literals, makeLiteral, view } from '../dist/codecs.js'; -import { runIO } from '../dist/io.js'; import { decodeBundle, loadBundle } from '../dist/bundle.js'; const data = JSON.parse(await readFile(new URL('../demos.json', import.meta.url))); for(const id of ['parser','lexer'])data.demos[id]=JSON.parse(await readFile(new URL('../'+id+'-demo.json',import.meta.url))); @@ -77,9 +82,20 @@ for (const [name, demo] of Object.entries(data.demos)) { const g = new Graph(budget,collectionPolicy(budget,PACKAGE_COLLECTION.threshold)), h = host(golden.input); const exports=loadBundle(g,bundle); const applied=g.apply(exports.get(golden.export),golden.arguments.map(x=>g.fromTernary(x))); - const result=demo.mode==='io'?await runIO(g,applied,h):applied; - assert.equal(g.toTernary(result), golden.result); - assert.equal(h.state.terminal, golden.terminal); + if(demo.mode==='io') { + const trees=constructors({leaf:g.leaf,observe:g.observe.bind(g),stem:g.stem.bind(g),fork:g.fork.bind(g),app:g.app.bind(g)}); + const selected=envelope(trees,applied);assert.equal(selected.version,2); + // Reference tests exercise only the v2 model; no legacy interpreter. + assert.equal(envelope(g,applied).version,2); + assert.equal(h.state.terminal,'');assert.equal(h.state.trace.length,0); + const session=new Session(trees,selected.action,'1',roots=>g.collect(roots),{summarize:false,backend:'test-reference'}); + const terminal=terminalHost(Terminal,golden.input); + const result=await drive({run:'1',turn:c=>session.turn(c),view:async()=>view(g,session.core.result.value,'ternary')},terminal); + assert.equal(result.value,golden.result);assert.equal(terminal.terminalText,golden.terminal); + } else { + assert.equal(g.toTernary(applied), golden.result); + assert.equal(h.state.terminal, golden.terminal); + } } }); } @@ -115,34 +131,6 @@ test('deep transport and graph observation do not use recursive JS evaluation', const root=g.fromTernary(term,term.length); assert.equal(g.toTernary(root,term.length),term); }); -test('IO rejects unknown tags/envelopes; pausing happens before effects', async () => { - const g=new Graph(), h=host(); - const action=(tag,payload=g.leaf)=>g.fork(natural(g,BigInt(tag)),payload); - const io=action=>g.fork(text(g,'tricuIO'),g.fork(natural(g,1n),action)); - await assert.rejects(runIO(g,io(action(20)),h),/Unsupported/); - await assert.rejects(runIO(g,g.leaf,h),/envelope/); - let resume, wrote=false; - h.before=()=>new Promise(resolve=>{resume=resolve;});h.write=()=>{wrote=true;}; - const pending=runIO(g,io(action(10,text(g,'hi'))),h); - await Promise.resolve();assert.equal(wrote,false);resume();await pending;assert.equal(wrote,true); -}); -test('IO action/output budgets and abort do not force arbitrary payloads',async()=>{ - for(const output of [false,true]){ - const g=new Graph(),h=host(); - const action=(tag,payload=g.leaf)=>g.fork(natural(g,BigInt(tag)),payload); - const primitive=output?action(10,text(g,'x'.repeat(4096))):action(0); - let current=action(0); - for(let i=0;i<(output?6:70);i++)current=action(1,g.fork(primitive,g.fork(g.leaf,current))); - const io=g.fork(text(g,'tricuIO'),g.fork(natural(g,1n),current)); - await assert.rejects(runIO(g,io,h),output?/output limit/:/action limit/); - assert.ok(h.state.terminal.length<=16384); - } - const g=new Graph({fuel:1000}),q=g.fromTernary('212110021100'),loop=g.app(q,q); - const abort=g.fork(natural(g,2n),loop),io=g.fork(text(g,'tricuIO'),g.fork(natural(g,1n),abort)); - await assert.rejects(runIO(g,io,host()),/Execution aborted/); - assert.ok(g.stats.work<500); -}); - test('draft checkpoints preserve sharing and lazy poison across failed disposable runs',()=>{ const g=new Graph({fuel:10000}); const q=g.fromTernary('212110021100'), loop=g.app(q,q); @@ -159,3 +147,10 @@ test('draft checkpoints preserve sharing and lazy poison across failed disposabl assert.throws(()=>Graph.restore(checkpoint,{nodes:1}),/allocation/); assert.throws(()=>next.force(loop),/limit/); }); + +test('immutable v1 conversation requires its retained runtime, not current execution',async()=>{ + const pin='0811457d501113ee11194f90e279ebb4ef9d548c70cb48d146be03eb9af7505f'; + const g=new Graph(BROWSER_BUDGET); + const exports=loadBundle(g,decodeBundle(await readFile(new URL(`../bundles/${pin}.arboricx`,import.meta.url)))); + assert.throws(()=>envelope(g,exports.get('main')),/Unsupported tricuIO version/); +}); diff --git a/ext/demo-site/test/inline-io-probe.mjs b/ext/demo-site/test/inline-io-probe.mjs @@ -1,82 +1,92 @@ import './source-probe-helpers.mjs'; +import {terminalHost} from './terminal-host.mjs'; const assert=(condition,message)=>{if(!condition)throw Error(message);}; const el=id=>document.getElementById(id),pause=ms=>new Promise(r=>setTimeout(r,ms)); const input=(id,value)=>{if(id==='source-editor'){globalThis.__sourceEditor.replaceSource(value,true);return;}el(id).value=value;el(id).dispatchEvent(new Event('input'));}; const change=(id,value)=>{el(id).value=value;el(id).dispatchEvent(new Event('change'));}; async function wait(check,label){for(let i=0;i<3600;i++){if(check())return;await pause(25);}throw Error('Timeout '+label+' / '+el('status').textContent+' / '+el('operation-diagnostic').textContent);} const status=text=>wait(()=>el('status').textContent===text,text); -const fixture=`!export answer -answer = 42 -io = t "tricuIO" (t 1 (t 11 t)) -wrong = t "other" (t 1 (t 11 t)) -unsupported = t "tricuIO" (t 2 (t 11 t)) -loop = (x : x x) (x : x x) -latent = t "tricuIO" (t 1 loop) -write = t "tricuIO" (t 1 (t 10 "Explicit output")) -abort = t "tricuIO" (t 1 (t 2 loop))`; export async function run(control,backend='haskell-wasm'){ await status('Ready'); if(el('runtime-backend').value!==backend){change('runtime-backend',backend);await status('Ready');} assert(!el('expression-editor')&&!el('run')&&!document.querySelector('.expression-dock'),'No separate evaluation component'); const base=new URL('../',document.querySelector('script[type=module][src]').src); - const {createRuntimeSession}=(await import(new URL('dist/probe-api.js',base))).runtimeSession; + const api=await import(new URL('dist/probe-api.js',base)); + const {createRuntimeSession}=api.runtimeSession,{Terminal}=api.ioV2Terminal; + const releases=api.packages.packageIndex(await(await fetch(new URL('packages.json',base))).json()); + const lib=name=>api.packages.packageReleases(releases,'tricu-lib-'+name)[0].manifest; + const ioDefinition='io = IO.io (IO.require IO.stdin readAction)'; + const fixture=`!import ${lib('io')} as IO +!import ${lib('io-v2-text')} as Text +!export answer +answer = 42 +readAction reader = IO.require (Text.readLine (Text.lineCursor reader) 256) (reply : case reply of + [["line" value _] _] = IO.pure value + [["eof"] _] = IO.pure "" + _ = IO.fail "Invalid line reply") +${ioDefinition} +wrong = t "other" (t 2 (t 0 t)) +unsupported = t "tricuIO" (t 3 (t 0 t)) +retired = t "tricuIO" (t 1 (t 11 t)) +loop = (x : x x) (x : x x) +latent = t "tricuIO" (t 2 loop) +write = IO.io (IO.require IO.stdout (writer : IO.require (Text.writeText writer "Explicit output") (_ : IO.pure t))) +abort = IO.io (IO.fail loop)`; const session=await createRuntimeSession(backend,true); try{ assert((await session.compile(Array.from({length:64},(_,i)=>`local${i} = ${i}`).join('\n'),'empty')).length===64,'Source exploration is not capped at 16 definitions'); await session.compile(fixture,'empty'); for(const name of ['io','write','latent','abort'])assert(await session.probeIO(name),'Supported IO envelope '+name); - for(const name of ['answer','wrong','unsupported']){ - assert(!await session.probeIO(name),'Non-IO or unsupported envelope '+name); - } + for(const name of ['answer','wrong','unsupported','retired'])assert(!await session.probeIO(name),'Non-IO or retired framing '+name); assert((await session.inspectDefinition('answer','number')).value==='42','Probes preserve compiled scope'); - let output=''; - const host={before:async()=>{},after:()=>{},write:text=>{output+=text;},read:async()=>'λ雪🙂',running:()=>{}}; - assert((await session.runDefinitionIO('io','string',host)).value==='λ雪🙂','Private IO definition runs directly without expression compilation'); - await session.runDefinitionIO('write','tricu',host);assert(output==='Explicit output','Explicit IO executes once'); - let aborted=false;try{await session.runDefinitionIO('abort','tricu',host);}catch(error){aborted=/aborted/i.test(String(error));}assert(aborted,'Abort does not force a divergent diagnostic'); - assert((await session.inspectDefinition('answer','number')).value==='42','IO error preserves definitions'); + assert((await session.runDefinitionIO('io','string',terminalHost(Terminal,'λ雪🙂'))).value==='λ雪🙂','Private IO definition runs directly'); + const output=terminalHost(Terminal,null); + await session.runDefinitionIO('write','tricu',output);assert(output.terminalText==='Explicit output','Explicit IO executes once'); + let aborted=false;try{await session.runDefinitionIO('abort','tricu',terminalHost(Terminal,null));}catch(error){aborted=/IO v2 failed/.test(String(error));}assert(aborted,'Failure does not force divergent diagnostic'); + assert((await session.inspectDefinition('answer','number')).value==='42','Failure preserves definitions'); }finally{session.dispose();} document.querySelector('[data-compiler-example=identity]').click();input('source-editor',fixture);await status('Compiled'); const button=name=>document.querySelector(`[aria-label="Inspect compiled definition ${name}"]`); const card=()=>document.querySelector('.definition-card'),view=()=>card().querySelector('pre'); + const form=()=>el('terminal-v2').querySelector('form:has(input)'); + const output=()=>el('terminal-v2').querySelector('[data-stream=output]').textContent; const open=async name=>{button(name).click();await pause(0);await wait(()=>!card().hidden&&!card().querySelector('select').disabled,'inspect '+name);}; + const advance=async check=>{for(let i=0;i<3600;i++){if(check())return;if(el('status').textContent==='Paused before action')el('next').click();await pause(25);}throw Error('Stepped IO did not progress');}; await open('answer');assert(view().textContent==='42','Pure Auto inspection'); assert(el('run-io').closest('section').hidden,'Pure binding has no IO run surface'); assert(!card().querySelector('.definition-actions button').disabled,'Pure result can be copied/downloaded');button('answer').click(); - await open('latent');assert(card().textContent.includes('IO program · tricuIO v1'),'Detect sentinel without forcing divergent action'); - assert(el('terminal').textContent===''&&el('input-form').hidden,'Opening IO never executes'); - el('run-io').click();await wait(()=>el('status').textContent.includes('Running IO'),'divergent IO started');await pause(100);el('stop-io').click();assert(el('status').textContent==='Stopped','CPU-bound IO is cancellable'); - el('compile').click();await status('Compiled');assert(!el('status').textContent.includes('Running'),'Cancelled observation does not automatically restart after recompile'); - await open('write');await pause(100);assert(el('terminal').textContent==='','Detection does not emit output');el('run-io').click();await status('Finished'); - assert(el('terminal').textContent==='Explicit output','Inline output appears after explicit Run'); - el('run-io').click();await status('Finished');assert(el('terminal').textContent==='Explicit output','Rerun clears old terminal');button('write').click(); + await open('latent');assert(card().textContent.includes('IO program · authenticated tricuIO envelope'),'Detection never forces divergent action'); + assert(el('terminal-v2').textContent===''&&!el('input-form'),'Opening IO never executes');button('latent').click(); + // Structured Stop is not a hard interruption of divergent native reduction. + // Exercise acknowledged cancellation below at a real pending terminal read. + await open('write');await pause(100);assert(el('terminal-v2').textContent==='','Detection emits nothing');el('run-io').click();await status('Finished'); + assert(output()==='Explicit output','Explicit inline output');el('run-io').click();await status('Finished');assert(output()==='Explicit output','Rerun clears terminal');button('write').click(); await open('io');el('io-return-format').value='string';el('step-mode').checked=true;el('run-io').click();await status('Paused before action'); - el('next').click();await status('Waiting for input'); - assert(!button('io').hidden&&button('io').getAttribute('aria-disabled')==='true'&&button('io').getAttribute('aria-expanded')==='true'&&getComputedStyle(button('io')).pointerEvents==='none','Active IO name stays highlighted but cannot start another observation'); - input('line-input','a'.repeat(257));el('input-form').requestSubmit();assert(!el('input-form').hidden,'Invalid input remains retryable'); - input('line-input','λ雪🙂');el('input-form').requestSubmit();await status('Finished');assert(view().textContent==='λ雪🙂','Returned IO value uses selected interpretation'); - el('step-mode').checked=false;el('run-io').click();await status('Waiting for input');el('stop-io').click(); - assert(el('input-form').hidden,'Stop removes pending input');assert(!el('compile').disabled,'Stop permits recompilation'); - el('compile').click();await status('Compiled'); - await open('io');el('run-io').click();await status('Waiting for input');card().querySelector('[aria-label="Close definition inspection"]').click(); - assert(card().hidden&&el('status').textContent==='Stopped','Closing active IO cancels its runtime');el('compile').click();await status('Compiled'); - // Opening a different binding never inherits IO authority from the last one. - await open('answer');assert(el('run-io').closest('section').hidden,'No stale IO controls on pure binding');button('answer').click(); - await open('io');input('source-editor',fixture.replace('io = t "tricuIO" (t 1 (t 11 t))','io = 7'));await status('Compiled'); - await wait(()=>view().textContent==='7','revision replaces open IO card');assert(el('run-io').closest('section').hidden,'Source edits invalidate IO recognition'); - // Source-free packages retain direct named inspection without an evaluator. + assert(!form(),'Step stops before input admission');await advance(()=>!!form()); + assert(button('io').getAttribute('aria-disabled')==='true'&&button('io').getAttribute('aria-expanded')==='true','Active IO inspection stays highlighted'); + form().querySelector('input').value='a'.repeat(257);form().requestSubmit();assert(form()&&!form().hidden,'Invalid input remains retryable'); + form().querySelector('input').value='λ雪🙂';form().requestSubmit();await advance(()=>el('status').textContent==='Finished');assert(view().textContent==='λ雪🙂','Unicode return uses selected interpretation'); + el('step-mode').checked=false;el('run-io').click();await wait(()=>form(),'pending read');el('stop-io').click();await status('Stopped'); + assert(!form()&&!el('run-io').disabled,'Acknowledged Stop retires input and permits rerun'); + el('run-io').click();await wait(()=>form(),'reuse after Stop');card().querySelector('[aria-label="Close definition inspection"]').click();await status('Stopped');assert(card().hidden,'Closing active IO cancels and settles'); + await open('answer');assert(el('run-io').closest('section').hidden,'No stale IO controls');button('answer').click(); + await open('io');input('source-editor',fixture.replace(ioDefinition,'io = 7'));await status('Compiled'); + await wait(()=>view().textContent==='7','revision replaces IO card');assert(el('run-io').closest('section').hidden,'Edits invalidate IO recognition'); const catalogue=await(await fetch(new URL('catalogue.json',base))).json(),names=catalogue.find(item=>item.id==='names-values'); change('program-picker','hash');input('bundle-hash',names.hash);el('program-hash').requestSubmit();await status('Ready'); assert(el('source-editor').hidden&&!el('package-definitions').hidden,'Source-free definition browser'); - [...el('package-definition-list').querySelectorAll('button')].find(button=>button.textContent==='answer').click(); - await wait(()=>view().textContent==='42'&&!card().querySelector('select').disabled,'source-free named inspection'); - assert(card().parentElement.id==='package-definition-view','Card remains visible outside hidden source wrapper'); + [...el('package-definition-list').querySelectorAll('button')].find(b=>b.textContent==='answer').click();await wait(()=>view().textContent==='42'&&!card().querySelector('select').disabled,'source-free inspection'); + assert(card().parentElement.id==='package-definition-view','Source-free card remains visible'); const conversation=catalogue.find(item=>item.id==='greet');input('bundle-hash',conversation.hash);el('program-hash').requestSubmit();await status('Ready'); - [...el('package-definition-list').querySelectorAll('button')].find(button=>button.textContent==='main').click();await wait(()=>!el('run-io').disabled,'source-free IO detection'); - el('io-return-format').value='tricu';el('run-io').click();await status('Waiting for input');input('line-input','Source-free');el('input-form').requestSubmit();await status('Finished'); - assert(el('terminal').textContent.includes('Source-free'),'Source-free IO runs in its named card too'); - // No automatic execution, even on a published IO demo. - change('program-picker','demo:greet');await status('Ready');await open('main');assert(el('terminal').textContent===''&&el('input-form').hidden,'Published IO is also opt-in'); - el('io-return-format').value='tricu';el('run-io').click();await status('Waiting for input');input('line-input','Inline tester');el('input-form').requestSubmit();await status('Finished');assert(el('terminal').textContent.includes('Inline tester'),'Published IO conversation works'); - return {userAgent:navigator.userAgent,backend,checks:['sentinel/version detection without action forcing; explicit inline IO; private definitions; read/write/abort; stepping; Unicode input and retry; rerun; Stop/recompile; revision invalidation; ordinary value inspection; no separate evaluator']}; + [...el('package-definition-list').querySelectorAll('button')].find(b=>b.textContent==='main').click();await wait(()=>!el('run-io').disabled,'source-free IO detection'); + const sendLine=async value=>{await wait(()=>form(),'line input');form().querySelector('input').value=value;form().requestSubmit();await status('Finished');}; + el('io-return-format').value='string';el('run-io').click();await sendLine('Source-free');assert(output()==='What is your name?\nHello, Source-free!\n','Source-free IO executes'); + change('program-picker','demo:greet');await status('Ready');await open('main');assert(el('terminal-v2').textContent===''&&!el('input-form'),'Published IO is opt-in'); + el('io-return-format').value='string';el('run-io').click();await sendLine('Inline tester');assert(output().includes('Inline tester'),'Published conversation'); + el('run-io').click();await sendLine('');assert(output()==='What is your name?\nHello, !\n','Empty line is data'); + el('run-io').click();await wait(()=>form(),'EOF prompt');form().querySelector('button[type=button]').click();await status('Finished');assert(output()==='What is your name?\nEnd of input.\n'&&view().textContent==='','Explicit EOF is distinct'); + el('run-io').click();await wait(()=>form(),'cancel prompt');const stale=form();el('stop-io').click();await status('Stopped'); + assert(!form(),'Stop retires input');el('run-io').click();await wait(()=>form(),'rerun without compilation');stale.requestSubmit();await pause(50);assert(form(),'Retired form cannot consume new input');await sendLine('Still compiled'); + assert(output().includes('Still compiled'),'Stop preserves definitions'); + return {userAgent:navigator.userAgent,backend,checks:['v2-only framing; opaque latent action/failure; explicit private and public IO; scheduler stepping; Unicode and retry; EOF; rerun; acknowledged Stop and stale-form isolation; source revisions; source-free and pure inspection']}; } diff --git a/ext/demo-site/test/io-v2-core-probe.mjs b/ext/demo-site/test/io-v2-core-probe.mjs @@ -0,0 +1,47 @@ +// Executes bare computation trees privately on native evaluators. Production IO +// envelopes and terminal providers remain gated; no JS evaluator substitution. +export async function run(_control,backend){ + const base=new URL('../',document.querySelector('script[type=module][src]').src); + const fixtures=await(await fetch('/zig-negative.json')).json(),checks=[]; + let execute,dispose; + if(backend==='haskell-wasm'){ + const {CompilerWorker}=await import(new URL('runtime/haskell/client.mjs',base)); + const client=new CompilerWorker();dispose=()=>client.dispose();await client.request({type:'init',compiler:false}); + execute=async fixture=>{await client.request({type:'load',bytes:new Uint8Array(fixture.bytes).buffer,hash:fixture.hash});return JSON.parse((await client.request({type:'io-v2-core',name:'root',arguments:[]})).text);}; + }else{ + const {WasmRuntime}=await import('/io-v2-runtime.mjs'),{ioV2Private}=await import(new URL('dist/zig-helpers.js',base)); + const g=await WasmRuntime.create(new Uint8Array(await(await fetch(new URL('runtime/zig/arboricx.wasm',base))).arrayBuffer())); + dispose=()=>g.dispose();let run=0n; + await g.scopeAsync(async()=>{ + const kept=g.stem(g.leaf),retired=g.stem(g.leaf),owned=g.stats().owned; + let rejected=false;try{g.sweepScope([kept,-1n]);}catch(error){rejected=error.message==='Stale interpreter root';} + if(!rejected||g.stats().owned!==owned)throw Error('Invalid sweep partially retired handles'); + g.sweepScope([kept,g.leaf]); + if(g.observe(kept).node.kind!=='stem')throw Error('Live sweep root lost'); + rejected=false;try{g.observe(retired);}catch{rejected=true;} + if(!rejected)throw Error('Retired native alias stayed usable'); + }); + checks.push('invalid root sweep is atomic; retired native handles reject'); + execute=async fixture=>{ + const owned=g.stats().owned; + const out=await g.scopeAsync(async()=>{ + const action=g.load(new Uint8Array(fixture.bytes),'root'); + const tree=ioV2Private.constructors({leaf:g.leaf,observe:g.observe.bind(g),stem:g.stem.bind(g),fork:g.fork.bind(g),app:g.app.bind(g)}); + return ioV2Private.runPrivate(tree,action,++run,roots=>g.sweepScope(roots)); + }); + if(g.stats().owned!==owned)throw Error('Native owned handle history retained');return out; + }; + } + const cases=[['pure','completed'],['bind','completed'],['scope','completed'],['child-0','failed'],['child-1','completed'],['unsupported','completed'],['discard-invalid','completed'],['terminal-2','failed'],['terminal-9','cancelled'],['malformed','failed']]; + try{ + for(const [name,kind]of cases){ + const out=await execute(fixtures['v2-run-'+name]); + const expected={kind,value:kind==='completed'?'42':null,cleanup:0,tasks:{scopes:0,tasks:0,hosts:0},resources:{resources:0,admissions:0,leases:0},host:{pending:0,replies:0}}; + if(JSON.stringify(out)!==JSON.stringify(expected))throw Error(name+': '+JSON.stringify(out)); + checks.push(name); + } + for(let i=0;i<5;i++)await execute(fixtures['v2-run-scope']); + checks.push('repeated run retirement and reuse; native evaluation with per-turn root sweeps'); + }finally{dispose();} + return {backend,checks}; +} diff --git a/ext/demo-site/test/io-v2-core.test.mjs b/ext/demo-site/test/io-v2-core.test.mjs @@ -0,0 +1,111 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Graph} from '../dist/graph.js'; +import {natural,text,readNatural,readList,readText} from '../dist/codecs.js'; +import {Core,Wire} from '../dist/io-v2-core.js'; +function fixture(){ + const g=new Graph(),tree={leaf:g.leaf,observe:g.observe.bind(g),fork:g.fork.bind(g),stem:g.stem.bind(g),app:g.app.bind(g),natural:n=>natural(g,n),text:s=>text(g,s)}; + const w=new Wire(tree,1n),n=x=>natural(g,BigInt(x)),act=(code,p=g.leaf)=>g.fork(n(code),p),k=x=>g.fork(g.leaf,x); + const bind=(left,right)=>act(1,g.fork(left,k(right))); + const spawn=(child,sup=true,keep=true)=>act(5,g.fork(n(sup?1:0),g.fork(n(keep?1:0),g.fork(g.leaf,k(child))))); + const request=(args=g.leaf)=>act(10,g.fork(text(g,'future'),g.fork(n(1),g.fork(text(g,'read'),args)))); + const provider={request:e=>()=>e.wire.result(false,e.wire.problem('unsupported','future/read')),release:()=>assert.fail('borrowed backend close')}; + const core=(action,p=provider,limit)=>new Core(tree,1n,action,[null,null,null],p,limit); + return {g,tree,w,n,act,k,bind,spawn,request,provider,core}; +} +async function drain(e,turn=()=>{}){ + for(let i=0;i<20000&&!e.result;i++){ + const progress=e.advance();turn(); + if(!progress){assert(e.work.counts.pending,'interpreter stalled');await e.wait();} + } + assert(e.result,'interpreter did not settle'); + assert.deepEqual(e.lifecycle.tasks.counts,{scopes:0,tasks:0,hosts:0}); + assert.deepEqual(e.lifecycle.resources.counts,{resources:0,admissions:0,leases:0}); + assert.deepEqual(e.work.counts,{pending:0,replies:0});return e.result; +} +function result(f,value){const [flag,tail]=f.w.pair(value);return {ok:f.g.observe(flag).node.kind==='stem',value:f.w.pair(tail)[0]};} + +test('real tree bind/scope/current-scope execute, with roots collected between turns',async()=>{ + const f=fixture(),e=f.core(f.bind(f.act(0,f.n(1)),f.act(3,f.k(f.act(4))))); + const out=await drain(e,()=>f.g.collect([...e.roots()]));assert.equal(out.kind,'completed'); + const parts=readList(f.g,out.value,3);assert.equal(readText(f.g,parts[0]),'io.v2.scope');assert.equal(readNatural(f.g,parts[2]),1n); +}); +test('opaque terminal diagnostics never demand payloads or abandoned continuations',async()=>{ + for(const tag of [2,9]){ + const f=fixture(),poison=f.g.app(f.g.leaf,f.g.leaf),observe=f.tree.observe; + f.tree.observe=h=>{if(h===poison)throw Error('forced opaque diagnostic');return observe(h);}; + const e=f.core(f.act(1,f.g.fork(f.act(tag,poison),poison))),out=await drain(e); + assert.equal(out.kind,tag===2?'failed':'cancelled');assert.equal(out.value,poison);assert.equal(e.fault,undefined); + } +}); +test('spawn/join encode the native outcome contract and consume tickets once',async()=>{ + const f=fixture(),join=f.act(6,f.w.marker('io.v2.join',1)); + const e=f.core(f.bind(f.spawn(f.act(0,f.n(42))),join)),out=await drain(e),joined=result(f,out.value); + assert(joined.ok);const parts=readList(f.g,joined.value,2);assert.equal(readText(f.g,parts[0]),'completed');assert.equal(readNatural(f.g,parts[1]),42n); + const other=fixture(),again=other.act(6,other.w.marker('io.v2.join',1)); + const repeated=await drain(other.core(other.bind(other.spawn(other.act(0)),other.bind(again,again)))); + const stale=result(other,repeated.value);assert(!stale.ok);assert.equal(readText(other.g,readList(other.g,stale.value,4)[0]),'stale-task'); +}); +test('supervised failure is data; uncontained failure defeats parent success',async()=>{ + for(const supervised of [true,false]){ + const f=fixture(),out=await drain(f.core(f.bind(f.spawn(f.act(2,f.n(17)),supervised),f.act(6,f.w.marker('io.v2.join',1))))); + assert.equal(out.kind,supervised?'completed':'failed'); + if(supervised){const joined=result(f,out.value);assert(joined.ok);assert.equal(readText(f.g,readList(f.g,joined.value,3)[0]),'failed');} + else assert.equal(readNatural(f.g,out.value),17n); + } +}); +test('unsupported facilities stay ordinary Results without argument demand',async()=>{ + const f=fixture(),poison=f.g.app(f.g.leaf,f.g.leaf),observe=f.tree.observe;f.tree.observe=h=>{if(h===poison)throw Error('demanded arguments');return observe(h);}; + const e=f.core(f.request(poison)),out=await drain(e,()=>f.g.collect([...e.roots()]));assert.equal(out.kind,'completed');assert.equal(result(f,out.value).ok,false); +}); +test('tree cancellation drains pending provider before join and suppresses abandoned continuation',async()=>{ + const f=fixture(),args=f.n(123);let acknowledge,aborts=0,argumentLive=false; + const gate=new Promise(resolve=>{acknowledge=resolve;}); + const p={...f.provider,request:async(e,_scope,request,signal)=>{ + await new Promise(resolve=>signal.addEventListener('abort',()=>{aborts++;resolve();},{once:true})); + await gate;argumentLive=readNatural(f.g,request.arguments)===123n;return ()=>{throw Error('cancelled reply encoder ran');}; + }}; + const cancel=f.act(8,f.g.fork(f.w.marker('io.v2.task',1),f.n(7))); + const e=f.core(f.bind(f.spawn(f.request(args)),f.bind(cancel,f.act(6,f.w.marker('io.v2.join',1)))),p); + for(let i=0;i<40;i++){const progress=e.advance();f.g.collect([...e.roots()]);if(!progress)break;} + assert.equal(aborts,1);assert.equal(e.result,null);assert.equal(e.work.counts.pending,1); + acknowledge();await e.wait();const out=await drain(e,()=>f.g.collect([...e.roots()]));assert(argumentLive); + const joined=result(f,out.value),parts=readList(f.g,joined.value,3);assert.equal(readText(f.g,parts[0]),'cancelled');assert.equal(readNatural(f.g,parts[1]),7n); +}); +test('malformed action is terminal, malformed operation is an ordinary Result',async()=>{ + const f=fixture(),terminal=await drain(f.core(f.act(1,f.g.leaf)));assert.equal(terminal.kind,'failed'); + const ordinary=await drain(f.core(f.act(6,f.g.leaf)));assert.equal(ordinary.kind,'completed');assert(!result(f,ordinary.value).ok); +}); +test('evaluator fault after lexical admission drains admitted scopes without retrying evaluation',async()=>{ + const f=fixture(),failure=new Error('injected evaluator failure');let applications=0; + f.tree.app=()=>{applications++;throw failure;};const e=f.core(f.act(3,f.k(f.act(0)))); + assert.equal((await drain(e)).kind,'failed');assert.equal(e.fault,failure);assert.equal(applications,1); +}); +test('action bound stops the entire run and cleans up',async()=>{ + const f=fixture(),e=f.core(f.bind(f.act(0),f.act(0)),f.provider,1); + assert.equal((await drain(e)).kind,'failed');assert.equal(e.fault,undefined); +}); + +test('evaluator errors cannot masquerade as protocol or ordinary-operation errors',async()=>{ + for(const message of ['invalid computation shape','invalid-argument']){ + const f=fixture(),fault=new Error(message),action=f.act(6,f.g.leaf),observe=f.tree.observe; + f.tree.observe=h=>{if(h===(message==='invalid-argument'?f.g.leaf:action))throw fault;return observe(h);}; + const e=f.core(action);assert.equal((await drain(e)).kind,'failed');assert.equal(e.fault,fault);assert(e.hasFault); + } +}); +test('undefined provider rejection is retained as an embedding fault, not successful undefined data',async()=>{ + const f=fixture(),e=f.core(f.request(),{...f.provider,request:()=>Promise.reject(undefined)}); + assert.equal((await drain(e)).kind,'failed');assert(e.hasFault);assert.equal(e.fault,undefined); +}); + +test('lexical physical cleanup failure defeats success once, with uncertain release progress',async()=>{ + const f=fixture();let closed=0; + const e=f.core(f.act(3,f.k(f.request())),{ + request:(engine,scope)=>{ + const r=engine.lifecycle.resources;r.publish(r.beginAcquire(scope),'test',null); + return ()=>engine.wire.result(true,engine.tree.leaf); + },release:()=>{closed++;throw Error('uncertain close');} + }); + const out=await drain(e);assert.equal(out.kind,'failed');assert.equal(closed,1);assert.equal(out.cleanup.items.length,0); + const problem=readList(f.g,out.value,4);assert.equal(readText(f.g,problem[0]),'cleanup-failed');assert.equal(readText(f.g,problem[3]),'unknown'); +}); diff --git a/ext/demo-site/test/io-v2-events.test.mjs b/ext/demo-site/test/io-v2-events.test.mjs @@ -0,0 +1,33 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Events} from '../dist/io-v2-events.js'; +test('invalid/stale/wrong-kind replies preserve the live read',async()=>{ + const e=new Events('1'),c=new AbortController(),pending=e.read(3,c.signal); + assert.equal(e.reply({run:'1',id:1,kind:'read',bytes:[1],eof:false}),false); + const event=e.take()[0]; + for(const change of [{run:'2'},{id:2},{kind:'write',count:1},{bytes:[0,1,2,3]},{bytes:[256]},{bytes:[],eof:false},{bytes:[1],eof:true},{kind:'cancelled'}]) + assert.equal(e.reply({...event,bytes:[1],eof:false,...change}),false); + assert.equal(e.counts.pending,1);assert(e.reply({...event,bytes:[1,2],eof:false}));assert.deepEqual(await pending,{kind:'read',bytes:[1,2],eof:false}); + assert.equal(e.reply({...event,bytes:[1],eof:false}),false);assert.deepEqual(e.counts,{pending:0,queued:0}); +}); +test('cancellation before dispatch has no host effect; delivered cancellation waits for acknowledgement',async()=>{ + const e=new Events('1'),a=new AbortController(),p=e.read(4,a.signal);a.abort();assert.deepEqual(e.take(),[]);assert.deepEqual(await p,{kind:'cancelled'}); + const b=new AbortController(),q=e.read(4,b.signal),event=e.take()[0];let settled=false;q.then(()=>{settled=true;}); + b.abort();b.abort();await Promise.resolve();assert(!settled);assert.deepEqual(e.take(),[{run:'1',id:event.id,kind:'cancel',operation:'read'}]); + assert(e.reply({...event,kind:'cancelled'}));assert.deepEqual(await q,{kind:'cancelled'});assert.deepEqual(e.counts,{pending:0,queued:0}); +}); +test('normal completion racing abort acknowledges cleanup but discards its data',async()=>{ + const e=new Events('1'),c=new AbortController(),p=e.write('output',[1,2],c.signal),event=e.take()[0];c.abort(); + assert(e.reply({...event,kind:'write',count:1}));assert.deepEqual(await p,{kind:'cancelled'});assert.deepEqual(e.take(),[]); +}); +test('output snapshots cannot mutate validation; partial progress is explicit',async()=>{ + const e=new Events('1'),bytes=[1,2],c=new AbortController(),p=e.write('error',bytes,c.signal);bytes.push(3); + const event=e.take()[0];event.bytes.push(4);event.kind='read'; + assert(!e.reply({run:'1',id:1,kind:'write',count:3}));assert(e.reply({run:'1',id:1,kind:'write',count:1})); + assert.deepEqual(await p,{kind:'write',count:1}); +}); +test('identity exhaustion cannot prevent cancellation or retirement; repeated operations retain no history',async()=>{ + const e=new Events('1',1),c=new AbortController(),p=e.read(1,c.signal),event=e.take()[0]; + assert.throws(()=>e.read(1,c.signal),/exhausted/);c.abort();assert(e.reply({...event,kind:'cancelled'}));await p; + const many=new Events('2');for(let i=0;i<1000;i++){const pending=many.read(1,new AbortController().signal),ev=many.take()[0];assert(many.reply({...ev,bytes:[],eof:true}));await pending;assert.deepEqual(many.counts,{pending:0,queued:0});} +}); diff --git a/ext/demo-site/test/io-v2-host-work.test.mjs b/ext/demo-site/test/io-v2-host-work.test.mjs @@ -0,0 +1,94 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Tasks} from '../dist/io-v2-tasks.js'; +import {HostWork} from '../dist/io-v2-host-work.js'; +const ok=value=>({kind:'completed',value}),failed=value=>({kind:'failed',value}); +const finish=(tasks,scope)=>{tasks.bodyDone(scope,ok(null));assert(tasks.beginRelease(scope));return tasks.settle(scope);}; +const deferred=()=>{let resolve;const promise=new Promise(done=>{resolve=done;});return {promise,resolve};}; +const setup=()=>{const tasks=new Tasks(1n,'owner-stopped','cycle');return {tasks,work:new HostWork(tasks)};}; + +test('pending read never blocks sibling work; cancellation waits for actual provider cleanup',async()=>{ + const {tasks,work}=setup(),read=tasks.spawn(0,true,true),writer=tasks.spawn(0,true,false),cleanup=deferred(),events=[]; + const opaque=new Proxy({},{get(){throw Error('late value forced');}}); + work.start(read.key,async signal=>{ + events.push('read started'); + await new Promise(resolve=>signal.addEventListener('abort',()=>{events.push('abort requested');resolve();},{once:true})); + await cleanup.promise;events.push('read cleanup finished');return opaque; + }); + work.start(writer.key,()=>{events.push('sibling wrote');return 7;}); + await work.wait();assert.equal(work.take().outcome.value,7);finish(tasks,writer.key); + tasks.cancel(0,read,'stop');work.cancelStopped();work.cancelStopped(); + tasks.bodyDone(read.key,ok(null));assert.equal(tasks.beginRelease(read.key),false); + await Promise.resolve();assert.deepEqual(events,['read started','sibling wrote','abort requested']); + const settled=work.wait();cleanup.resolve();await settled; + assert.equal(work.take(),null);assert.deepEqual(work.counts,{pending:0,replies:0}); + assert(tasks.beginRelease(read.key));tasks.settle(read.key); + tasks.claimJoin(0,read);assert.equal(tasks.pollJoin(0).kind,'cancelled');finish(tasks,0); + assert.equal(events.at(-1),'read cleanup finished');assert.deepEqual(tasks.counts,{scopes:0,tasks:0,hosts:0}); +}); +test('completion queued before cancellation is discarded and cannot revive the body',async()=>{ + const {tasks,work}=setup(),child=tasks.spawn(0,true,false),value={retained:'reply'}; + work.start(child.key,()=>value);await work.wait();assert.deepEqual([...work.values()],[value]); + tasks.cancel(0,child,'stop');work.cancelStopped();assert.deepEqual([...work.values()],[]);assert.equal(work.take(),null); + finish(tasks,child.key);finish(tasks,0);assert.deepEqual(work.counts,{pending:0,replies:0}); +}); +test('queued data for an already settled scope is dropped; no scope history required',async()=>{ + const {tasks,work}=setup(),child=tasks.spawn(0,true,false); + work.start(child.key,()=>42);await work.wait();finish(tasks,child.key); + work.cancelStopped();assert.equal(work.take(),null);assert.deepEqual(work.counts,{pending:0,replies:0});finish(tasks,0); +}); +test('synchronous throws and async rejections settle ownership once without inspecting errors',async()=>{ + for(const asynchronous of [false,true]){ + const {tasks,work}=setup(),error=new Proxy({},{get(){throw Error('opaque error forced');}}); + work.start(0,()=>{if(asynchronous)return Promise.reject(error);throw error;}); + await work.wait();const reply=work.take();assert.equal(reply.outcome.ok,false);assert.equal(reply.outcome.error,error); + assert.equal(work.take(),null);assert.equal(tasks.state(0).hostCalls,0);finish(tasks,0); + } +}); +test('read rejection after cancellation is acknowledgement, not a fresh task failure',async()=>{ + const {tasks,work}=setup(),child=tasks.spawn(0,true,false); + work.start(child.key,signal=>new Promise((_resolve,reject)=>signal.addEventListener('abort',()=>reject(Error('abort')),{once:true}))); + tasks.stop(child.key,failed('original'));work.cancelStopped();tasks.bodyDone(child.key,ok(null)); + await work.wait();assert.equal(work.take(),null);assert(tasks.beginRelease(child.key));assert.equal(tasks.settle(child.key).value,'original'); + finish(tasks,0); +}); +test('multiple waiters share notification; out-of-order replies have correct identities',async()=>{ + const {tasks,work}=setup(),a=deferred(),b=deferred(); + const first=work.start(0,()=>a.promise),second=work.start(0,()=>b.promise); + const one=work.wait(),two=work.wait();assert.equal(one,two);b.resolve('second');await one; + assert.deepEqual(work.take(),{token:second,outcome:{ok:true,value:'second'}}); + const next=work.wait();a.resolve('first');await next; + assert.deepEqual(work.take(),{token:first,outcome:{ok:true,value:'first'}});finish(tasks,0); +}); +test('sequential host operations retain neither callback history nor consumed reply data',async()=>{ + const {tasks,work}=setup(); + for(let i=0;i<1000;i++){ + const value={iteration:i};work.start(0,()=>value);await work.wait();assert.equal(work.take().outcome.value,value); + assert.deepEqual(work.counts,{pending:0,replies:0});assert.equal(tasks.state(0).hostCalls,0);assert.deepEqual([...work.values()],[]); + } + finish(tasks,0); +}); +test('async physical release survives new cancellation and reports cleanup failure',async()=>{ + const {tasks,work}=setup(),release=deferred(); + assert.throws(()=>work.release(0,()=>null),/invalid-transition/); + tasks.bodyDone(0,ok('body'));assert(tasks.beginRelease(0)); + const token=work.release(0,async()=>{await release.promise;throw 'cleanup failed';}); + assert.equal(token.kind,'release');assert.throws(()=>tasks.settle(0),/invalid-transition/); + tasks.stop(0,{kind:'cancelled',value:'primary'});work.cancelStopped(); + assert.deepEqual(tasks.takeCancellations(),[]);assert.throws(()=>tasks.settle(0),/invalid-transition/); + const waiting=work.wait();release.resolve();await waiting; + const reply=work.take();assert.equal(reply.outcome.ok,false);assert.equal(reply.outcome.error,'cleanup failed'); + const result=tasks.settle(0,{items:[reply.outcome.error],omitted:0}); + assert.equal(result.kind,'cancelled');assert.equal(result.value,'primary');assert.deepEqual(result.cleanup.items,['cleanup failed']); + assert.deepEqual(work.counts,{pending:0,replies:0}); +}); +test('body callback identity exhaustion cannot prevent the reserved cleanup batch',async()=>{ + const tasks=new Tasks(1n,'stop','cycle',1),work=new HostWork(tasks); + work.start(0,()=>1);await work.wait();work.take(); + assert.throws(()=>work.start(0,()=>2),/identity-exhausted/); + tasks.bodyDone(0,ok(null));assert(tasks.beginRelease(0)); + const release=work.release(0,()=>3);await work.wait();assert.equal(work.take().outcome.value,3); + assert.equal(release.kind,'release');assert.equal(tasks.endHost(release),'stale'); + assert.throws(()=>work.release(0,()=>4),/invalid-transition/);tasks.settle(0); + assert.deepEqual(tasks.counts,{scopes:0,tasks:0,hosts:0}); +}); diff --git a/ext/demo-site/test/io-v2-lifecycle-probe.mjs b/ext/demo-site/test/io-v2-lifecycle-probe.mjs @@ -0,0 +1,49 @@ +// Lifecycle building blocks with a real DOM read/provider and native pure work. +// This does NOT execute a v2 language program or bypass the production v2 gate. +export async function run(_control,backend){ + const assert=(value,message)=>{if(!value)throw Error(message);}; + const base=new URL('../',document.querySelector('script[type=module][src]').src); + const {ioV2Resources:{lifecycle},ioV2HostWork:{HostWork},runtimeSession}=await import(new URL('dist/probe-api.js',base)); + const session=await runtimeSession.createRuntimeSession(backend,false),checks=[]; + const {tasks,resources}=lifecycle(1n,'owner-stopped','cycle',[null,null,null]),work=new HostWork(tasks); + const input=document.createElement('input');input.id='lifecycle-test-input';document.body.append(input); + const original=resources.publish(resources.beginAcquire(0),'reader',input); + const {task:read,handles}=resources.spawn(0,true,true,[original]),other=tasks.spawn(0,true,false); + let stale=false;try{resources.lookup(0,original);}catch(error){stale=error.message==='stale-handle';}assert(stale,'old alias survived transfer'); + let acknowledge,aborts=0,events=0,signal; + const cleanup=new Promise(resolve=>{acknowledge=resolve;}); + const listener=()=>{events++;};input.addEventListener('input',listener); + const release=async id=>{work.release(id,()=>resources.releaseScope(id,r=>r.backend.remove(),e=>e,'cleanup-failed'));await work.wait();const reply=work.take();assert(reply?.outcome.ok,'release failed');return tasks.settle(id,reply.outcome.value);}; + const finish=async id=>{tasks.bodyDone(id,{kind:'completed',value:null});assert(tasks.beginRelease(id),'release blocked');return release(id);}; + try{ + const fixture=(await(await fetch('/zig-negative.json')).json())['pure-number']; + await session.load(new Uint8Array(fixture.bytes),fixture.hash); + work.start(read.key,async abort=>{ + signal=abort;const lease=resources.beginLease(read.key,handles[0],'reader','read'); + try{await new Promise(resolve=>abort.addEventListener('abort',()=>{aborts++;input.removeEventListener('input',listener);resolve();},{once:true})); + await cleanup;return 'late read value';}finally{resources.endLease(lease.token);} + }); + const forbidden=()=>{throw Error('pure work invoked IO hook');}; + work.start(other.key,()=>session.run('root',[],'number','pure',{before:forbidden,after:forbidden,read:forbidden,write:forbidden,running:forbidden})); + await work.wait();const reply=work.take();assert(reply.outcome.ok&&reply.outcome.value.value==='42','native sibling result');await finish(other.key); + assert(!signal.aborted&&tasks.state(read.key).hostCalls===1,'read was detached'); + input.dispatchEvent(new Event('input'));assert(events===1,'listener not live'); + checks.push('pending DOM read does not block sibling native pure evaluation'); + tasks.cancel(0,read,'cancel read');work.cancelStopped();work.cancelStopped(); + tasks.bodyDone(read.key,{kind:'completed',value:null}); + assert(aborts===1&&signal.aborted,'abort delivered more than once'); + input.dispatchEvent(new Event('input'));assert(events===1,'cancelled listener still active'); + assert(!tasks.beginRelease(read.key)&&input.isConnected,'cancellation request was treated as completion'); + const wait=work.wait();acknowledge();await wait; + assert(input.isConnected&&work.take()===null,'resource closed before scope release or late reply escaped'); + assert(resources.counts.leases===0,'read lease survived acknowledgement'); + assert(tasks.beginRelease(read.key),'acknowledged read did not release');await release(read.key); + assert(!input.isConnected,'owned DOM resource escaped physical release'); + tasks.claimJoin(0,read);assert(tasks.pollJoin(0).kind==='cancelled','cancelled join outcome');await finish(0); + assert(tasks.counts.scopes===0&&tasks.counts.tasks===0&&tasks.counts.hosts===0,'lifecycle retained history'); + assert(work.counts.pending===0&&work.counts.replies===0,'host work retained history'); + assert(resources.counts.resources===0&&resources.counts.leases===0&&resources.counts.admissions===0,'registry retained history'); + checks.push('transferred alias invalidation; leased DOM read; one abort; physical acknowledgement and resource release before cancelled join'); + }finally{acknowledge?.();input.removeEventListener('input',listener);input.remove();session.dispose();} + return {backend,checks}; +} diff --git a/ext/demo-site/test/io-v2-output.test.mjs b/ext/demo-site/test/io-v2-output.test.mjs @@ -0,0 +1,51 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {OutputBuffer} from '../dist/io-v2-output.js'; +const encode=text=>[...new TextEncoder().encode(text)]; +const reconstruct=view=>view.tokens.flatMap(t=>t.kind==='text'?encode(t.text):t.bytes); +test('valid UTF8 remains text including BOM, NUL, CRLF, combining and literal escape-looking text',()=>{ + const text='\ufeff\0\r\nλ😀e\u0301\\xff <script>\x1b[31m',buffer=new OutputBuffer(); + buffer.append('output',encode(text));buffer.finish();const out=buffer.snapshot().output; + assert.deepEqual(out.tokens,[{kind:'text',offset:0,text}]);assert.deepEqual(reconstruct(out),encode(text)); +}); +test('all byte values survive exact raw, hex and token reconstruction',()=>{ + const bytes=Array.from({length:256},(_,i)=>i),buffer=new OutputBuffer();buffer.append('output',bytes);buffer.finish(); + const out=buffer.snapshot().output;assert.deepEqual(out.bytes,bytes);assert.deepEqual(reconstruct(out),bytes); + assert.deepEqual(out.hex.split(' ').map(s=>parseInt(s,16)),bytes); + assert(out.tokens.some(t=>t.kind==='bytes'&&t.reason==='invalid')); +}); +test('partitioning never changes final presentation and valid text after bad prefixes survives',()=>{ + const bytes=[...encode('a😀λ'),0xe0,0x80,65,0xed,0xa0,0x80,0xf4,0x90,0x80,0x80,66,0xf0,0x9f]; + const all=new OutputBuffer();all.append('output',bytes);all.finish();const expected=all.snapshot(); + for(let split=1;split<bytes.length;split++){ + const b=new OutputBuffer();b.append('output',bytes.slice(0,split)); + assert.deepEqual(reconstruct(b.snapshot().output),bytes.slice(0,split)); + b.append('output',bytes.slice(split));b.finish();assert.deepEqual(b.snapshot(),expected); + } + assert(expected.output.tokens.some(t=>t.kind==='text'&&t.text==='A')); + assert.equal(expected.output.tokens.at(-1).reason,'incomplete'); +}); +test('pending suffixes resolve across writes but never across stdout/stderr',()=>{ + const b=new OutputBuffer();b.append('output',[0xf0,0x9f]);b.append('error',[0x98,0x80]); + assert.equal(b.snapshot().output.tokens[0].reason,'pending'); + assert(b.snapshot().error.tokens.every(t=>t.reason==='invalid')); + b.append('output',[0x98,0x80]);assert.equal(b.snapshot().output.tokens[0].text,'😀'); + b.append('error',[0xc2]);b.finish();b.finish();assert.equal(b.snapshot().error.tokens.at(-1).reason,'incomplete'); + assert.throws(()=>b.append('output',[65]),/ended/); +}); +test('accepted-byte bound is shared and rejection is atomic; copies have no aliases',()=>{ + const b=new OutputBuffer(3),bytes=[65,66];b.append('output',bytes);bytes[0]=0; + const snapshot=b.snapshot();snapshot.output.bytes[0]=0;snapshot.output.tokens[0].text='changed'; + assert.equal(b.snapshot().output.tokens[0].text,'AB');b.append('error',[255]); + const before=b.snapshot();assert.throws(()=>b.append('error',[1]),/limit/);assert.deepEqual(b.snapshot(),before); + for(const bytes of [[256],[-1],[1.5],Array(1),[],Array(4097).fill(0)])assert.throws(()=>b.append('output',bytes),/bytes/); + assert.deepEqual(b.snapshot(),before); + for(const limit of [-1,16385,NaN,1.5])assert.throws(()=>new OutputBuffer(limit),/limit/); +}); +test('many independent malformed streams reconstruct exactly without replacement scalars',()=>{ + let state=12345;for(let run=0;run<128;run++){ + const bytes=Array.from({length:128},()=>{state=(Math.imul(state,1664525)+1013904223)>>>0;return state>>>24;}); + const b=new OutputBuffer();for(const byte of bytes)b.append('output',[byte]);b.finish(); + assert.deepEqual(reconstruct(b.snapshot().output),bytes); + } +}); diff --git a/ext/demo-site/test/io-v2-protocol-probe.mjs b/ext/demo-site/test/io-v2-protocol-probe.mjs @@ -0,0 +1,33 @@ +// Real WASM engines, inert header inspection only, no source compiler substitute. +export async function run(_control,backend){ + const base=new URL('../',document.querySelector('script[type=module][src]').src); + const fixtures=await(await fetch('/zig-negative.json')).json(),checks=[]; + let inspect,dispose; + if(backend==='haskell-wasm'){ + const {CompilerWorker}=await import(new URL('runtime/haskell/client.mjs',base)); + const client=new CompilerWorker();dispose=()=>client.dispose(); + await client.request({type:'init',compiler:false}); + inspect=async fixture=>{await client.request({type:'load',bytes:new Uint8Array(fixture.bytes).buffer,hash:fixture.hash});return JSON.parse((await client.request({type:'io-v2-header',name:'root',arguments:[]})).text);}; + }else{ + const {WasmRuntime}=await import('/io-v2-runtime.mjs'); + const {ioV2Protocol}=await import(new URL('dist/zig-helpers.js',base)); + const graph=await WasmRuntime.create(new Uint8Array(await(await fetch(new URL('runtime/zig/arboricx.wasm',base))).arrayBuffer())); + dispose=()=>graph.dispose(); + inspect=async fixture=>graph.scope(()=>ioV2Protocol.header(graph,graph.load(new Uint8Array(fixture.bytes),'root'))); + } + const equal=(got,want)=>{if(JSON.stringify(got)!==JSON.stringify(want))throw Error('Header mismatch: '+JSON.stringify(got));}; + try{ + for(let code=0;code<10;code++)equal(await inspect(fixtures['v2-header-core-'+code]),{kind:'core',code}); + checks.push('all ten core headers without expanding shared payloads'); + equal(await inspect(fixtures['v2-header-future']),{kind:'request',group:'future-9',revision:65535,operation:'unknown'}); + equal(await inspect(fixtures['v2-header-long-name']),{kind:'request',group:'a'.repeat(64),revision:1,operation:'read'}); + checks.push('future named request and exact name/revision bounds'); + for(const [name,message] of [['bad-tag','unknown computation constructor'],['bad-name','invalid facility header'],['bad-revision','invalid facility header'],['zero-revision','invalid facility header'],['bad-shape','invalid computation shape']]){ + let rejected=false;try{await inspect(fixtures['v2-header-'+name]);}catch(error){if(!error.message.includes(message))throw error;rejected=true;} + if(!rejected)throw Error('Accepted '+name); + equal(await inspect(fixtures['v2-header-core-0']),{kind:'core',code:0}); + } + checks.push('five malformed headers reject; runtime remains usable after each'); + }finally{dispose();} + return {backend,checks}; +} diff --git a/ext/demo-site/test/io-v2-protocol.test.mjs b/ext/demo-site/test/io-v2-protocol.test.mjs @@ -0,0 +1,37 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Graph} from '../dist/graph.js'; +import {natural,text} from '../dist/codecs.js'; +import {canonicalNat,decodeAction,header} from '../dist/io-v2-protocol.js'; +function setup(){ + const g=new Graph(),poison=g.app(g.leaf,g.leaf),observe=g.observe.bind(g); + g.observe=id=>{if(id===poison)throw Error('FORCED opaque payload');return observe(id);}; + const request=(group,revision,op,args=poison)=>g.fork(natural(g,10n),g.fork(group,g.fork(revision,g.fork(op,args)))); + return {g,poison,request}; +} +test('v2 header decoding preserves opaque core and future request payloads',()=>{ + const {g,poison,request}=setup(); + for(let n=0;n<10;n++)assert.deepEqual(decodeAction(g,g.fork(natural(g,BigInt(n)),poison)),{kind:'core',code:n,payload:poison}); + for(const n of [11n,255n,256n])assert.throws(()=>decodeAction(g,g.fork(natural(g,n),poison)),/unknown computation/); + assert.deepEqual(header(g,request(text(g,'future-9'),natural(g,65535n),text(g,'unknown'))),{kind:'request',group:'future-9',revision:65535,operation:'unknown'}); + assert.throws(()=>decodeAction(g,g.leaf),/invalid computation shape/); +}); +test('v2 names, revisions and bits are bounded and strictly canonical',()=>{ + const {g,poison,request}=setup(),good=text(g,'file'),one=natural(g,1n),zero=g.fork(g.leaf,g.leaf); + for(const invalid of ['', '-file','1file','File','a_b','a b','a\x7f','a'.repeat(65)]){ + assert.throws(()=>decodeAction(g,request(text(g,invalid),one,good)),/invalid facility header/); + assert.throws(()=>decodeAction(g,request(good,one,text(g,invalid))),/invalid facility header/); + } + for(const revision of [g.leaf,zero,natural(g,65536n)])assert.throws(()=>decodeAction(g,request(good,revision,good)),/invalid facility header/); + assert.throws(()=>decodeAction(g,request(g.leaf,poison,poison)),/invalid facility header/); + assert.equal(canonicalNat(g,zero,8),undefined); + assert.throws(()=>decodeAction(g,g.fork(zero,poison)),/unknown computation/); + assert.equal(header(g,request(text(g,'a'.repeat(64)),one,good)).group.length,64); + let excess=g.fork(poison,poison); + for(let i=0;i<64;i++)excess=g.fork(natural(g,97n),excess); + assert.throws(()=>decodeAction(g,request(excess,one,good)),/invalid facility header/); + let long=g.fork(poison,poison); + for(let i=0;i<8;i++)long=g.fork(g.leaf,long); + assert.equal(canonicalNat(g,long,8),undefined); + assert.throws(()=>decodeAction(g,poison),/FORCED/); // reduction faults are not malformed-header Results +}); diff --git a/ext/demo-site/test/io-v2-public-probe.mjs b/ext/demo-site/test/io-v2-public-probe.mjs @@ -0,0 +1,86 @@ +import {wait,status,change,inspect,el,pause,input} from './source-probe-helpers.mjs'; +export async function run(_control,backend){ + const assert=(ok,message)=>{if(!ok)throw Error(message);},checks=[]; + const base=new URL('../',document.querySelector('script[type=module][src]').src); + const {runtimeSession:{createRuntimeSession},ioV2TerminalView:{terminalView}}=await import(new URL('dist/probe-api.js',base)); + const fixture=await(await fetch('/io-v2-source.json')).json(),s=await createRuntimeSession(backend,false); + const legacy={before:async()=>{},after(){},read(){throw Error('legacy read invoked');},write(){throw Error('legacy write invoked');},running(){}}; + const parent=document.createElement('div');document.body.append(parent); + let widget,send,closed,steps=0,submitted=false,guardTask; + function host(mode='normal'){ + parent.replaceChildren();closed=false;submitted=false; + return {...legacy,before:async()=>{steps++;},v2:{stepping:mode==='step', + open(run,command){send=command;widget=terminalView(parent,run,reply=>command({run,kind:'reply',reply}),()=>command({run,kind:'stop'}));}, + event(event){widget.handle(event);if(event.kind==='read'&&mode==='stop'){ + guardTask=(async()=>{try{ + for(const operation of [()=>s.inspectDefinition('main','tree'),()=>s.compile('changed = 9','empty'),()=>s.runExpression('42','number','pure',legacy)]){ + let blocked=false;try{await operation();}catch(error){blocked=/pending|busy/.test(error.message);}assert(blocked,'unrelated work entered active v2 run'); + } + }finally{send({run:event.run,kind:'stop'});}})(); + } + if(event.kind==='read'&&mode==='input'){ + const form=parent.querySelector('form');if(form&&!form.hidden){if(!submitted){submitted=true;form.querySelector('input').value='λ😀';form.requestSubmit();}else form.querySelector('button[type=button]').click();} + } + }, + close(_run,settled){assert(settled,'unacknowledged runtime loss');widget.finish();closed=true;}, + }}; + } + try{ + await s.load(new Uint8Array(fixture.bytes),fixture.hash); + assert(await s.probeIO('main'),'v2 inert recognition');assert(!await s.probeIO('direct'),'bare computation is not an envelope'); + for(const name of ['main','publicHandled','publicCancellation','publicBinary']){ + const out=await s.runDefinitionIO(name,'number',host());assert(out.value==='42'&&closed,'public compiled outcome '+name); + const bytes=widget.snapshot().output.bytes,expected=name==='publicBinary'?[92,120,102,102,255,240,159]:[...new TextEncoder().encode(name==='publicCancellation'?'ready':'λ😀e\u0301\r\n')]; + assert(JSON.stringify(bytes)===JSON.stringify(expected),'public exact output '+name); + } + assert((await s.runDefinitionIO('publicRoundTrip','number',host('input'))).value==='42','line and EOF outcome'); + assert(JSON.stringify(widget.snapshot().output.bytes)===JSON.stringify([...new TextEncoder().encode('λ😀\n')]),'line/EOF byte preservation'); + checks.push('compiled direct/handled/cancellation/binary programs through public RuntimeSession; explicit line/EOF'); + const cases=[['string',{type:'string',value:'λ😀\0'},'λ😀\0'],['number',{type:'number',value:'184467440737095516160'},'184467440737095516160'],['bool',{type:'bool',value:true},true],['bytes',{type:'bytes',value:[0,255,240]},[0,255,240]]]; + for(const [type,literal,expected]of cases){const out=await s.run('returnValue',[literal],type,'io',host());assert(JSON.stringify(out.value)===JSON.stringify(expected),'full codec '+type);} + for(const type of ['tree','list','tricu','triangle','dag','ternary']){ + const literal=type==='list'?{type:'list',value:[{type:'number',value:'42'}]}:{type:'tree',value:'200'}; + const observed=await s.run('returnValue',[literal],type,'io',host());assert(observed.type===type&&observed.value!==null,'full graph codec '+type); + if(['tricu','triangle','dag','ternary'].includes(type)){const expected=await s.runExpression('t t t',type,'pure',legacy);// DAG binders reflect physical node numbering, not semantic identities. + const alpha=text=>{const names=new Map();return text.replace(/:[fn][0-9]+/g,name=>{if(!names.has(name))names.set(name,':v'+names.size);return names.get(name);});}; + assert((type==='dag'?alpha(expected.value)===alpha(observed.value):expected.value===observed.value),'complete graph result differs from independent pure result '+type);} + } + const values=await s.run('returnValue',[{type:'list',value:[{type:'number',value:'42'},{type:'string',value:'hello'}]}],'values','io',host()); + assert(JSON.stringify(values.value)===JSON.stringify([{type:'number',value:'42'},{type:'string',value:'hello'}]),'values codec'); + checks.push('all public codecs including >64-bit number, full trees and decoded values'); + let rejected=false;try{await s.runDefinitionIO('publicRoundTrip','number',host('stop'));}catch(error){rejected=/cancelled/.test(error.message);}assert(rejected&&closed,'public structured cancellation');await guardTask; + let badCodec=false;try{await s.run('returnValue',[{type:'tree',value:'10'}],'number','io',host());}catch{badCodec=true;}assert(badCodec&&closed,'codec failure after cleanup'); + assert((await s.runExpression('returnValue "expression result"','string','io',host())).value==='expression result','IO expression result'); + assert((await s.run('returnValue',[{type:'number',value:'42'}],'number','io',host('step'))).value==='42'&&steps>0,'public scheduler stepping'); + checks.push('structured Stop drains read, definitions remain usable; expression and stepping paths'); + }finally{s.dispose();parent.remove();} + const doomed=await createRuntimeSession(backend,false);let abandoned=false; + try{ + await doomed.load(new Uint8Array(fixture.bytes),fixture.hash); + let rejected=false;try{await doomed.runDefinitionIO('publicRoundTrip','number',{...legacy,v2:{open(){},event(event){if(event.kind==='read')doomed.dispose();},close(_run,settled){abandoned=!settled;}}});} + catch(error){rejected=/disposed|terminated/i.test(error.message);}assert(rejected&&abandoned,'emergency dispose must reject without claiming cleanup'); + }finally{doomed.dispose();} + checks.push('emergency runtime disposal rejects idle read and explicitly does not acknowledge cleanup'); + // Real mounted editor + outer worker + nested native worker, not a mock host. + await status('Ready');if(el('runtime-backend').value!==backend){change('runtime-backend',backend);await status('Ready');} + const index=await(await fetch(new URL('packages.json',base))).json(); + const io=index.releases.find(r=>r.title==='lib/io-v2.tri'&&r.version==='0.4.0');assert(io,'authenticated complete library'); + const source=`!import ${io.manifest} as IO\n!export answer\n!export binary\n!export waiting\n!export caps\n!export classification\n!export divergentValue\ndivergentValue = IO.io (IO.pure ((x : x x) (x : x x)))\nbackendOf rows = case rows of\n [["backend" value] | _] = value\n [_ | rest] = backendOf rest\n _ = "missing"\ncaps = IO.io (IO.require IO.capabilities (rows : IO.pure (backendOf rows)))\nloop = (x : x x) (x : x x)\nclassification = IO.io (IO.require (IO.inspect (IO.request "file" 1 "open" loop)) IO.pure)\nanswer = 42\nbinary = IO.io (do IO.require\n writer <- IO.stdout\n IO.writeAll writer [92 120 102 102 255 240 159]\n IO.pure "returned text")\nwaiting = IO.io (do IO.require\n reader <- IO.stdin\n IO.readSome reader 4\n IO.pure 42)`; + document.querySelector('[data-compiler-example=identity]').click();input('source-editor',source);await status('Compiled'); + await inspect('caps');el('io-return-format').value='string';el('run-io').click();await status('Finished');assert(document.querySelector('.definition-card').querySelector('pre').textContent===backend,'actual native capabilities'); + await inspect('classification');el('io-return-format').value='values';el('run-io').click();await status('Finished');assert(document.querySelector('.definition-card').querySelector('pre').textContent.includes('unsupported'),'inert inspection leaves divergent arguments opaque'); + await inspect('binary');el('io-return-format').value='string';el('run-io').click();await status('Finished'); + assert(el('terminal-v2').querySelector('[data-hex=output]').textContent==='5c 78 66 66 ff f0 9f','actual page lossless output'); + assert(document.querySelector('.definition-card').querySelector('pre').textContent==='returned text','actual page full returned text'); + await inspect('waiting');el('io-return-format').value='number';el('run-io').click();await wait(()=>el('terminal-v2').querySelector('form input'),'v2 input'); + const old=el('terminal-v2').querySelector('form');el('stop').click();assert(!el('stop').hidden,'Stop waits rather than disposing worker'); + await status('Stopped');assert(!el('terminal-v2').querySelector('form'),'input physically retired'); + old.dispatchEvent(new Event('submit',{cancelable:true}));assert((await inspect('answer','number'))==='42','compiled state survives structured Stop'); + // Stop also bypasses a suspended scheduler turn. + await inspect('waiting');el('step-mode').checked=true;el('run-io').click();await status('Paused before action');el('stop').click();await status('Stopped');el('step-mode').checked=false; + assert((await inspect('answer','number'))==='42','compiled state survives paused Stop'); + await inspect('divergentValue');el('io-return-format').value='number';el('run-io').click();await status('IO settled; interpreting returned value');el('stop').click();await status('Stopped'); + el('compile').click();await status('Compiled');assert((await inspect('answer','number'))==='42','hard Stop after acknowledged IO settlement can rebuild'); + await pause(10);checks.push('actual page exact bytes/full return, pending-input and paused-step Stop, stale DOM isolation and reuse without recompilation'); + return {backend,checks}; +} diff --git a/ext/demo-site/test/io-v2-public.test.mjs b/ext/demo-site/test/io-v2-public.test.mjs @@ -0,0 +1,104 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Graph} from '../dist/graph.js'; +import {readText,readNatural,readList,view} from '../dist/codecs.js'; +import {constructors} from '../dist/io-v2-private.js'; +import {Wire} from '../dist/io-v2-core.js'; +import {Session} from '../dist/io-v2-session.js'; +import {envelope,available} from '../dist/io-v2-profile.js'; +import {drive} from '../dist/io-v2-driver.js'; +function fixture(){ + const g=new Graph(),t=constructors({leaf:g.leaf,observe:g.observe.bind(g),stem:g.stem.bind(g),fork:g.fork.bind(g),app:g.app.bind(g)}),w=new Wire(t,1n); + const n=x=>t.natural(BigInt(x)),a=(tag,p=t.leaf)=>t.fork(n(tag),p),request=(group,op,p=t.leaf,revision=1)=>a(10,t.fork(t.text(group),t.fork(n(revision),t.fork(t.text(op),p)))); + return {g,t,w,n,a,request,session:(action,steps=128)=>new Session(t,action,'1',roots=>g.collect([...roots]),{summarize:false,steps,backend:'test-native'})}; +} +const host=()=>({before:async()=>{},after(){},read(){throw Error('legacy read');},write(){throw Error('legacy write');},running(){}}); +const driver=(s,read)=>({run:s.run,turn:c=>s.turn(c),view:async()=>read(s.core.result.value)}); +async function finish(s){for(let i=0;i<100;i++){const r=await s.turn({run:s.run,kind:'poll'});if(r.done)return r;}throw Error('stalled');} +const result=(f,s)=>f.w.pair(f.w.pair(s.core.result.value)[1])[0]; +test('public framing is bounded and leaves action opaque; unknown versions never trial-execute',()=>{ + const f=fixture(),poison=f.g.app(f.g.leaf,f.g.leaf),original=f.t.observe;f.t.observe=h=>{if(h===poison)throw Error('payload forced');return original(h);}; + for(const version of [2])assert.deepEqual(envelope(f.t,f.t.fork(f.t.text('tricuIO'),f.t.fork(f.n(version),poison))),{version,action:poison}); + const padded=f.t.fork(f.t.stem(f.t.leaf),f.t.fork(f.t.leaf,f.t.leaf)); + assert.throws(()=>envelope(f.t,f.t.fork(f.t.text('tricuIO'),f.t.fork(padded,poison))),/version/); + for(const version of [0,1,3,4])assert.throws(()=>envelope(f.t,f.t.fork(f.t.text('tricuIO'),f.t.fork(f.n(version),poison))),/version/); + assert.throws(()=>envelope(f.t,f.t.fork(f.t.text('tricuIOx'),poison)),/sentinel/); +}); +test('browser capabilities and inert inspection agree on exact implemented facilities',async()=>{ + const f=fixture(),s=f.session(f.request('runtime','capabilities'));await finish(s); + const rows=new Map(readList(f.g,result(f,s),20).map(h=>{const [k,v]=readList(f.g,h,2);return [readText(f.g,k),v];})); + assert.equal(readText(f.g,rows.get('backend')),'test-native');assert.equal(readText(f.g,rows.get('authority')),'browser-terminal'); + const groups=readList(f.g,rows.get('groups'),20).map(h=>{const [group,revision,ops]=readList(f.g,h,3);return [readText(f.g,group),readNatural(f.g,revision).toString(),readList(f.g,ops,10).map(h=>readText(f.g,h))];}); + assert.deepEqual(groups,[['runtime','1',['capabilities','inspect']],['standard','1',['input','output','error']],['stream','1',['read','write']],['resource','1',['close']],['utf8','1',['encode','decode']]]); + for(const [group,,ops]of groups)for(const op of ops)assert(available(group,1,op)); + for(const group of ['file','tcp','process','clock','__proto__','constructor'])assert(!available(group,1,'open')); + for(const [group,op,rev,label]of [['stream','write',1,'available'],['stream','write',2,'unsupported'],['file','open',1,'unsupported']]){ + const q=fixture(),poison=q.g.app(q.g.leaf,q.g.leaf),observe=q.t.observe;q.t.observe=h=>{if(h===poison)throw Error('argument forced');return observe(h);}; + const inspect=q.session(q.request('runtime','inspect',q.request(group,op,poison,rev)));await finish(inspect); + const out=readList(q.g,result(q,inspect),5);assert.equal(readText(q.g,out[4]),label);assert(!inspect.core.hasFault); + } +}); +test('public sessions never summarize a completed payload before the selected full codec',async()=>{ + const f=fixture(),payload=f.t.text('λ😀'),s=f.session(f.a(0,payload)); + let rendered=false,closed; + const out=await drive(driver(s,h=>{rendered=true;return view(f.g,h,'string');}),{...host(),v2:{open(){},event(){throw Error('unexpected event');},close(_run,settled){closed=settled;assert(!rendered);}}}); + assert.equal(out.value,'λ😀');assert.equal(closed,true); + const q=fixture(),opaque=q.g.app(q.g.leaf,q.g.leaf),observe=q.t.observe;q.t.observe=h=>{if(h===opaque)throw Error('summary forced payload');return observe(h);}; + const retained=q.session(q.a(0,opaque));assert.equal((await finish(retained)).outcome.value,null); +}); +test('public Stop cancels a pending read and waits for physical acknowledgement',async()=>{ + const f=fixture(),s=f.session(f.request('stream','read',f.t.fork(f.w.marker('io.v2.resource',1),f.n(4)))); + let send,closed=false,cancelled=false,stale; + await assert.rejects(drive(driver(s,()=>{throw Error('cancelled payload viewed');}),{...host(),v2:{ + open(_run,command){send=command;stale=command;}, + event(event){if(event.kind==='read')send({run:'1',kind:'stop'});else{ + assert.equal(event.kind,'cancel');cancelled=true; + setTimeout(()=>{assert(!closed);send({run:'1',kind:'reply',reply:{...event,kind:'cancelled'}});},10); + }},close(_run,settled){assert(cancelled);assert(settled);closed=true;} + }}),/cancelled/); + assert(closed);stale({run:'1',kind:'stop'});assert.equal(s.events.counts.pending,0); +}); +test('step hooks cannot block structured stop, including a never-resolving pause',async()=>{ + const f=fixture(),s=f.session(f.a(0,f.n(42)),1);let send,seen=0; + await assert.rejects(drive(driver(s,()=>{throw Error('unexpected view');}),{...host(),before(){seen++;queueMicrotask(()=>send({run:'1',kind:'stop'}));return new Promise(()=>{});},v2:{stepping:true,open(_r,c){send=c;},event(){throw Error('unexpected event');},close(_r,settled){assert(settled);}}}),/cancelled/); + assert.equal(seen,1); +}); +test('undefined step-hook rejection is preserved after cleanup, and renderer failure occurs after settlement',async()=>{ + for(const phase of ['step','view']){ + const f=fixture(),s=f.session(f.a(0,f.n(42)),1);let closed=false; + try{await drive(driver(s,()=>{if(phase==='view')throw undefined;return {}; }),{...host(),before:async()=>{if(phase==='step')throw undefined;},v2:{stepping:true,open(){},event(){},close(_r,settled){closed=settled;}}});assert.fail('must reject');} + catch(e){assert.equal(e,undefined);assert(closed);} + } +}); + +test('unexpected driver loss is labelled unacknowledged, never a successful cleanup',async()=>{ + let settled,viewed=false,discarded=false; + await assert.rejects(drive({run:'1',discard(){discarded=true;},turn:async()=>{throw Error('runtime lost');},view:async()=>{viewed=true;}},{...host(),v2:{open(){},event(){},close(_r,value){settled=value;}}}),/runtime lost/); + assert.equal(settled,false);assert.equal(viewed,false);assert(discarded); +}); +test('unknown facilities/revisions are ordinary opaque errors; malformed inspection is not execution',async()=>{ + for(const [group,operation,revision]of [['file','open',1],['runtime','capabilities',2]]){ + const f=fixture(),poison=f.g.app(f.g.leaf,f.g.leaf),observe=f.t.observe;f.t.observe=h=>{if(h===poison)throw Error('unknown argument forced');return observe(h);}; + const s=f.session(f.request(group,operation,poison,revision));await finish(s);assert(!s.core.hasFault); + assert.equal(readText(f.g,readList(f.g,result(f,s),4)[0]),'unsupported'); + } + const f=fixture(),s=f.session(f.request('runtime','inspect',f.t.leaf));await finish(s);assert(!s.core.hasFault); + assert.equal(readText(f.g,readList(f.g,result(f,s),4)[0]),'invalid-argument'); +}); + +test('emergency disposal interrupts an idle read without inventing physical acknowledgement',async()=>{ + const f=fixture(),s=f.session(f.request('stream','read',f.t.fork(f.w.marker('io.v2.resource',1),f.n(4)))); + let settled;const lifetime=new AbortController(); + await assert.rejects(drive({...driver(s,()=>{throw Error('unexpected view');}),signal:lifetime.signal},{...host(),v2:{open(){},event(event){assert.equal(event.kind,'read');setTimeout(()=>lifetime.abort(Error('runtime disposed')),10);},close(_r,value){settled=value;}}}),/disposed/); + assert.equal(settled,false);assert.equal(s.events.counts.pending,1,'no fabricated cancelled reply'); +}); + +test('completed runs remove emergency-disposal listeners rather than retaining per-turn history',async()=>{ + const controller=new AbortController(),signal=controller.signal,add=signal.addEventListener.bind(signal),remove=signal.removeEventListener.bind(signal);let listeners=0,peak=0; + signal.addEventListener=(...args)=>{listeners++;peak=Math.max(peak,listeners);return add(...args);};signal.removeEventListener=(...args)=>{listeners--;return remove(...args);}; + for(let run=0;run<5;run++){ + const f=fixture();let action=f.a(0,f.n(42));for(let i=0;i<20;i++)action=f.a(1,f.t.fork(f.a(0),f.t.fork(f.t.leaf,action))); + const s=f.session(action,1);await drive({...driver(s,h=>view(f.g,h,'number')),signal},{...host(),v2:{stepping:true,open(){},event(){},close(){}}});assert.equal(listeners,0); + } + assert.equal(peak,1); +}); diff --git a/ext/demo-site/test/io-v2-resources.test.mjs b/ext/demo-site/test/io-v2-resources.test.mjs @@ -0,0 +1,144 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {lifecycle} from '../dist/io-v2-resources.js'; +import {HostWork} from '../dist/io-v2-host-work.js'; +const ok=value=>({kind:'completed',value}); +const setup=(limits={})=>lifecycle(1n,'owner-stopped','cycle',['stdin','stdout','stderr'],limits); +const create=(s,owner,backend,flags={},kind='file')=>s.resources.publish(s.resources.beginAcquire(owner),kind,backend,flags); +async function finish(s,scope,release=()=>{},outcome=ok(null)){ + s.tasks.bodyDone(scope,outcome);assert(s.tasks.beginRelease(scope)); + const errors=await s.resources.releaseScope(scope,release,error=>error,'fallback');return s.tasks.settle(scope,errors); +} +function retired(s){assert.deepEqual(s.tasks.counts,{scopes:0,tasks:0,hosts:0});assert.deepEqual(s.resources.counts,{resources:0,admissions:0,leases:0});} + +test('admission closure preserves admitted opens and existing descendant borrowing',async()=>{ + const s=setup(),child=s.tasks.spawn(0,true,false),admission=s.resources.beginAcquire(0); + s.tasks.bodyDone(0,ok(null));assert.equal(s.tasks.beginRelease(0),false); + assert.throws(()=>s.resources.beginAcquire(child.key,0),/closed-scope/); + const file=s.resources.publish(admission,'file','backend'),lease=s.resources.beginLease(child.key,file,'file','read'); + assert.equal(lease.backend,'backend');s.tasks.bodyDone(child.key,ok(null));assert.equal(s.tasks.beginRelease(child.key),false); + s.resources.endLease(lease.token);assert(s.tasks.beginRelease(child.key));s.tasks.settle(child.key); + assert(s.tasks.beginRelease(0));assert.throws(()=>s.tasks.settle(0),/busy/); + const released=[];await s.resources.releaseScope(0,r=>released.push(r.backend),e=>e,'fallback');s.tasks.settle(0); + assert.deepEqual(released,['backend']);retired(s); +}); +test('pending ancestor acquisition blocks owner even after acquiring child has stopped',async()=>{ + const s=setup(),child=s.tasks.spawn(0,true,false),admission=s.resources.beginAcquire(child.key,0); + s.tasks.cancel(0,child,'cancel');await finish(s,child.key);s.tasks.bodyDone(0,ok(null)); + assert.equal(s.tasks.beginRelease(0),false);s.resources.abortAcquire(admission); + assert.throws(()=>s.resources.abortAcquire(admission),/invalid-admission/); + assert(s.tasks.beginRelease(0));await s.resources.releaseScope(0,()=>assert.fail('borrowed close'),e=>e,'fallback');s.tasks.settle(0);retired(s); +}); +test('transfer validation is atomic; rekeying invalidates aliases and preserves acquisition order',async()=>{ + const s=setup(),a=create(s,0,'first'),b=create(s,0,'second'); + const before={tasks:s.tasks.counts,resources:s.resources.counts}; + for(const [tokens,message] of [[[a,a],/duplicate/],[[a,{...b,run:2n}],/stale/],[[a,{run:1n,key:1}],/borrowed/]]){ + assert.throws(()=>s.resources.spawn(0,false,true,tokens),message); + assert.deepEqual({tasks:s.tasks.counts,resources:s.resources.counts},before);assert.equal(s.resources.lookup(0,a).backend,'first'); + } + const {task,handles}=s.resources.spawn(0,true,true,[b,a]); + assert.throws(()=>s.resources.lookup(0,a),/stale/);assert.throws(()=>s.resources.lookup(task.key,b),/stale/); + assert.throws(()=>s.resources.lookup(0,handles[0]),/out-of-scope/); + assert.equal(s.resources.lookup(task.key,handles[0]).backend,'second');assert.deepEqual(s.resources.newestOwned(task.key),handles[0]); + const nested=s.tasks.nested(task.key);assert.throws(()=>s.resources.spawn(nested,true,false,[handles[1]]),/not-owner/); + await finish(s,nested);const released=[];await finish(s,task.key,r=>released.push(r.backend)); + assert.deepEqual(released,['second','first']);await finish(s,0);retired(s); +}); +test('failed task admission cannot move resources, start a child or consume resource IDs',async()=>{ + const s=setup({taskIdentities:1}),file=create(s,0,'owner'),only=s.tasks.spawn(0,true,false);await finish(s,only.key); + const before=s.resources.counts; + assert.throws(()=>s.resources.spawn(0,true,true,[file]),/scope-identity-exhausted/); + assert.equal(s.resources.lookup(0,file).owner,0);assert.deepEqual(s.resources.counts,before); + assert.deepEqual(s.tasks.counts,{scopes:1,tasks:0,hosts:0}); + const next=create(s,0,'next');assert.equal(next.key,file.key+2,'failed spawn consumed resource identities'); + await finish(s,0);retired(s); +}); +test('direction leases, wrong-kind rejection and exactly-once close acknowledgements',async()=>{ + const s=setup(),file=create(s,0,'duplex',{duplex:true}); + assert.throws(()=>s.resources.beginLease(0,file,'reader','read'),/wrong-kind/);assert.equal(s.resources.counts.leases,0); + const r=s.resources.beginLease(0,file,'file','read'),w=s.resources.beginLease(0,file,'file','write'); + assert.throws(()=>s.resources.beginLease(0,file,'file','read'),/busy/);assert.throws(()=>s.resources.spawn(0,true,false,[file]),/busy/); + assert.equal(s.resources.claimClose(0,file),null);assert.throws(()=>s.resources.beginLease(0,file,'file','read'),/closed-handle/); + s.resources.endLease(r.token);assert.throws(()=>s.resources.endLease(r.token),/invalid-lease/);s.resources.endLease(w.token); + assert(s.resources.claimClose(0,file));assert.equal(s.resources.claimClose(0,file),null); + s.tasks.bodyDone(0,ok(null));assert(s.tasks.beginRelease(0));assert.throws(()=>s.tasks.settle(0),/busy/); + s.resources.finishClose(file);assert.throws(()=>s.resources.finishClose(file),/stale-handle/); + await s.resources.releaseScope(0,()=>{},e=>e,'fallback');s.tasks.settle(0);retired(s); +}); +test('ordinary single-cursor resources, internal resources and borrowed standards stay distinct',async()=>{ + const s=setup(),file=create(s,0,'file'),lease=s.resources.beginLease(0,file,'file','read'); + assert.throws(()=>s.resources.beginLease(0,file,'file','write'),/busy/);s.resources.endLease(lease.token); + const hidden=create(s,0,'hidden',{internal:true}); + assert.throws(()=>s.resources.beginLease(0,hidden,'file','read'),/internal-resource/); + assert.throws(()=>s.resources.claimClose(0,hidden),/internal-resource/); + assert.throws(()=>s.resources.spawn(0,true,false,[hidden]),/internal-resource/); + assert.throws(()=>s.resources.claimClose(0,{run:1n,key:2}),/borrowed-resource/); + const internal=s.resources.beginLease(0,hidden,'file','exclusive',true);s.resources.endLease(internal.token); + const view=s.resources.lookup(0,file);view.owner=999;assert.equal(s.resources.lookup(0,file).owner,0); + assert.throws(()=>{view.flags.borrowed=true;},TypeError); + await finish(s,0);retired(s); +}); +test('reverse publication order, not open-start order, determines cleanup',async()=>{ + const s=setup(),first=s.resources.beginAcquire(0),second=s.resources.beginAcquire(0); + s.resources.publish(second,'file','published first');s.resources.publish(first,'file','published second'); + const order=[];await finish(s,0,r=>order.push(r.backend));assert.deepEqual(order,['published second','published first']);retired(s); +}); +test('publication identity exhaustion leaves admission live for raw release/abort; cleanup needs no IDs',async()=>{ + const s=setup({resourceIdentities:4}),admission=s.resources.beginAcquire(0); + assert.throws(()=>s.resources.publish(admission,'file','raw backend'),/identity-exhausted/); + assert.deepEqual(s.resources.counts,{resources:3,admissions:1,leases:0}); + assert.throws(()=>s.resources.beginAcquire(0),/identity-exhausted/); + assert.throws(()=>s.resources.beginLease(0,{run:1n,key:1},'reader','read'),/identity-exhausted/); + s.resources.abortAcquire(admission);await finish(s,0);retired(s); +}); +test('physical cleanup attempts every provider once and diagnostics failure cannot skip releases',async()=>{ + const s=setup(),order=[];let classified=0;for(let i=0;i<20;i++)create(s,0,i); + s.tasks.bodyDone(0,{kind:'failed',value:'primary'});assert(s.tasks.beginRelease(0)); + const errors=await s.resources.releaseScope(0,r=>{order.push(r.backend);throw 'release failed';},()=>{classified++;throw 'classifier failed';},'fallback'); + assert.deepEqual(order,Array.from({length:20},(_,i)=>19-i));assert.equal(errors.items.length,16);assert.equal(errors.omitted,4);assert.equal(classified,16); + const out=s.tasks.settle(0,errors);assert.equal(out.value,'primary');assert.deepEqual(out.cleanup,errors);retired(s); +}); +test('asynchronous release stays registered until provider acknowledgement, even on failure',async()=>{ + const s=setup(),work=new HostWork(s.tasks),file=create(s,0,'file');let acknowledge; + s.tasks.bodyDone(0,ok(null));assert(s.tasks.beginRelease(0)); + const pending=new Promise(resolve=>{acknowledge=resolve;}); + work.release(0,()=>s.resources.releaseScope(0,async()=>{await pending;throw 'close failed';},e=>e,'fallback')); + assert(s.resources.lookup(0,file).releasing);assert.throws(()=>s.tasks.settle(0),/invalid-transition/); + const wait=work.wait();acknowledge();await wait;const reply=work.take();assert(reply.outcome.ok); + assert.throws(()=>s.resources.lookup(0,file),/stale-handle/); + const out=s.tasks.settle(0,reply.outcome.value);assert.equal(out.kind,'failed');assert.equal(out.value,'close failed');retired(s); +}); +test('repeated scoped resources and leases retain no registry history',async()=>{ + const s=setup();for(let i=0;i<1000;i++){ + const child=s.resources.spawn(0,true,false,[]).task,file=create(s,child.key,i),lease=s.resources.beginLease(child.key,file,'file','exclusive'); + s.resources.endLease(lease.token);await finish(s,child.key); + assert.deepEqual(s.resources.counts,{resources:3,admissions:0,leases:0});assert.deepEqual(s.tasks.counts,{scopes:1,tasks:0,hosts:0}); + } + await finish(s,0);retired(s); +}); +test('explicit close cancels a leased operation, not its task, and waits for lease acknowledgement',async()=>{ + const s=setup(),work=new HostWork(s.tasks),file=create(s,0,'reader',{},'reader'),child=s.tasks.spawn(0,true,false);let acknowledge; + const drained=new Promise(resolve=>{acknowledge=resolve;}); + const read=work.start(child.key,async signal=>{ + const lease=s.resources.beginLease(child.key,file,'reader','read'); + try {await new Promise(resolve=>signal.addEventListener('abort',resolve,{once:true}));await drained;throw 'resource closed';} + finally{s.resources.endLease(lease.token);} + }); + assert.equal(s.resources.claimClose(0,file),null); + assert.equal(work.cancelBody({...read,run:2n}),false);assert.equal(work.cancelBody(read),true);assert.equal(work.cancelBody(read),false); + assert.equal(s.resources.claimClose(0,file),null);assert.equal(s.tasks.state(child.key).reason,null); + const waiting=work.wait();acknowledge();await waiting;const reply=work.take(); + assert.equal(reply.outcome.ok,false);assert.equal(reply.outcome.error,'resource closed');assert.equal(s.tasks.state(child.key).reason,null); + let physical=0;work.start(0,()=>{assert(s.resources.claimClose(0,file));physical++;s.resources.finishClose(file);}); + await work.wait();assert(work.take().outcome.ok);assert.equal(physical,1); + await finish(s,child.key);await finish(s,0);retired(s); +}); + +test('resource exhaustion and oversize transfers do not admit tasks or invalidate aliases',async()=>{ + const s=setup({resourceIdentities:5}),file=create(s,0,'file'); + for(const [tokens,error] of [[[file],/identity-exhausted/],[Array(257).fill(file),/transfer-limit/]]){ + assert.throws(()=>s.resources.spawn(0,true,false,tokens),error); + assert.equal(s.resources.lookup(0,file).owner,0);assert.deepEqual(s.tasks.counts,{scopes:1,tasks:0,hosts:0}); + } + await finish(s,0);retired(s); +}); diff --git a/ext/demo-site/test/io-v2-session-probe.mjs b/ext/demo-site/test/io-v2-session-probe.mjs @@ -0,0 +1,57 @@ +// Native tree sibling cancellation through cloneable events, with a real DOM +// provider. No raw handles leave the worker/session boundary. +export async function run(_control,backend){ + const base=new URL('../',document.querySelector('script[type=module][src]').src),fixture=(await(await fetch('/zig-negative.json')).json())['v2-session-streams']; + const assert=(ok,message)=>{if(!ok)throw Error(message);}; + async function exercise(start,turn,stop){ + let report=await start();const events=[]; + for(let i=0;i<30&&!events.some(e=>e.kind==='write');i++){events.push(...report.events);if(!events.some(e=>e.kind==='write'))report=await turn({run:report.run,kind:'poll'});} + const read=events.find(e=>e.kind==='read'),write=events.find(e=>e.kind==='write');assert(read&&write,'pending read blocked sibling write'); + const input=document.createElement('input');document.body.append(input);let hits=0; + const listener=()=>{hits++;};input.addEventListener('input',listener); + try{ + if(stop){ + report=await turn({run:report.run,kind:'stop'}); + assert(!report.done&&report.events.filter(e=>e.kind==='cancel').length===2,'stop did not cancel both providers'); + input.removeEventListener('input',listener);input.remove(); + report=await turn({run:report.run,kind:'reply',reply:{...read,kind:'cancelled'}}); + assert(!report.done,'stop forgot pending output acknowledgement'); + report=await turn({run:report.run,kind:'reply',reply:{...write,kind:'cancelled'}}); + for(let i=0;i<30&&!report.done;i++)report=await turn({run:report.run,kind:'poll'}); + assert(report.done&&report.outcome.kind==='cancelled','structured session stop did not settle'); + assert(Object.values(report.counts).every(group=>Object.values(group).every(value=>value===0)),'stop retained lifecycle state'); + return; + } + let rejected=false;try{await turn({run:'999',kind:'stop'});}catch{rejected=true;}assert(rejected,'wrong run accepted'); + report=await turn({run:report.run,kind:'reply',reply:{...read,run:'999',bytes:[1],eof:false}});assert(report.accepted===false,'wrong reply consumed wait'); + assert(write.bytes.length===1&&write.bytes[0]===65,'write payload mismatch'); + report=await turn({run:report.run,kind:'reply',reply:{run:write.run,id:write.id,kind:'write',count:1}}); + let cancel=report.events.find(e=>e.kind==='cancel'); + for(let i=0;i<30&&!cancel;i++){report=await turn({run:report.run,kind:'poll'});cancel=report.events.find(e=>e.kind==='cancel');} + assert(cancel?.id===read.id,'missing read cancellation');assert(!report.done&&report.counts.events.pending===1,'abort delivery settled provider'); + input.removeEventListener('input',listener);input.dispatchEvent(new Event('input'));assert(hits===0,'listener survived cancellation'); + report=await turn({run:report.run,kind:'poll'});assert(!report.done&&input.isConnected,'unacknowledged cleanup retired run'); + input.remove();report=await turn({run:report.run,kind:'reply',reply:{...cancel,kind:'cancelled'}}); + for(let i=0;i<30&&!report.done;i++)report=await turn({run:report.run,kind:'poll'}); + assert(report.done&&report.outcome.kind==='completed'&&report.outcome.value==='42','tree run result mismatch'); + assert(Object.values(report.counts).every(group=>Object.values(group).every(value=>value===0)),'historical transport/lifecycle state retained'); + }finally{input.removeEventListener('input',listener);input.remove();} + } + for(const stop of [false,true]){ + if(backend==='haskell-wasm'){ + const {CompilerWorker}=await import(new URL('runtime/haskell/client.mjs',base)),client=new CompilerWorker(); + try{await client.request({type:'init',compiler:false});await client.request({type:'load',bytes:new Uint8Array(fixture.bytes).buffer,hash:fixture.hash}); + await exercise(async()=>JSON.parse((await client.request({type:'io-v2-session-start',name:'root',arguments:[]})).text),async command=>JSON.parse((await client.request({type:'io-v2-session-turn',...command})).text),stop); + }finally{client.dispose();} + }else{ + const {WasmRuntime}=await import('/io-v2-runtime.mjs'),{ioV2Private,ioV2Session}=await import(new URL('dist/zig-helpers.js',base)); + const g=await WasmRuntime.create(new Uint8Array(await(await fetch(new URL('runtime/zig/arboricx.wasm',base))).arrayBuffer())); + try{await g.scopeAsync(async()=>{ + const action=g.load(new Uint8Array(fixture.bytes),'root'),tree=ioV2Private.constructors({leaf:g.leaf,observe:g.observe.bind(g),stem:g.stem.bind(g),fork:g.fork.bind(g),app:g.app.bind(g)}); + const session=new ioV2Session.Session(tree,action,'1',roots=>g.sweepScope(roots)); + await exercise(()=>session.turn({run:'1',kind:'poll'}),command=>session.turn(command),stop); + });}finally{g.dispose();} + } + } + return {backend,checks:['native tree read permits sibling write','wrong-run stop and reply reject','tree cancellation waits for DOM cleanup acknowledgement','complete42; no retained task/resource/event records','external structured stop awaits both provider acknowledgements']}; +} diff --git a/ext/demo-site/test/io-v2-session.test.mjs b/ext/demo-site/test/io-v2-session.test.mjs @@ -0,0 +1,73 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Graph} from '../dist/graph.js'; +import {readText,readNatural,readList} from '../dist/codecs.js'; +import {constructors} from '../dist/io-v2-private.js'; +import {Wire} from '../dist/io-v2-core.js'; +import {Session} from '../dist/io-v2-session.js'; +function fixture(budget={}){ + const g=new Graph(budget),t=constructors({leaf:g.leaf,observe:g.observe.bind(g),stem:g.stem.bind(g),fork:g.fork.bind(g),app:g.app.bind(g)}),w=new Wire(t,1n); + const n=x=>t.natural(BigInt(x)),a=(tag,p=t.leaf)=>t.fork(n(tag),p),request=(group,op,p=t.leaf)=>a(10,t.fork(t.text(group),t.fork(n(1),t.fork(t.text(op),p)))); + const ref=key=>w.marker('io.v2.resource',key),read=request('stream','read',t.fork(ref(1),n(4))),write=bytes=>request('stream','write',t.fork(ref(2),w.list(bytes.map(n)))); + const seq=(left,right)=>a(1,t.fork(left,t.fork(t.leaf,right))),session=action=>new Session(t,action,'1',roots=>g.collect([...roots])); + return {g,t,w,n,a,request,ref,read,write,seq,session}; +} +async function poll(s,predicate){for(let i=0;i<100;i++){const r=await s.turn({run:'1',kind:'poll'});if(predicate(r))return r;}throw Error('poll did not progress');} +const value=(f,s)=>f.w.pair(f.w.pair(s.core.result.value)[1])[0]; +test('stream read uses the independent native data-pair encoding, not a nested tuple',async()=>{ + const f=fixture(),s=f.session(f.read),r=await poll(s,r=>r.events.length);const event=r.events[0]; + await s.turn({run:'1',kind:'reply',reply:{...event,bytes:[65,0,255],eof:false}});if(!s.core.result)await poll(s,r=>r.done); + const [tag,bytes]=f.w.pair(value(f,s));assert.equal(readText(f.g,tag),'data');assert.deepEqual(readList(f.g,bytes,3).map(h=>readNatural(f.g,h)),[65n,0n,255n]); +}); +test('EOF is sticky and standard handles are borrowed, not physically closed',async()=>{ + const f=fixture(),s=f.session(f.seq(f.read,f.read)),r=await poll(s,r=>r.events.length); + let out=await s.turn({run:'1',kind:'reply',reply:{...r.events[0],bytes:[],eof:true}}); + if(!out.done)out=await poll(s,r=>{assert.equal(r.events.length,0);return r.done;}); + assert.equal(readText(f.g,readList(f.g,value(f,s),1)[0]),'eof'); + const other=fixture(),close=other.session(other.request('resource','close',other.ref(2)));await poll(close,r=>r.done); + assert.equal(readText(other.g,readList(other.g,value(other,close),4)[0]),'borrowed-resource'); +}); +test('structured session stop waits for provider acknowledgement and refuses reuse',async()=>{ + const f=fixture(),s=f.session(f.read),r=await poll(s,r=>r.events.length),event=r.events[0]; + await assert.rejects(s.turn({run:'2',kind:'stop'}),/stale/); + const stopped=await s.turn({run:'1',kind:'stop'});assert(!stopped.done);assert.equal(stopped.events[0].kind,'cancel'); + let done=await s.turn({run:'1',kind:'reply',reply:{...event,kind:'cancelled'}});if(!done.done)done=await poll(s,r=>r.done); + assert.equal(done.outcome.kind,'cancelled');assert(Object.values(done.counts).every(c=>Object.values(c).every(n=>n===0))); + await assert.rejects(s.turn({run:'1',kind:'poll'}),/stale/); +}); +test('failed output poisons the slot before demanding any later byte payload',async()=>{ + const f=fixture(),poison=f.g.app(f.g.leaf,f.g.leaf),observe=f.t.observe; + f.t.observe=h=>{if(h===poison)throw Error('forced poisoned write data');return observe(h);}; + const later=f.request('stream','write',f.t.fork(f.ref(2),poison)),s=f.session(f.seq(f.write([65]),later)),r=await poll(s,r=>r.events.length); + await s.turn({run:'1',kind:'reply',reply:{...r.events[0],kind:'error'}});if(!s.core.result)await poll(s,r=>r.done); + assert.equal(s.core.hasFault,false);const error=readList(f.g,value(f,s),4);assert.equal(readText(f.g,error[0]),'invalid-state');assert.equal(readText(f.g,error[3]),'unknown'); +}); +test('attempted output has an aggregate bound and never silently retries partial writes',async()=>{ + const f=fixture({fuel:20000000});let action=f.write([1]);for(let i=0;i<4;i++)action=f.seq(f.write(Array(4096).fill(65)),action); + const s=f.session(action);let events=0; + for(let i=0;i<100&&!s.core.result;i++){ + const r=await s.turn({run:'1',kind:'poll'}); + for(const event of r.events){events++;await s.turn({run:'1',kind:'reply',reply:{...event,kind:'write',count:1}});} + } + assert(s.core.result);assert.equal(events,4);assert.equal(readText(f.g,readList(f.g,value(f,s),4)[0]),'invalid-bound'); +}); +test('named UTF8 facilities preserve nonfinal carry and generic BOM text',async()=>{ + const f=fixture(),bytes=[0xef,0xbb,0xbf,65,0xf0,0x9f],s=f.session(f.request('utf8','decode',f.w.list([f.n(0),f.w.list(bytes.map(f.n))]))); + await poll(s,r=>r.done);const [text,carry]=readList(f.g,value(f,s),2); + assert.equal(readText(f.g,text),'\ufeffA');assert.deepEqual(readList(f.g,carry,2).map(h=>readNatural(f.g,h)),[240n,159n]); + const other=fixture(),encoded=other.session(other.request('utf8','encode',other.t.text('λ😀')));await poll(encoded,r=>r.done); + assert.deepEqual(readList(other.g,value(other,encoded),6).map(h=>Number(readNatural(other.g,h))),[206,187,240,159,152,128]); +}); +test('named UTF8 rejects excess heads/tails without demand and malformed scalar values as ordinary errors',async()=>{ + for(const [op,limit]of [['encode',1024],['decode',4096]]){ + const f=fixture({fuel:20000000}),poison=f.g.app(f.g.leaf,f.g.leaf),observe=f.t.observe; + f.t.observe=h=>{if(h===poison)throw Error('forced excess payload');return observe(h);}; + let list=f.t.fork(poison,poison);for(let i=0;i<limit;i++)list=f.t.fork(f.n(65),list); + const s=f.session(f.request('utf8',op,op==='encode'?list:f.w.list([f.n(0),list])));await poll(s,r=>r.done); + assert(!s.core.hasFault);assert.equal(readText(f.g,readList(f.g,value(f,s),4)[0]),'invalid-argument'); + } + for(const scalar of [0xd800,0x110000]){ + const f=fixture(),s=f.session(f.request('utf8','encode',f.w.list([f.n(scalar)])));await poll(s,r=>r.done); + assert.equal(readText(f.g,readList(f.g,value(f,s),4)[0]),'invalid-argument'); + } +}); diff --git a/ext/demo-site/test/io-v2-source-probe.mjs b/ext/demo-site/test/io-v2-source-probe.mjs @@ -0,0 +1,116 @@ +// Real compiled source libraries, native evaluators and the private event session. +export async function run(_control,backend,terminalMode=false){ + const base=new URL('../',document.querySelector('script[type=module][src]').src),fixture=await(await fetch('/io-v2-source.json')).json(),checks=[]; + const input=[...new TextEncoder().encode('\ufeffA😀λe\u0301\r\n\0')]; + async function drive(name,start,turn){ + if(terminalMode)return driveTerminal(name,start,turn); + let report=await start(),queue=[...report.events],offset=0,pendingRead=null,cancelled=false;const output=[]; + for(let i=0;i<2000&&!report.done;i++){ + if(!queue.length){report=await turn({run:report.run,kind:'poll'});queue.push(...report.events);continue;} + const event=queue.shift();let reply; + if(event.kind==='read'){ + if(name==='cancellation'){pendingRead=event.id;continue;} + if(name!=='roundTrip')throw Error('Unexpected read'); + const bytes=input.slice(offset,offset+Math.min(event.limit,1));offset+=bytes.length; + reply={...event,bytes,eof:bytes.length===0}; + }else if(event.kind==='write'){ + if(name==='cancellation'&&pendingRead===null)throw Error('Write ran without the expected pending reader'); + // Deliberate positive partial progress: the SOURCE writeAll must advance + // exactly once, including through a split multi-byte scalar. + output.push(event.bytes[0]);reply={...event,count:1}; + }else{ + if(event.id!==pendingRead||cancelled)throw Error('Unexpected cancellation'); + cancelled=true;reply={...event,kind:'cancelled'}; + } + report=await turn({run:report.run,kind:'reply',reply});if(report.accepted!==true)throw Error('Provider reply rejected');queue.push(...report.events); + } + const expected=name.startsWith('invalid')?[]:name==='roundTrip'?input:[...new TextEncoder().encode(name==='cancellation'?'ready':'λ😀e\u0301\r\n')]; + if(!report.done||report.outcome.kind!=='completed'||report.outcome.value!=='42'||report.outcome.fault)throw Error(name+': '+JSON.stringify(report)); + if(JSON.stringify(output)!==JSON.stringify(expected))throw Error(name+' output byte mismatch: '+JSON.stringify(output)); + if(name==='cancellation'&&!cancelled)throw Error('Missing source cancellation'); + if(!Object.values(report.counts).every(group=>Object.values(group).every(v=>v===0)))throw Error('Retained run records'); + checks.push(name+': full bytes/result42; split UTF8 and source partial-progress handling'); + } + async function driveTerminal(name,start,turn){ + const {ioV2TerminalView:{terminalView}}=await import(new URL('dist/probe-api.js',base)); + let report=await start(),stop=false,sent=false,aborted=false;const replies=[],parent=document.createElement('div');document.body.append(parent); + const widget=terminalView(parent,report.run,r=>replies.push(r),()=>{stop=true;}); + try{ + for(let i=0;i<2000&&!report.done;i++){ + for(const event of report.events){ + const old=parent.querySelector('form'),oldEOF=old?.querySelector('button[type=button]'); + widget.handle(event); + if(event.kind==='cancel'){ + aborted=true;if(!parent.querySelector('form').hidden)throw Error('Input still visible at cancellation acknowledgement'); + const count=replies.length;old.dispatchEvent(new Event('submit',{cancelable:true}));oldEOF?.click(); + if(replies.length!==count)throw Error('Stale DOM prompt emitted reply'); + } + if(event.kind==='read'&&name==='roundTrip'){ + const form=parent.querySelector('form'); + if(!sent){form.querySelector('input').value='\ufeffA😀λe\u0301\0';form.dispatchEvent(new Event('submit',{cancelable:true}));sent=true;} + else form.querySelector('button[type=button]').click(); + } + if(event.kind==='read'&&name==='externalStop')parent.querySelector('section > button').click(); + } + const command=stop?{kind:'stop'}:replies.length?{kind:'reply',reply:replies.shift()}:{kind:'poll'};stop=false; + report=await turn({run:report.run,...command}); + if(command.kind==='reply'&&report.accepted!==true)throw Error('UI completion rejected'); + } + if(!report.done||report.outcome.kind!==(name==='externalStop'?'cancelled':'completed')||report.outcome.fault)throw Error('UI run: '+JSON.stringify(report)); + if(name!=='externalStop'&&report.outcome.value!=='42')throw Error('UI result mismatch'); + widget.finish();const snapshot=widget.snapshot(); + const expected=name==='binary'?[92,120,102,102,255,240,159]:name==='roundTrip'?[...new TextEncoder().encode('\ufeffA😀λe\u0301\0\n')]:name.startsWith('invalid')||name==='externalStop'?[]:[...new TextEncoder().encode(name==='cancellation'?'ready':'λ😀e\u0301\r\n')]; + if(JSON.stringify(snapshot.output.bytes)!==JSON.stringify(expected))throw Error('UI output mismatch '+name); + if(parent.querySelector('[data-hex=output]').textContent!==expected.map(b=>b.toString(16).padStart(2,'0')).join(' '))throw Error('Wrong hex view'); + if(name==='binary'){ + const text=parent.querySelector('[data-stream=output]'); + if(text.firstChild.nodeType!==Node.TEXT_NODE||text.firstChild.textContent!=='\\xff'||text.querySelectorAll('mark').length!==2||!text.querySelector('[data-byte-reason=incomplete]'))throw Error('Byte escapes indistinguishable from literal text'); + } + if(['externalStop','cancellation'].includes(name)&&!aborted)throw Error('No UI cancellation'); + if(!Object.values(report.counts).every(group=>Object.values(group).every(v=>v===0)))throw Error('UI run retained records'); + if(replies.length||parent.querySelector('form'))throw Error('UI not retired'); + checks.push('terminal '+name+': exact bytes, DOM lifecycle and outcome'); + }finally{parent.remove();} + } + if(terminalMode){ + const {ioV2TerminalView:{terminalView}}=await import(new URL('dist/probe-api.js',base)); + const parent=document.createElement('div'),replies=[];document.body.append(parent);let stops=0; + const widget=terminalView(parent,'900',r=>replies.push(r),()=>{stops++;}); + try{ + widget.handle({run:'900',id:1,kind:'read',limit:4096});const stale=parent.querySelector('form'),staleEOF=stale.querySelector('button[type=button]'); + widget.handle({run:'900',id:1,kind:'cancel',operation:'read'});widget.handle({run:'900',id:2,kind:'read',limit:4096}); + stale.dispatchEvent(new Event('submit',{cancelable:true}));staleEOF.click(); + if(replies.length!==1||parent.querySelector('form').hidden)throw Error('Old prompt consumed successor'); + const form=parent.querySelector('form');form.querySelector('input').value='\ud800';form.dispatchEvent(new Event('submit',{cancelable:true})); + if(replies.length!==1||form.hidden)throw Error('Invalid line consumed wait'); + form.querySelector('input').value='';form.dispatchEvent(new Event('submit',{cancelable:true})); + if(replies.at(-1).eof||JSON.stringify(replies.at(-1).bytes)!=='[10]')throw Error('Empty line conflated with EOF'); + const text='<script>throw Error("injected")</script><img src=x onerror=alert(1)>'; + widget.handle({run:'900',id:3,kind:'write',stream:'error',bytes:[...new TextEncoder().encode(text)]}); + if(parent.querySelector('script,img')||parent.querySelector('[data-stream=error]').textContent!==text)throw Error('Output interpreted as markup'); + const button=parent.querySelector('section > button');button.click();button.click();if(stops!==1)throw Error('Repeated stop'); + widget.finish();checks.push('terminal DOM: stale prompt isolation, malformed/empty line, inert markup, once-only stop'); + }finally{parent.remove();} + } + const names=terminalMode?['direct','handled','roundTrip','cancellation','invalidUTF8','invalidScalar','binary','externalStop']:['direct','handled','roundTrip','cancellation','invalidUTF8','invalidScalar']; + if(backend==='haskell-wasm'){ + const {CompilerWorker}=await import(new URL('runtime/haskell/client.mjs',base)),client=new CompilerWorker(); + try{ + await client.request({type:'init',compiler:false});await client.request({type:'load',bytes:new Uint8Array(fixture.bytes).buffer,hash:fixture.hash}); + for(const name of names)await drive(name, + async()=>JSON.parse((await client.request({type:'io-v2-session-start',name:name==='externalStop'?'roundTrip':name,arguments:[]})).text), + async command=>JSON.parse((await client.request({type:'io-v2-session-turn',...command})).text)); + }finally{client.dispose();} + }else{ + const {WasmRuntime}=await import('/io-v2-runtime.mjs'),{ioV2Private,ioV2Session}=await import(new URL('dist/zig-helpers.js',base)); + const g=await WasmRuntime.create(new Uint8Array(await(await fetch(new URL('runtime/zig/arboricx.wasm',base))).arrayBuffer()));let run=0n; + try{ + for(const name of names)await g.scopeAsync(async()=>{ + const action=g.load(new Uint8Array(fixture.bytes),name==='externalStop'?'roundTrip':name),tree=ioV2Private.constructors({leaf:g.leaf,observe:g.observe.bind(g),stem:g.stem.bind(g),fork:g.fork.bind(g),app:g.app.bind(g)}); + const session=new ioV2Session.Session(tree,action,String(++run),roots=>g.sweepScope(roots)); + await drive(name,()=>session.turn({run:session.run,kind:'poll'}),command=>session.turn(command)); + }); + }finally{g.dispose();} + } + return {backend,checks}; +} diff --git a/ext/demo-site/test/io-v2-tasks.test.mjs b/ext/demo-site/test/io-v2-tasks.test.mjs @@ -0,0 +1,132 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Tasks} from '../dist/io-v2-tasks.js'; +const ok=value=>({kind:'completed',value}),failed=value=>({kind:'failed',value}),cancelled=value=>({kind:'cancelled',value}); +const diagnostics=items=>({items,omitted:0}); +const fresh=(run=1n,limit)=>new Tasks(run,'owner-stopped','join-cycle',limit); +function finish(s,id,outcome=ok(null),cleanup){s.bodyDone(id,outcome);assert.equal(s.beginRelease(id),true);return s.settle(id,cleanup);} +function retired(s){assert.deepEqual(s.counts,{scopes:0,tasks:0,hosts:0});assert.deepEqual([...s.payloads()],['owner-stopped','join-cycle']);} + +test('normal exit closes admission but waits for children; ordinary errors remain values',()=>{ + const s=fresh(),child=s.spawn(0,false,false),ordinary={err:'domain error'}; + s.bodyDone(0,ok(ordinary));assert.equal(s.beginRelease(0),false); + assert.throws(()=>s.spawn(0,false,false),/invalid-transition/); + const grandchild=s.spawn(child.key,false,false); // existing child remains live during ancestor join + finish(s,grandchild.key);finish(s,child.key); + assert.equal(s.beginRelease(0),true);const out=s.settle(0); + assert.equal(out.kind,'completed');assert.equal(out.value,ordinary);retired(s); +}); +test('normal joining remains cancellable and the first reason is sticky',()=>{ + const s=fresh(),child=s.spawn(0,false,false); + s.bodyDone(0,ok('return'));s.stop(0,cancelled('stop'));s.stop(0,failed('late')); + assert.deepEqual(s.state(child.key).reason,cancelled('owner-stopped')); + finish(s,child.key,failed('late child')); + assert(s.beginRelease(0));assert.equal(s.settle(0).value,'stop');retired(s); +}); +test('supervision contains failure; unsupervised failure stops siblings immediately',()=>{ + const s=fresh(),contained=s.spawn(0,true,true); + finish(s,contained.key,failed('contained'));assert.equal(s.state(0).reason,null); + s.claimJoin(0,contained);assert.equal(s.pollJoin(0).value,'contained'); + const a=s.spawn(0,false,true),b=s.spawn(0,false,true); + s.stop(a.key,failed('first'));s.stop(b.key,failed('second')); + assert.deepEqual(s.state(0).reason,failed('first')); + assert.deepEqual(s.state(b.key).reason,cancelled('owner-stopped')); + finish(s,a.key);finish(s,b.key);assert.equal(finish(s,0).value,'first');retired(s); +}); +test('joins consume before waiting; abandoned joins never restore tickets or retain results',()=>{ + const s=fresh(),child=s.spawn(0,true,true),waiter=s.spawn(0,true,true); + s.claimJoin(waiter.key,child);assert.equal(s.pollJoin(waiter.key),null); + assert.throws(()=>s.discard(0,child),/stale-task/); + s.cancel(0,waiter,'cancel wait');finish(s,waiter.key); + const result={unique:'unclaimed'};finish(s,child.key,ok(result)); + assert(![...s.payloads()].includes(result)); + s.claimJoin(0,waiter);assert.equal(s.pollJoin(0).value,'cancel wait'); + assert.throws(()=>s.claimJoin(0,waiter),/stale-task/); + finish(s,0);retired(s); +}); +test('explicit and implicit cycles use task identities across lexical scopes',()=>{ + const s=fresh(),a=s.spawn(0,true,true);let b=s.spawn(0,true,true); + const inner=s.nested(a.key); + assert.throws(()=>s.claimJoin(inner,a),/join-cycle/); + s.claimJoin(inner,b);assert.throws(()=>s.claimJoin(b.key,a),/join-cycle/); + s.abandonJoin(inner);finish(s,b.key);b=s.spawn(0,true,true); + const c=s.spawn(inner,true,true); + s.claimJoin(b.key,a);s.claimJoin(c.key,b);s.bodyDone(inner,ok(null)); + assert.equal(s.state(inner).reason.value,'join-cycle');assert.equal(s.state(a.key).reason.value,'join-cycle'); + finish(s,c.key);assert(s.beginRelease(inner));s.settle(inner);finish(s,a.key); + assert.equal(s.pollJoin(b.key).value,'join-cycle');finish(s,b.key);finish(s,0);retired(s); +}); +test('out-of-scope, cross-run and completed task cancellation reject without consumption',()=>{ + const s=fresh(),a=s.spawn(0,true,true),b=s.spawn(0,true,true),owned=s.spawn(a.key,true,true); + assert.throws(()=>s.claimJoin(b.key,owned),/out-of-scope/); + assert.throws(()=>s.cancel(0,{...a,run:2n},'bad'),/stale-task/); + finish(s,owned.key,ok('child'));s.claimJoin(a.key,owned);assert.equal(s.pollJoin(a.key).value,'child'); + finish(s,a.key);assert.throws(()=>s.cancel(0,a,'late'),/stale-task/);s.discard(0,a); + finish(s,b.key);finish(s,0);retired(s); +}); +test('cleanup defeats success, preserves the primary and propagates bounded diagnostics once',()=>{ + const s=fresh(),inner=s.nested(0),child=s.spawn(inner,false,false); + assert.equal(finish(s,child.key,ok(null),diagnostics([17])).value,17); + const lexical=finish(s,inner,ok(null),diagnostics(Array.from({length:25},(_,i)=>100+i))); + assert.equal(lexical.cleanup,undefined); + const out=finish(s,0,failed(999),diagnostics([200])); + assert.equal(out.kind,'failed');assert.equal(out.value,17); + assert.deepEqual(out.cleanup,{items:Array.from({length:16},(_,i)=>100+i),omitted:10});retired(s); +}); +test('pending host cancellation is delivered once but release requires acknowledgement',async()=>{ + const s=fresh(),reader=s.spawn(0,true,true),read=s.beginHost(reader.key),controller=new AbortController(),events=[]; + let acknowledge; + const physicalWork=new Promise(resolve=>{acknowledge=resolve;}); + controller.signal.addEventListener('abort',()=>events.push('abort-requested'),{once:true}); + s.cancel(0,reader,'cancel input');s.bodyDone(reader.key,ok('ignored value')); + assert.deepEqual(s.takeCancellations(),[read]);controller.abort(); + assert.deepEqual(s.takeCancellations(),[]);assert.equal(s.beginRelease(reader.key),false); + s.bodyDone(0,ok('parent'));assert.equal(s.beginRelease(0),false); + await Promise.resolve();assert.deepEqual(events,['abort-requested']);assert.equal(s.beginRelease(reader.key),false); + acknowledge();await physicalWork;events.push('physical-work-settled'); + assert.equal(s.endHost(read),'ignored');assert.equal(s.endHost(read),'stale'); + assert(s.beginRelease(reader.key));assert.equal(s.settle(reader.key).kind,'cancelled'); + assert(s.beginRelease(0));s.settle(0);retired(s); +}); +test('wrong/stale host resumes leave the correct wait intact',()=>{ + const s=fresh(),child=s.spawn(0,true,false),host=s.beginHost(child.key); + for(const bad of [{...host,scope:0},{...host,run:2n},{...host,key:host.key+1}])assert.equal(s.endHost(bad),'stale'); + assert.equal(s.state(child.key).hostCalls,1);assert.equal(s.endHost(host),'reply'); + const next=s.beginHost(child.key);assert.notEqual(next.key,host.key);assert.equal(s.endHost(host),'stale'); + assert.equal(s.endHost(next),'reply');finish(s,child.key);finish(s,0);retired(s); +}); +test('all callback completion orders retain cancelled scopes until their own work settles',()=>{ + for(const order of [[0,1,2],[0,2,1],[1,0,2],[1,2,0],[2,0,1],[2,1,0]]){ + const s=fresh(),children=order.map(()=>s.spawn(0,false,false)),hosts=children.map(c=>s.beginHost(c.key)); + s.stop(0,failed('stop'));s.bodyDone(0,ok(null)); + for(const child of children)s.bodyDone(child.key,ok(null)); + assert.equal(s.takeCancellations().length,3);assert.equal(s.takeCancellations().length,0); + for(const index of order){ + assert.equal(s.beginRelease(0),false);assert.equal(s.beginRelease(children[index].key),false); + assert.equal(s.endHost(hosts[index]),'ignored');assert(s.beginRelease(children[index].key));s.settle(children[index].key); + } + assert(s.beginRelease(0));assert.equal(s.settle(0).value,'stop');retired(s); + } +}); +test('nonwrapping identities, scope state copies and immutable diagnostic capture',()=>{ + for(const bad of [0n,-1n,1,2n**64n])assert.throws(()=>fresh(bad),/identity/); + const s=fresh(1n,2),a=s.spawn(0,true,true),copy=s.state(a.key);copy.phase='releasing'; + assert.equal(s.state(a.key).phase,'open'); + const items=['cleanup'];s.bodyDone(a.key,failed('primary'));assert(s.beginRelease(a.key)); + const result=s.settle(a.key,diagnostics(items));items[0]='changed';result.cleanup.items[0]='also changed'; + s.claimJoin(0,a);assert.equal(s.pollJoin(0).cleanup.items[0],'cleanup'); + const b=s.spawn(0,true,false);finish(s,b.key); + assert.throws(()=>s.spawn(0,true,false),/identity-exhausted/);assert.deepEqual(s.counts,{scopes:1,tasks:0,hosts:0}); + const h=s.beginHost(0);assert(Object.isFrozen(h));s.endHost(h);s.endHost(s.beginHost(0)); + assert.throws(()=>s.beginHost(0),/identity-exhausted/);finish(s,0);retired(s); +}); +test('joined, discarded and non-result tasks retain no history or payload roots',()=>{ + const s=fresh(); + for(let i=0;i<2000;i++){ + const t=s.spawn(0,true,i%3!==2),payload={unique:i};finish(s,t.key,ok(payload)); + if(i%3===0){s.claimJoin(0,t);assert.equal(s.pollJoin(0).value,payload);} + if(i%3===1)s.discard(0,t); + assert.deepEqual(s.counts,{scopes:1,tasks:0,hosts:0});assert(![...s.payloads()].includes(payload)); + } + finish(s,0);retired(s); +}); diff --git a/ext/demo-site/test/io-v2-terminal.test.mjs b/ext/demo-site/test/io-v2-terminal.test.mjs @@ -0,0 +1,37 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Terminal} from '../dist/io-v2-terminal.js'; +const read=(id,limit=4096)=>({run:'1',id,kind:'read',limit}); +test('line input is scalar bounded, strict, LF framed and explicitly distinct from EOF',()=>{ + const t=new Terminal('1');assert.equal(t.handle(read(1)),undefined); + for(const value of ['x\ny','\ud800','😀'.repeat(257),null])assert.equal(t.line('1',1,value),undefined); + assert.equal(t.line('2',1,'x'),undefined);assert.deepEqual(t.waiting,{run:'1',id:1}); + assert.deepEqual(t.line('1',1,''),{run:'1',id:1,kind:'read',bytes:[10],eof:false}); + t.handle(read(2));assert.deepEqual(t.endInput('1',2),{run:'1',id:2,kind:'read',bytes:[],eof:true}); + assert.equal(t.handle(read(3)).eof,true);t.finish(); +}); +test('encoded line is split at requested byte bounds, without loss or invented EOF',()=>{ + const t=new Terminal('1');t.handle(read(1,1));const parts=[...t.line('1',1,'😀'.repeat(256)).bytes]; + for(let id=2;id<=1025;id++) {const r=t.handle(read(id,1));assert.equal(r.eof,false);parts.push(...r.bytes);} + assert.deepEqual(parts,[...new TextEncoder().encode('😀'.repeat(256)),10]); + assert.equal(t.handle(read(1026)),undefined);assert.throws(()=>t.finish(),/pending/); +}); +test('cancellation retires exactly the pending prompt; stale submissions do not consume its successor',()=>{ + const t=new Terminal('1');t.handle(read(1)); + assert.deepEqual(t.handle({run:'1',id:1,kind:'cancel',operation:'read'}),{run:'1',id:1,kind:'cancelled'}); + t.handle(read(2));assert.equal(t.line('1',1,'stale'),undefined);assert.equal(t.endInput('1',1),undefined); + assert.equal(t.handle({run:'1',id:1,kind:'cancel',operation:'read'}),undefined); + assert.equal(t.waiting.id,2);assert.deepEqual(t.line('1',2,'ok').bytes,[111,107,10]);t.finish(); +}); +test('binary write acknowledgement is independent of display decoding and abort races never duplicate it',()=>{ + const t=new Terminal('1'),event={run:'1',id:1,kind:'write',stream:'output',bytes:[255,240,159]}; + assert.deepEqual(t.handle(event),{run:'1',id:1,kind:'write',count:3}); + assert.equal(t.handle({run:'1',id:1,kind:'cancel',operation:'write'}),undefined); + assert.throws(()=>t.handle(event),/stale/);t.finish(); + assert.deepEqual(t.output.snapshot().output.bytes,event.bytes);assert.equal(t.output.snapshot().output.tokens.at(-1).reason,'incomplete'); +}); +test('invalid input events preserve current waits and do not consume future identities',()=>{ + const t=new Terminal('1');assert.throws(()=>t.handle(read(2,0)),/read/);t.handle(read(1)); + assert.throws(()=>t.handle(read(2)),/read/);assert.throws(()=>t.handle({...read(2),run:'2'}),/event/); + assert.equal(t.waiting.id,1);t.endInput('1',1);assert.equal(t.handle(read(2)).eof,true); +}); diff --git a/ext/demo-site/test/io-v2-utf8.test.mjs b/ext/demo-site/test/io-v2-utf8.test.mjs @@ -0,0 +1,36 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {encodeUTF8,decodeUTF8} from '../dist/io-v2-utf8.js'; +test('all Unicode scalars match independent platform encoding and strict decoding',()=>{ + const encoder=new TextEncoder(); + for(let start=0;start<=0x10ffff;start+=1024){ + const scalars=[];for(let n=start;n<Math.min(start+1024,0x110000);n++)if(n<0xd800||n>0xdfff)scalars.push(n); + const text=String.fromCodePoint(...scalars),expected=[...encoder.encode(text)]; + assert.deepEqual(encodeUTF8(text),expected);assert.deepEqual(decodeUTF8(expected,true),{text,carry:[]}); + } +}); +test('every valid multibyte scalar prefix yields only copied bounded carry, and fails when final',()=>{ + const encoder=new TextEncoder(); + for(let n=128;n<=0x10ffff;n++){ + if(n>=0xd800&&n<=0xdfff)continue; + const bytes=[...encoder.encode(String.fromCodePoint(n))]; + for(let split=1;split<bytes.length;split++){ + const prefix=bytes.slice(0,split),out=decodeUTF8(prefix,false); + assert.equal(out.text,'');assert.deepEqual(out.carry,prefix);assert.notEqual(out.carry,prefix); + } + } + // Exhaustive prefixes above; explicit final/invalid-prefix controls below keep + // exception creation out of the million-scalar loop. + for(const bytes of [[0xc2],[0xe0,0xa0],[0xf4,0x8f,0xbf]])assert.throws(()=>decodeUTF8(bytes,true),/incomplete/); +}); +test('invalid lead/continuation/overlong/surrogate/range prefixes fail before finality',()=>{ + for(const bytes of [[0x80],[0xc0],[0xc1],[0xf5],[0xff],[0xe0,0x9f],[0xed,0xa0],[0xf0,0x8f],[0xf4,0x90],[0xc2,0x41],[0xe2,0x82,0x41]]) + for(const final of [false,true])assert.throws(()=>decodeUTF8(bytes,final),/invalid UTF-8/); + assert.throws(()=>decodeUTF8([65,0xff],false),/at byte 1$/); +}); +test('BOM, NUL, combining scalars and line endings are unchanged; scalar/byte bounds are separate',()=>{ + const text='\ufeffa\0\r\nλ😀e\u0301';assert.deepEqual(decodeUTF8(encodeUTF8(text),true),{text,carry:[]}); + assert.equal(encodeUTF8('😀'.repeat(1024)).length,4096); + assert.throws(()=>encodeUTF8('a'.repeat(1025)),/scalar/);assert.throws(()=>encodeUTF8('\ud800'),/scalar/); + assert.throws(()=>decodeUTF8(Array(4097).fill(65),false),/byte/); +}); diff --git a/ext/demo-site/test/io-version-boundary.test.mjs b/ext/demo-site/test/io-version-boundary.test.mjs @@ -0,0 +1,16 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {Graph} from '../dist/graph.js'; +import {natural,text} from '../dist/codecs.js'; +import {envelope} from '../dist/io-v2-profile.js'; + +test('current framing rejects v1 before observing action and never guesses a protocol',()=>{ + const g=new Graph(),poison=g.app(g.leaf,g.leaf),observe=g.observe.bind(g); + g.observe=id=>{if(id===poison)throw Error('action forced');return observe(id);}; + for(const version of [0n,1n,3n,99n]){ + const program=g.fork(text(g,'tricuIO'),g.fork(natural(g,version),poison)); + assert.throws(()=>envelope(g,program),/Unsupported tricuIO version/); + } + const program=g.fork(text(g,'tricuIO'),g.fork(natural(g,2n),poison)); + assert.deepEqual(envelope(g,program),{version:2,action:poison}); +}); diff --git a/ext/demo-site/test/io-version-probe.mjs b/ext/demo-site/test/io-version-probe.mjs @@ -0,0 +1,40 @@ +// Actual selected production adapter, independent constructor-only bundles. +export async function run(_control,backend){ + const assert=(value,message)=>{if(!value)throw Error(message);}; + const base=new URL('../',document.querySelector('script[type=module][src]').src); + const {createRuntimeSession}=(await import(new URL('dist/probe-api.js',base))).runtimeSession; + const fixtures=await(await fetch('/zig-negative.json')).json(); + const checks=[];const s=await createRuntimeSession(backend,false); + const host={before:async()=>{},after:()=>{},read:async()=>'',write:()=>{},running:()=>{}}; + const load=async name=>{const f=fixtures[name];await s.load(new Uint8Array(f.bytes),f.hash);}; + try{ + await load('pure-number'); + assert((await s.run('root',[],'number','pure',host)).value==='42','pure evaluation'); + checks.push(backend+' pure evaluation'); + for(const name of ['version','version-effect']){ + await load(name);assert(await s.probeIO('root')===true,'inert probe recognizes supported v2 framing'); + let callbacks=0;const forbidden=()=>{callbacks++;throw Error('unexpected host callback');};let rejected=false; + try{await s.run('root',[],'tree','io',{before:forbidden,after:forbidden,read:forbidden,write:forbidden,running:forbidden});} + catch(error){assert(/IO version 2 requires a structured terminal host/.test(error.message),'wrong rejection: '+error.message);rejected=true;} + assert(rejected&&callbacks===0,'legacy host accepted v2 or was invoked'); + } + for(const name of ['unknown-version','padded-v1','v1-output']){ + await load(name);assert(!await s.probeIO('root'),'unknown/noncanonical version inert rejection'); + let unknownCallbacks=0;const never=()=>{unknownCallbacks++;throw Error('unknown version invoked host');}; + try{await s.run('root',[],'tree','io',{...host,selected:never,v2:{open:never,event:never,close:never}});throw Error('unknown version accepted');} + catch(error){assert(/Unsupported tricuIO version/.test(error.message),'unknown version error');} + assert(unknownCallbacks===0,'unknown framing reached callbacks'); + } + checks.push(backend+' rejects v2 on legacy hosts before callbacks, including overlapping v1 output; no retry'); + await load('pure-number'); + assert((await s.run('root',[],'number','pure',host)).value==='42','reuse after rejection'); + checks.push(backend+' rejects historical v1 without callbacks; pure runtime remains reusable'); + }finally{s.dispose();} + const compiler=await createRuntimeSession(backend,true); + try{ + await compiler.compile('answer = 42'); + assert((await compiler.inspectDefinition('answer','number')).value==='42','pure compiler'); + checks.push(backend+' separately pinned pure compiler remains usable'); + }finally{compiler.dispose();} + return {backend,checks}; +} diff --git a/ext/demo-site/test/live-ux-probe.mjs b/ext/demo-site/test/live-ux-probe.mjs @@ -29,9 +29,9 @@ export async function run(control,backend='haskell-wasm'){ el('source-editor').dispatchEvent(new CompositionEvent('compositionstart'));input('source-editor','answer = 49');await pause(900);assert(el('status').textContent==='Changes pending','IME does not compile mid-composition'); el('source-editor').dispatchEvent(new CompositionEvent('compositionend'));await status('Compiled');assert(await inspect('answer','number')==='49','IME completion compiles'); input('source-editor','answer = 50');change('program-picker','demo:language-reference');await status('Ready');await pause(900);assert(el('status').textContent==='Ready','Package change cancels queued compilation'); - document.querySelector('[data-compiler-example=input]').click();await status('Compiled');assert(el('input-form').hidden&&el('terminal').textContent==='','Compilation never runs IO'); - await inspect('io');assert(el('input-form').hidden,'Recognition never runs IO');el('io-return-format').value='string';el('run-io').click();await status('Waiting for input'); - assert(globalThis.__sourceEditor.isDisabled&&el('reset').disabled,'Active IO cannot be replaced by background edits or Clear');input('line-input','hello λ🙂');el('input-form').requestSubmit();await status('Finished');assert(result()==='hello λ🙂','Inline IO return'); + document.querySelector('[data-compiler-example=input]').click();await status('Compiled');assert(!el('input-form')&&el('terminal-v2').textContent==='','Compilation never runs IO'); + await inspect('io');assert(!el('terminal-v2').querySelector('form'),'Recognition never runs IO');el('io-return-format').value='string';el('run-io').click();await wait(()=>el('terminal-v2').querySelector('form input'),'structured input'); + assert(globalThis.__sourceEditor.isDisabled&&el('reset').disabled,'Active IO cannot be replaced by background edits or Clear');el('terminal-v2').querySelector('form input').value='hello λ🙂';el('terminal-v2').querySelector('form').requestSubmit();await status('Finished');assert(result()==='hello λ🙂','Inline IO return'); input('source-editor','id x = x\nanswer = id 51\nbomb = (x : x x) (x : x x)');await status('Compiled');assert(await inspect('answer','number')==='51','Unused divergent binding is not forced'); document.querySelector('[aria-label="Inspect compiled definition bomb"]').click();await wait(()=>el('status').textContent==='Inspecting bomb','divergent observation');await pause(150);el('stop').click();await status('Stopped'); }finally{globalThis.Worker=NativeWorker;} diff --git a/ext/demo-site/test/module-probe.mjs b/ext/demo-site/test/module-probe.mjs @@ -1,3 +1,4 @@ +import {terminalHost} from './terminal-host.mjs'; import {evaluateSource,inspect} from './source-probe-helpers.mjs'; // Production adapters + real page; expected values are independent arithmetic, // native header contracts, and the CLI cases in native-modules.py. @@ -11,6 +12,7 @@ const change=(id,value)=>{el(id).value=value;el(id).dispatchEvent(new Event('cha export async function run(control,backend='haskell-wasm'){ const base=new URL('../',document.querySelector('script[type=module][src]').src); const {createRuntimeSession}=(await import(new URL('dist/probe-api.js',base))).runtimeSession; + const {Terminal}=(await import(new URL('dist/probe-api.js',base))).ioV2Terminal; const {packageIndex,packageReleases}=(await import(new URL('dist/probe-api.js',base))).packages; const releases=packageIndex(await(await fetch(new URL('packages.json',base))).json()); const lib=name=>packageReleases(releases,'tricu-lib-'+name)[0]; @@ -30,12 +32,21 @@ export async function run(control,backend='haskell-wasm'){ await s.compile(demo.source,demo.environment); for(const c of fixtures.demos[demo.id].haskellCases){ let terminal=''; + const hooks=demo.mode==='io'?terminalHost(Terminal,c.input):{...host,read:async()=>c.input,write:text=>{terminal+=text;}}; const result=await s.run(c.export,c.arguments.map(value=>({type:'tree',value})),'ternary',demo.mode, - {...host,read:async()=>c.input,write:text=>{terminal+=text;}}); + hooks); equal(result.value,c.result,'Recompiled demo golden '+demo.id); - equal(terminal,c.terminal,'Recompiled terminal '+demo.id); + equal(demo.mode==='io'?hooks.terminalText:terminal,c.terminal,'Recompiled terminal '+demo.id); } } + await phase('retained v1 publication'); + const oldGreeting=releases.find(r=>r.name==='greet'&&r.version==='0.2.0'); + assert(oldGreeting?.hash==='0811457d501113ee11194f90e279ebb4ef9d548c70cb48d146be03eb9af7505f','Original publication pin'); + await s.load(new Uint8Array(await(await fetch(new URL('/_arboricx/bundle/hash/'+oldGreeting.hash,base))).arrayBuffer()),oldGreeting.hash); + assert(!await s.probeIO('main'),'Historical v1 is not current executable IO'); + let callbacks=0;const forbidden=()=>{callbacks++;throw Error('retired host callback');}; + await rejects(()=>s.run('main',[],'string','io',{...host,selected:forbidden,before:forbidden,write:forbidden,read:forbidden}),/Unsupported tricuIO version/); + equal(callbacks,0,'Retired framing rejects before host callbacks'); await phase('native module contracts'); equal(await s.compile(source),['answer'],'Only explicit public exports'); equal(await value('answer'),'42','Qualified module application'); @@ -62,11 +73,11 @@ export async function run(control,backend='haskell-wasm'){ equal(hash,golden.ternaryAsciiSha256,'Exact function tree agrees with independent native compiler'); } const names=await s.compile(`!export * from ${io.manifest}`); - assert(names.includes('io')&&names.includes('readFile')&&names.length>16,'Header-only full re-export'); + assert(names.includes('io')&&names.includes('openReader')&&names.length>16,'Header-only full re-export'); equal((await s.run('io',[{type:'number',value:'42'}],'tree','pure',host)).type,'tree','Re-export is executable, not source expansion'); - await rejects(()=>value('readFile'),/unresolved name/); // reexports do not introduce lexical imports - await s.compile(`!import ${io.manifest} as I\n!export main\nmain = I.io (I.readFile "no-browser-filesystem")`); - await rejects(()=>s.run('main',[],'string','io',host),/Unsupported.*20|unsupported.*20/i); + await rejects(()=>value('openReader'),/unresolved name/); // reexports do not introduce lexical imports + await s.compile(`!import ${io.manifest} as I\n!export main\nmain = I.io (I.scope (owner : I.require (I.openReader owner "no-browser-filesystem") I.pure))`); + await rejects(()=>s.run('main',[],'string','io',terminalHost(Terminal,null)),/IO v2 failed/i); await s.compile(`!import ${math.manifest} as N\n!export answer\nloop x = loop x\nanswer = N.add 40 2`); equal(await value('answer'),'42','Unused divergent function is never normalized'); const failures=[ diff --git a/ext/demo-site/test/modules.test.mjs b/ext/demo-site/test/modules.test.mjs @@ -1,6 +1,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import {readFile, readdir} from 'node:fs/promises'; +import {createHash} from 'node:crypto'; import {decodeBundle, sha256} from '../dist/bundle.js'; import {indexedObject, moduleIdentity, moduleHash} from '../dist/module-identity.js'; import {fetchModule} from '../dist/module-resolver.js'; @@ -21,17 +22,41 @@ test('every library source has a complete-interface package; no module allowlist } } await walk('lib/'); - assert.deepEqual(libraries.map(r => r.title).sort(), paths.sort()); + // Historical package names remain indexed when an authored workspace alias + // changes. Check every source and its current alias, not an allowlist or the + // first historical release carrying the same source-path title. + assert.deepEqual([...new Set(libraries.map(r => r.title))].sort(), paths.sort()); + const workspace=(await readFile(new URL('tricu.workspace',root),'utf8')).trimEnd().split('\n'); + assert.equal(workspace.shift(),'tricu.workspace.v1'); + const aliases=new Map(workspace.map(row=>{const [alias,path]=row.split('\t');return [path,alias];})); + const currentLibrary=path=>libraries.find(r=>r.name==='tricu-lib-'+ + (aliases.get(path)??path.slice(4,-4).replaceAll('/','_').replaceAll('-','_')).replaceAll('_','-')); + for(const path of paths){const release=currentLibrary(path);assert.ok(release,path);assert.equal(release.title,path);} for (const cohort of ['compiler-library', 'support-library']) { - const fixture = decodeBundle(await readFile(new URL(`test/fixtures/${cohort}.arboricx`, root))); + // The aggregate native support fixture exceeds the browser's per-module + // node bound. Compare its independently authenticated native manifests, + // not a relaxed browser decoder. Every actual published module below still + // passes the unchanged browser transport/graph bounds. + const fixtureBytes = await readFile(new URL(`test/fixtures/${cohort}.arboricx`, root)); + const fixture = cohort === 'compiler-library' ? decodeBundle(fixtureBytes) : null; + if (!fixture) assert.throws(() => decodeBundle(fixtureBytes), /Bundle node count limit exceeded/); const metadata = await readFile(new URL(`test/fixtures/${cohort}.v1`, root), 'utf8'); for (const row of metadata.split('\n').filter(s => s.startsWith('module\t'))) { const [, , alias, path] = row.split('\t'); if (!path.startsWith('lib/')) continue; - const release = libraries.find(r => r.title === path); + const release = currentLibrary(path); assert.ok(release, path); const bundle = decodeBundle(await bytes(release)); - assert.deepEqual(bundle.exports.map(e => e.name), fixture.exports.filter(e => e.name.startsWith(alias + '.')).map(e => e.name.slice(alias.length + 1)), path); + let expected; + if (fixture) expected = fixture.exports.filter(e => e.name.startsWith(alias + '.')).map(e => e.name.slice(alias.length + 1)); + else { + const manifest = await readFile(new URL(`test/fixtures/support-receipts/${alias}.manifest`, root)); + const record = metadata.split('\n').find(line => line.startsWith(`rebuilt\t${alias}\t`)).split('\t'); + assert.equal(createHash('sha256').update('arboricx.module-manifest.v1\0').update(manifest).digest('hex'), record[3]); + const rows = manifest.toString('utf8').trimEnd().split('\n'); assert.equal(rows.shift(), 'arboricx.module-manifest.v1'); + expected = rows.map(line => { const fields = line.split('\t'); assert.equal(fields.length, 5); assert.equal(fields[0], 'export'); return fields[1]; }); + } + assert.deepEqual(bundle.exports.map(e => e.name), expected, path); } } }); diff --git a/ext/demo-site/test/prepare-compiler.py b/ext/demo-site/test/prepare-compiler.py @@ -105,6 +105,17 @@ with tempfile.TemporaryDirectory(prefix='arboricx-browser-compiler-') as tempora path = temp / f'arg{i}.tree' path.write_bytes(bytes(map(int, argument))) args += ['--arg-tree-file', path] + if case.get('mode') == 'io': + # Historical framing remains a compilation/inspection specimen. + # Current native execution must reject it before terminal work. + rejected = subprocess.run([str(host), 'arboricx', 'run', '--module', manifest, + '--name', 'main', '--store', str(store), '--io'], + input=b'', capture_output=True, timeout=60, cwd=p.ROOT) + assert rejected.returncode != 0 and rejected.stdout == b'' + assert b'Unsupported tricuIO version' in rejected.stderr + case['compileOnly'] = True + case['executionRejected'] = 'Unsupported tricuIO version' + continue case['result'] = run('arboricx', 'run', '--module', manifest, '--name', 'main', '--store', store, '--format', 'ternary', *args, *(['--io'] if case.get('mode') == 'io' else []), input=(case['input'] + '\n').encode() if 'input' in case else None) diff --git a/ext/demo-site/test/probe-api.mjs b/ext/demo-site/test/probe-api.mjs @@ -9,3 +9,10 @@ export * as codecs from './codecs.js'; export * as literalInput from './literal-input.js'; export { editorFor } from './source-editor.js'; +export * as ioV2Tasks from './io-v2-tasks.js'; +export * as ioV2HostWork from './io-v2-host-work.js'; +export * as ioV2Resources from './io-v2-resources.js'; + +export * as ioV2TerminalView from './io-v2-terminal-view.js'; + +export * as ioV2Terminal from './io-v2-terminal.js'; diff --git a/ext/demo-site/test/resume.test.mjs b/ext/demo-site/test/resume.test.mjs @@ -1,9 +1,8 @@ import test from 'node:test'; import assert from 'node:assert/strict'; -import {resumeValue} from '../dist/io.js'; -test('resume kinds and bounded Unicode lines are checked without consuming a retry',()=>{ - assert.equal(resumeValue('step',undefined),''); - for(const v of ['',false,7,null])assert.equal(resumeValue('step',v),undefined); - for(const v of [undefined,null,7,'\n','\r','\ud800','\udfff','a'.repeat(257),'🙂'.repeat(257)])assert.equal(resumeValue('input',v),undefined); - for(const v of ['','λ雪🙂','🙂'.repeat(256)])assert.equal(resumeValue('input',v),v); +import {validStepResume} from '../dist/io.js'; +test('scheduler resumes accept no payload; terminal input is a separate v2 protocol',()=>{ + assert.equal(validStepResume(undefined),true); + for(const value of ['',false,7,null,'λ雪🙂','\n','\r','\ud800','\udfff','a'.repeat(257),'🙂'.repeat(257),{}]) + assert.equal(validStepResume(value),false); }); diff --git a/ext/demo-site/test/site.test.mjs b/ext/demo-site/test/site.test.mjs @@ -55,7 +55,8 @@ test('conformance workers retain unbounded execution and shared transport limits test('inline IO has explicit authority, revision checks and full-result actions',async()=>{ const html=await read('index.html'),app=await read('src/app.ts'),inspector=await read('src/definition-inspector.ts'),worker=await read('src/wasm-worker.ts'); assert.doesNotMatch(html,/id="run"|id="stop-run"|id="mode"|id="arguments"|id="output-type"/); - for(const token of ['run-io','stop-io','input-form','line-input','step-mode','next','Returned value','Copy','Download'])assert.ok(inspector.includes(token),token); + for(const token of ['run-io','stop-io','terminal-v2','step-mode','next','Returned value','Copy','Download'])assert.ok(inspector.includes(token),token); + assert.doesNotMatch(inspector,/input-form|line-input|tricuIO v1/); assert.match(inspector,/io\.run\(selected/);assert.match(inspector,/ioDetected/);assert.match(inspector,/valueText\(value\)/);assert.match(inspector,/saved\.text/); assert.match(app,/editRevision !== compiledRevision/);assert.match(worker,/session\.probeIO\(name\)/);assert.match(worker,/session\.runDefinitionIO/); }); diff --git a/ext/demo-site/test/terminal-host.mjs b/ext/demo-site/test/terminal-host.mjs @@ -0,0 +1,28 @@ +// Test-only host for complete native/compiler goldens. Production uses the DOM +// terminal view. null means explicit EOF; an empty string is one empty line. +export function terminalHost(Terminal, input) { + let terminal, send, submitted=false, settled=false; + const forbidden=()=>{throw Error('v2 golden invoked a legacy text callback');}; + return { + before:async()=>{},after(){},read:forbidden,write:forbidden,running(){}, + get terminalText(){ + if(!settled)throw Error('terminal did not settle'); + const snapshot=terminal.output.snapshot(); + if(snapshot.error.bytes.length)throw Error('unexpected stderr'); + return new TextDecoder('utf-8',{fatal:true}).decode(new Uint8Array(snapshot.output.bytes)); + }, + v2:{ + open(run,command){submitted=false;settled=false;terminal=new Terminal(run);send=command;}, + event(event){ + let reply=terminal.handle(event); + if(!reply&&terminal.waiting){ + const {run,id}=terminal.waiting; + reply=submitted||input===null?terminal.endInput(run,id):terminal.line(run,id,input); + submitted=true; + } + if(reply)send({run:reply.run,kind:'reply',reply}); + }, + close(_run,acknowledged){settled=acknowledged;if(settled)terminal.finish();}, + }, + }; +} diff --git a/ext/demo-site/test/zig-negative-fixtures.mjs b/ext/demo-site/test/zig-negative-fixtures.mjs @@ -10,14 +10,50 @@ export function fixtures(){ const list=xs=>xs.reduceRight((tail,x)=>fork(x,tail),leaf),text=s=>list(Array.from(s,c=>num(c.codePointAt(0)))); const action=(tag,payload)=>fork(num(tag),payload),constant=t=>fork(leaf,t); const io=(value,version=1)=>fork(text('tricuIO'),fork(num(version),value)); - const root=build({leaf,fork,num,text,action,constant,io});const bytes=makeBundle(nodes,[['root',root]]); + const root=build({leaf,stem:a=>node(a),fork,num,text,action,constant,io});const bytes=makeBundle(nodes,[['root',root]]); result[name]={bytes:[...bytes],hash:createHash('sha256').update(bytes).digest('hex')}; }; + fixture('pure-number',x=>x.num(42)); + fixture('v1-output',x=>x.io(x.action(10,x.text('v1 output')))); + fixture('padded-v1',x=>x.fork(x.text('tricuIO'),x.fork(x.fork(x.stem(x.leaf),x.fork(x.leaf,x.leaf)),x.action(10,x.text('MUST NOT WRITE'))))); + fixture('unknown-version',x=>x.io(x.action(10,x.text('MUST NOT WRITE')),3)); fixture('version',x=>x.io(x.action(0,x.leaf),2)); + fixture('version-effect',x=>x.io(x.action(10,x.text('MUST NOT WRITE')),2)); fixture('unsupported',x=>x.io(x.action(99,x.leaf))); fixture('abort',x=>{let t=x.leaf;for(let i=0;i<40;i++)t=x.fork(t,t);return x.io(x.action(2,t));}); fixture('output',x=>{let a=x.action(0,x.leaf);const write=x.action(10,x.text('x'.repeat(4096)));for(let i=0;i<5;i++)a=x.action(1,x.fork(write,x.constant(a)));return x.io(a);}); fixture('actions',x=>{let a=x.action(0,x.leaf);for(let i=0;i<65;i++)a=x.action(1,x.fork(x.action(0,x.leaf),x.constant(a)));return x.io(a);}); fixture('sharing',x=>{let t=x.leaf;for(let i=0;i<40;i++)t=x.fork(t,t);return t;}); + for(let code=0;code<10;code++)fixture('v2-header-core-'+code,x=>{let payload=x.leaf;for(let i=0;i<80;i++)payload=x.fork(payload,payload);return x.action(code,payload);}); + const req=(x,g,v,op)=>x.action(10,x.fork(g,x.fork(v,x.fork(op,x.leaf)))); + fixture('v2-header-future',x=>req(x,x.text('future-9'),x.num(65535),x.text('unknown'))); + fixture('v2-header-long-name',x=>req(x,x.text('a'.repeat(64)),x.num(1),x.text('read'))); + fixture('v2-header-bad-tag',x=>x.fork(x.fork(x.leaf,x.leaf),x.leaf)); + fixture('v2-header-bad-name',x=>req(x,x.text('a'.repeat(65)),x.num(1),x.text('read'))); + fixture('v2-header-bad-revision',x=>req(x,x.text('file'),x.num(65536),x.text('read'))); + fixture('v2-header-zero-revision',x=>req(x,x.text('file'),x.num(0),x.text('read'))); + fixture('v2-header-bad-shape',x=>x.leaf); + const seq=(x,a,b)=>x.action(1,x.fork(a,x.constant(b))); + fixture('v2-run-pure',x=>x.action(0,x.num(42))); + fixture('v2-run-bind',x=>seq(x,x.action(0,x.num(7)),x.action(0,x.num(42)))); + fixture('v2-run-scope',x=>x.action(3,x.constant(seq(x,x.action(4,x.leaf),x.action(0,x.num(42)))))); + for(const supervised of [0,1])fixture('v2-run-child-'+supervised,x=>{ + const child=x.action(2,x.num(7)); + const spawn=x.action(5,x.fork(x.num(supervised),x.fork(x.num(0),x.fork(x.leaf,x.constant(child))))); + return seq(x,spawn,x.action(0,x.num(42))); + }); + fixture('v2-run-unsupported',x=>seq(x,req(x,x.text('future'),x.num(1),x.text('read')),x.action(0,x.num(42)))); + fixture('v2-run-discard-invalid',x=>seq(x,x.action(7,x.leaf),x.action(0,x.num(42)))); + for(const tag of [2,9])fixture('v2-run-terminal-'+tag,x=>{let opaque=x.leaf;for(let i=0;i<80;i++)opaque=x.fork(opaque,opaque);return x.action(tag,opaque);}); + fixture('v2-run-malformed',x=>x.action(1,x.leaf)); + fixture('v2-session-streams',x=>{ + const marker=(name,key)=>x.fork(x.text(name),x.fork(x.num(1),x.fork(x.num(key),x.leaf))); + const request=(group,op,args)=>x.action(10,x.fork(x.text(group),x.fork(x.num(1),x.fork(x.text(op),args)))); + const read=request('stream','read',x.fork(marker('io.v2.resource',1),x.num(8))); + const write=request('stream','write',x.fork(marker('io.v2.resource',2),x.fork(x.num(65),x.leaf))); + const spawn=x.action(5,x.fork(x.num(1),x.fork(x.num(0),x.fork(x.leaf,x.constant(read))))); + const cancel=x.action(8,x.fork(marker('io.v2.task',1),x.leaf)); + return seq(x,spawn,seq(x,write,seq(x,cancel,x.action(0,x.num(42))))); + }); return result; } diff --git a/ext/demo-site/test/zig-session-probe.mjs b/ext/demo-site/test/zig-session-probe.mjs @@ -1,9 +1,12 @@ +import {terminalHost} from './terminal-host.mjs'; // Independent complete Haskell captures, exercised through Zig's coarse adapter. const assert=(ok,message)=>{if(!ok)throw Error(message);}; const equal=(a,b,label)=>assert(JSON.stringify(a)===JSON.stringify(b),label+': '+JSON.stringify(a).slice(0,160)); async function rejects(fn,pattern){try{await fn();}catch(e){assert(pattern.test(e.message),'Wrong rejection: '+e.message);return;}throw Error('Expected '+pattern);} -export async function run() { +export async function run(control) { + const phase=async name=>{if(control)await fetch(control+'/phase',{method:'POST',body:JSON.stringify({name})});}; const base=new URL('../',document.querySelector('script[type=module][src]').src); + const {Terminal}=(await import(new URL('dist/probe-api.js',base))).ioV2Terminal; const {create}=await import(new URL('runtime/zig/site-session.mjs',base)); const fixtures=await(await fetch('/zig-fixtures.json')).json(); const catalogue=await(await fetch(new URL('catalogue.json',base))).json(); @@ -14,22 +17,37 @@ export async function run() { const s=await create({base,compiler:false}); try { for(const [name,demo] of Object.entries(fixtures.demos)) { + await phase("full golden "+name); await s.load(await bytes(demo.hash),demo.hash);report.packages++; for(const c of demo.haskellCases){ let terminal='',before; - const hooks=host(c.input); + const hooks=demo.mode==='io'?terminalHost(Terminal,c.input):host(c.input); hooks.before=async e=>{assert(!before,'Missing after');before=e;}; hooks.after=e=>{assert(e.step===before?.step&&e.name===before.name,'Trace ordering/name');before=undefined;}; - hooks.read=async()=>{s.collect();return c.input;};hooks.write=text=>{terminal+=text;}; + if(demo.mode==='io'){const event=hooks.v2.event;hooks.v2.event=e=>{s.collect();event(e);};} + else {hooks.read=async()=>{s.collect();return c.input;};hooks.write=text=>{terminal+=text;};} equal(await s.inspect(c.export,c.arguments.map(value=>({type:'tree',value})),demo.mode,hooks),c.result,'Full golden '+name); - equal(terminal,c.terminal,'Terminal '+name);report.applications++; + equal(demo.mode==='io'?hooks.terminalText:terminal,c.terminal,'Terminal '+name);report.applications++; } } const negatives=await(await fetch('/zig-negative.json')).json(); - for(const [name,pattern] of [['version',/version/],['unsupported',/Unsupported/],['abort',/aborted/],['output',/output limit/],['actions',/action limit/]]){ + { + const f=negatives['v1-output'];await s.load(new Uint8Array(f.bytes),f.hash); + let calls=0;const forbidden=()=>{calls++;throw Error('retired callback');}; + await rejects(()=>s.run('root',[],'tree','io',{...host(),before:forbidden,write:forbidden,read:forbidden}),/Unsupported tricuIO version/); + equal(calls,0,'Retired v1 output never invokes host'); + } + for(const [name,pattern] of [['version',/version/],['version-effect',/version/],['unsupported',/Unsupported/],['abort',/Unsupported tricuIO version/],['output',/Unsupported tricuIO version/],['actions',/Unsupported tricuIO version/]]){ const f=negatives[name];await s.load(new Uint8Array(f.bytes),f.hash); await rejects(()=>s.run('root',[],'tree','io',host()),pattern); } + { + const f=negatives['version-effect'];await s.load(new Uint8Array(f.bytes),f.hash); + let callbacks=0;const forbidden=()=>{callbacks++;throw Error('version gate reached host callback');}; + await rejects(()=>s.run('root',[],'tree','io',{before:forbidden,after:forbidden,read:forbidden,write:forbidden,running:forbidden}),/version/); + equal(callbacks,0,'Unsupported version invoked host'); + report.checks.push('IO v2 rejected before host callbacks; no v1 retry'); + } const shared=negatives.sharing;await s.load(new Uint8Array(shared.bytes),shared.hash); const preview=(await s.run('root',[],'tree','pure',host())).value; assert(!preview.truncated&&preview.nodes.length===41,'Physical sharing expanded'); @@ -59,10 +77,11 @@ export async function run() { report.memory.push(s.diagnostics().linearMemoryBytes); } assert(Math.max(...report.memory.slice(25))<=Math.max(...report.memory.slice(0,25))+65536,'Memory failed to plateau'); - report.checks.push(`${report.applications} complete published goldens; IO GC/trace ordering/limits; scalar/list/tree codecs and negative controls; physical sharing; ownership and memory plateau`); + report.checks.push(`${report.applications} complete published goldens; v2 IO GC and retired framing rejection; scalar/list/tree codecs and negative controls; physical sharing; ownership and memory plateau`); const greet=fixtures.demos.greet;await s.load(await bytes(greet.hash),greet.hash); let entered;const paused=new Promise(resolve=>{entered=resolve;}); - const running=s.run(greet.haskellCases[0].export,[],'string','io',{...host(''),before:()=>{entered();return new Promise(()=>{});}}); + const pausedHost=terminalHost(Terminal,'');pausedHost.v2.stepping=true;pausedHost.before=()=>{entered();return new Promise(()=>{});}; + const running=s.run(greet.haskellCases[0].export,[],'string','io',pausedHost); running.catch(()=>{});await paused;s.dispose();await rejects(()=>running,/Disposed/);assert(s.disposed,'Disposed session reused'); }finally{s.dispose();} const c=await create({base,compiler:true}); @@ -70,9 +89,10 @@ export async function run() { for(const [name,demo] of Object.entries(fixtures.demos)){ const entry=catalogue.find(x=>x.hash===demo.hash); assert(entry||name==='intro'&&releases.some(r=>r.name==='intro'&&r.version==='0.2.0'&&r.hash===demo.hash),'Current catalogue pin or explicitly retained historical intro publication'); + await phase("source compile "+name); await c.compile(demo.source,entry?.environment??'empty'); const first=demo.haskellCases[0]; - equal(await c.inspect(first.export,first.arguments.map(value=>({type:'tree',value})),demo.mode,host(first.input)),first.result,'Compiled '+name);report.compiledDemos++; + equal(await c.inspect(first.export,first.arguments.map(value=>({type:'tree',value})),demo.mode,demo.mode==='io'?terminalHost(Terminal,first.input):host(first.input)),first.result,'Compiled '+name);report.compiledDemos++; } const intro=fixtures.demos.intro;await c.compile(intro.source,'empty'); await rejects(()=>c.compile('name = unavailableName','empty'),/Compiler diagnostic/); @@ -80,12 +100,13 @@ export async function run() { // previous program or its imported scope. Runtime/IO errors still retain it. await rejects(()=>c.run('answer',[],'number','pure',host()),/Missing definition/); const greet=fixtures.demos.greet;await c.compile(greet.source,'empty'); - await rejects(()=>c.run(greet.haskellCases[0].export,[],'string','io',{...host('x'),before:async()=>{throw Error('host failed');}}),/host failed/); - const first=greet.haskellCases[0];equal(await c.inspect(first.export,[],greet.mode,host(first.input)),first.result,'Host-error cleanup preserves definitions'); - await c.compile('q x = x x\nlazy = t (q q) t\naborted = t \"tricuIO\" (t 1 (t 2 (q q)))','empty'); + const failingHost=terminalHost(Terminal,'x');failingHost.v2.stepping=true;failingHost.before=async()=>{throw Error('host failed');}; + await rejects(()=>c.run(greet.haskellCases[0].export,[],'string','io',failingHost),/host failed/); + const first=greet.haskellCases[0];equal(await c.inspect(first.export,[],greet.mode,terminalHost(Terminal,first.input)),first.result,'Host-error cleanup preserves definitions'); + await c.compile('q x = x x\nlazy = t (q q) t\naborted = t \"tricuIO\" (t 2 (t 2 (q q)))','empty'); const partial=await c.preview('lazy',1);assert(partial.nodes.length===1&&partial.truncated,'Preview forced excluded divergent child'); - await rejects(()=>c.run('aborted',[],'tree','io',host()),/aborted/); - report.checks.push('all current and retained historical native source demos; compile-error invalidation/host-error retention; nonforcing divergent preview/abort'); + await rejects(()=>c.run('aborted',[],'tree','io',terminalHost(Terminal,null)),/IO v2 failed/); + report.checks.push('current published browser source demos and retained historical fixtures; compile-error invalidation/host-error retention; nonforcing divergent preview/abort'); }finally{c.dispose();} return report; } diff --git a/ext/haskell-wasm/BrowserRuntime.hs b/ext/haskell-wasm/BrowserRuntime.hs @@ -4,6 +4,7 @@ module BrowserRuntime where import Runtime.Tree import Runtime.Value +import qualified IOV2.Wire as Wire import Control.Exception (evaluate) import Control.Monad (unless, forM_) import Data.Array (Array, listArray, (!), elems) @@ -147,13 +148,7 @@ view kind tree=do "triangle"->preview 100000 tree "dag"->preview 100000 tree "ternary"->preview 100000 tree - "io-envelope"->catchIOError (do - (sentinel,body)<-pair tree - s<-text 7 sentinel - if s/="tricuIO" then pure(B False) else do - (version,_)<-pair body - v<-natural version - pure(B(v==1))) (\_ -> pure(B False)) + "io-envelope"->catchIOError (ioEnvelope tree >> pure(B True)) (\_ -> pure(B False)) "tree"->preview 64 tree "string"->S <$> text 4096 tree "number"->S . show <$> natural tree @@ -174,68 +169,24 @@ view kind tree=do pure(O[("type",S "number"),("value",S(show n))])) readable c=generalCategory c<=Space || c `elem` "\t\n\r\x200c\x200d" --- Explicit action machine. Events contain no tree handles; the RTS owns all --- continuations while the browser is waiting. Each resume consumes a fresh token. -data Machine = Machine T [T] Int Int -data Phase = Before Machine Integer T | ReadLine Machine | Wrote Machine | After Machine String | Done T - -beginIO :: T -> IO Phase -beginIO root=do +-- Bounded framing selection does not inspect the computation or its payload. +ioEnvelope :: T -> IO (Integer,T) +ioEnvelope root=do (sentinel,body)<-pair root - s<-text 7 sentinel - unless(s=="tricuIO")(fail "Missing tricuIO sentinel") + let match [] Leaf=True + match (c:cs) (Fork x xs)=Wire.canonicalNat 7 x==Just(toInteger(ord c)) && match cs xs + match _ _=False + unless(match "tricuIO" sentinel)(fail "Missing tricuIO sentinel") (version,action)<-pair body - v<-natural version - unless(v==1)(fail "Unsupported tricuIO version") - advance(Machine action [] 0 0) -advance :: Machine -> IO Phase -advance machine@(Machine action _ step _)=do - unless(step<128)(fail "IO action limit exceeded") - (tag,payload)<-pair action - code<-natural tag - unless(code `elem` [0,1,2,10,11])(fail ("Unsupported browser IO action "++show code)) - pure(Before machine code payload) -finishValue :: String -> Machine -> T -> Phase -finishValue _ (Machine _ [] _ _) value=Done value -finishValue name (Machine _ (f:fs) step output) value=After (Machine (apply f value) fs step output) name + case Wire.canonicalNat 2 version of + Just 2 -> pure(2,action) + _ -> fail "Unsupported tricuIO version" --- An after event precedes completion too, so stepping never skips an action. -data Pending = Pending Phase | Complete T Int String -resumeIO :: Pending -> String -> IO Pending -resumeIO (Complete value _ _) _=pure(Pending(Done value)) -resumeIO (Pending phase) value=case phase of - Before (Machine action frames step output) code payload->do - let m=Machine action frames (step+1) output - result v=case frames of []->Complete v (step+1) (actionName code);_->Pending(finishValue (actionName code) m v) - case code of - 0->pure(result payload) - 1->do (left,k)<-pair payload;pure(Pending(After (Machine left (k:frames) (step+1) output) "bind")) - 2->fail "Execution aborted (diagnostic payload not forced)." - 10->do - s<-text 4096 payload - unless(output+length s<=16384)(fail "Terminal output limit exceeded") - pure(Pending(Wrote(Machine action frames (step+1) (output+length s)))) - 11->pure(Pending(ReadLine m)) - _->fail "Unsupported IO action" - ReadLine m@(Machine _ frames step _)->do - unless(length value<=256 && all (`notElem` "\r\n") value)(fail "Input must be one bounded line") - let t=ofString value - pure(if null frames then Complete t step "getLine" else Pending(finishValue "getLine" m t)) - Wrote m@(Machine _ frames step _)->pure(if null frames then Complete Leaf step "putStr" else Pending(finishValue "putStr" m Leaf)) - After m _->Pending <$> advance m - Done _->fail "IO already finished" -actionName :: Integer -> String -actionName code=case code of 0->"pure";1->"bind";2->"abortIO";10->"putStr";11->"getLine";_->"unknown" -event :: Integer -> Pending -> IO J -event token pending=do - payload<-case pending of - Complete _ step name->pure[("kind",S "after"),("step",number step),("name",S name),("value",S "retained value")] - Pending phase->case phase of - Before (Machine _ frames step _) code t->do - detail<-if code==10 then text 4096 t else pure "retained value" - pure[("kind",S "before"),("step",number(step+1)),("name",S(actionName code)),("detail",S detail),("depth",number(length frames))] - ReadLine _->pure[("kind",S "read")] - Wrote (Machine action _ _ _)->do (_,t)<-pair action;s<-text 4096 t;pure[("kind",S "write"),("text",S s)] - After (Machine _ _ step _) name->pure[("kind",S "after"),("step",number step),("name",S name),("value",S "retained value")] - Done _->pure[("kind",S "done")] - pure(O(("token",N token):payload)) +-- Private embedding inspection, not an IO start or capability claim. Only a +-- computation header is classified; arguments and terminal payloads stay lazy. +ioV2Header :: T -> IO J +ioV2Header tree = case Wire.decodeAction tree of + Left message -> fail message + Right (Wire.Core code _) -> pure (O [("kind",S "core"),("code",N code)]) + Right (Wire.Request group revision operation _) -> pure (O + [("kind",S "request"),("group",S group),("revision",N revision),("operation",S operation)]) diff --git a/ext/haskell-wasm/BrowserTrees.hs b/ext/haskell-wasm/BrowserTrees.hs @@ -0,0 +1,87 @@ +-- Private, run-local handles for the JS interaction interpreter inside the worker. +-- No handles cross RuntimeSession. Registration never forces a tree; observation +-- forces only its outer constructor. Sweep occurs between interpreter turns. +module BrowserTrees (begin, command, clear, active) where + +import qualified BrowserRuntime as R +import Runtime.Tree +import Control.Exception (evaluate) +import Control.Monad (unless) +import Data.IORef +import qualified Data.IntMap.Lazy as IM +import System.IO.Unsafe (unsafePerformIO) +import Text.Read (readMaybe) + +{-# NOINLINE trees #-} +trees :: IORef (Maybe (IM.IntMap T)) +trees = unsafePerformIO (newIORef Nothing) +{-# NOINLINE next #-} +next :: IORef Integer +next = unsafePerformIO (newIORef 1) + +active :: IO Bool +active = maybe False (const True) <$> readIORef trees +clear :: IO () +clear = writeIORef trees Nothing +store :: IO (IM.IntMap T) +store = readIORef trees >>= maybe (fail "no private tree run") pure +put :: T -> IO Int +put value = do + table <- store + key <- readIORef next + -- wasm32 Int is narrower than a JS safe integer; never wrap either domain. + unless (key <= min 9007199254740991 (toInteger (maxBound :: Int))) (fail "private tree identity exhausted") + let ident = fromInteger key + writeIORef trees (Just (IM.insert ident value table)) + writeIORef next (key+1) + pure ident +ident :: String -> IO Int +ident text = case readMaybe text :: Maybe Integer of + Just value | value > 0 && value <= toInteger (maxBound :: Int) && show value == text -> pure (fromInteger value) + _ -> fail "invalid private tree handle" +get :: String -> IO T +get text = do + key <- ident text + table <- store + maybe (fail "stale private tree handle") pure (IM.lookup key table) +number :: Int -> R.J +number = R.N . toInteger +begin :: T -> IO R.J +begin action = do + busy <- active + unless (not busy) (fail "private tree run already active") + writeIORef trees (Just IM.empty) + leaf <- put Leaf + root <- put action + pure (R.O [("leaf",number leaf),("action",number root)]) +command :: [String] -> IO R.J +command fields = case fields of + ["observe",handle] -> do + tree <- get handle >>= evaluate + node <- case tree of + Leaf -> pure (R.O [("kind",R.S "leaf")]) + Stem child -> do + key <- put child + pure (R.O [("kind",R.S "stem"),("child",number key)]) + Fork left right -> do + a <- put left; b <- put right + pure (R.O [("kind",R.S "fork"),("left",number a),("right",number b)]) + pure (R.O [("node",node)]) + ["stem",a] -> get a >>= put . Stem >>= pure . number + ["fork",a,b] -> do + left <- get a; right <- get b + put (Fork left right) >>= pure . number + ["app",a,b] -> do + left <- get a; right <- get b + put (apply left right) >>= pure . number + "retain":handles -> do + unless (length handles <= 100000) (fail "private root limit") + keys <- mapM ident handles + table <- store + unless (all (`IM.member` table) keys) (fail "stale private tree handle") + writeIORef trees (Just (IM.fromList [(key,table IM.! key) | key <- keys])) + pure (number (length keys)) + ["view",handle,codec] -> get handle >>= R.view codec + ["count"] -> number . IM.size <$> store + ["clear"] -> clear >> pure (R.O []) + _ -> fail "invalid private tree operation" diff --git a/ext/haskell-wasm/README.md b/ext/haskell-wasm/README.md @@ -1,10 +1,10 @@ # Haskell WASM probes and browser embedding These opt-in targets compile the production `Runtime.Tree` and `Runtime.Value` -modules unchanged for native GHC and upstream GHC WASM. They are not a port of -the native CLI. The command-line probes measure reduction and the site's pinned -portable compiler; the browser target adds an isolated package/compiler session -with the site's codecs, inspection/export and five-action IO protocol. +modules unchanged for native GHC and upstream GHC WASM. Command-line probes +measure reduction and the site's pinned portable compiler. The browser target +adds an isolated package/compiler session with the site's codecs, inspection/export +and structured IO v2 terminal profile. ```sh nix build .#haskellWasmProbe --out-link /tmp/tricu-haskell-wasm-probe @@ -22,10 +22,10 @@ modules (the build records the Cabal-install version without building with it). The measurement script runs five fresh processes per host/workload, alternating host order, with a 60-second timeout, 512 MiB Haskell heap limit and 8 MiB Haskell -stack limit. These are not limits on total engine memory or native machine stack. +stack limit. Engine metadata and native machine-stack usage contribute additional memory. It records every sample, medians, binary SHA256s, raw and gzip sizes, versions, and platform. Wasmtime compilation caching is disabled. External process time -therefore includes engine compilation; it is **not** warm browser-call latency. +therefore includes engine compilation. Workloads are deliberately narrow: @@ -66,7 +66,7 @@ checks its complete result. Failures must produce the expected diagnostic. Both export-name availability (without forcing programs) and complete-result observation are timed. Timing runs are outside Nix builds, never threshold gates. Each sample starts a fresh process. Wasmtime caching is disabled; Node/V8 may -perform tiered compilation during execution. This is not a warm-session benchmark. +perform tiered compilation during execution. `prepare-compiler.mjs` authenticates the original site compiler bundle against `compiler.json` and the goldens, validates it with the site's bundle decoder, then @@ -99,8 +99,8 @@ See the separate browser reactor below. ## Persistent browser reactor -The separate opt-in target provides an isolated static test page and Worker, -**not a selectable backend in the published demo UI**: +The opt-in target provides an isolated static test page and Worker. The demo UI +uses a separate integration of the same Haskell runtime: ```sh nix build .#haskellWasmBrowser --out-link /tmp/tricu-haskell-wasm-browser @@ -194,36 +194,35 @@ arguments, and 16 MiB framed input/response bytes. Typed inputs use the site's literal validator, including its nesting/item/text bounds. Legacy ternary input is capped at 50,000 digits per argument. Preview permits 1–256 emitted nodes; complete snapshots permit up to 100,000, and refuse partial export. Those limits -do not bound the reduction needed to expose any one node: a divergent reduction -still needs explicit Worker termination. Shared export retains the site's +bound transport observations. Exposing a node can require divergent reduction, +which needs explicit Worker termination. Shared export retains the site's 32 MiB expanded / 128 MiB DAG file limits. The RTS receives a 256 MiB heap and -8 MiB Haskell-stack budget; the linker caps linear memory at 512 MiB. These are -not a browser-process memory cap. Reported `linearBytes` includes WASM linear -memory only, excluding JS buffers, compiled machine code and browser overhead. -Reset/GC can reuse memory but WebAssembly memory does not shrink. The plateau -assertion covers this finite workload, not arbitrary programs. +8 MiB Haskell-stack budget; the linker caps linear memory at 512 MiB. Reported +`linearBytes` counts WASM linear memory. Browser-process usage additionally +includes JS buffers, compiled machine code and browser overhead. +Reset/GC reuses allocated memory; WebAssembly retains its linear-memory capacity. +The plateau assertion covers this finite workload. The tracked checks have passed on Chromium 148 and Firefox 150.0.2 on Linux. -This does not establish support for earlier releases, other operating systems, -or Safari. Fresh-Worker startup samples use localhost and may benefit from -browser caches; they are not Internet cold-download measurements. Browser timer -precision differs: a reported zero for a short transfer/observation is below the -clock's resolution, not proof of zero cost. The [site adapter](../demo-site/README.md#runtime-selection) has separate -real-page integration tests; this isolated suite does not exercise that UI. -Restoration of definitions after termination is not supported. Prepared compiler +Other releases and platforms require separate validation. Fresh-Worker startup +samples use localhost and may benefit from browser caches. Browser timer precision +differs: a reported zero for a short transfer/observation means the duration is +below the clock's resolution. The [site adapter](../demo-site/README.md#runtime-selection) +has separate real-page integration tests. After termination, initialize a new +Worker and reload definitions. Prepared compiler DAG/environment bytes are authenticated against hashes embedded in the built Worker assets; their original compiler provenance is verified during Nix preparation. Ordinary package/environment bytes instead pass the site's SHA-256 and bundle decoder inside the Worker before conversion to the checked internal -DAG. The Haskell core does not implement a second bundle parser. +DAG. ### Worker operations `browser-client.mjs` builds to `client.mjs`; its `CompilerWorker` loads the adjacent `worker.mjs`. Both paths must come from the Nix browser output. Successful replies carry UTF-8 `text`, `milliseconds`, `roundtripMs` and actual `linearBytes`. Except -for the legacy text operations and compilation names, structured results are JSON -in `text`. These are private adapter operations, not yet the published site's UI +for plain-text operations and compilation names, structured results are JSON +in `text`. These are private adapter operations, distinct from the published site's UI protocol: | Operation | Inputs / result | @@ -236,12 +235,12 @@ protocol: | `evaluate` | `name`, typed `arguments`, `output`; returns the site's `ValueView`. | | `preview` / `snapshot` | `name`, typed `arguments`, optional `limit`; returns `{root,nodes,aliases,truncated}`. | | `export` | `name`, `format` (`ternary`, `tricu`, `triangle`, `dag`); returns the shared exporter's `TreeFile`. Stored bundles are exported without evaluation; compiled definitions require a complete snapshot. | -| `io-start` | `name`, typed `arguments`; starts the resumable action machine. | -| `io-resume` | `token`, optional line `value`; consumes exactly the current event token. | -| `io-view` / `io-snapshot` | Decode/inspect the completed IO result without moving a continuation into JS. | -| `io-cancel` | Drop the pending/completed interaction, retaining definitions and compiler. | -| `reset` | Drop definitions and IO state, collect, retain compiler. | -| `collect` | GC while retaining definitions, compiler and any IO continuation/result. | +| `io-start` / `io-definition-start` | Select a canonical v2 program with `selectIO:true`; return its run identity without executing terminal work. | +| `io-v2-session-turn` | Run-bound `poll`, `reply` or `stop`; advances the structured session and returns cloneable events/outcome. | +| `io-v2-public-view` | Decode a completed result for the selected run. | +| `io-v2-public-clear` | Release an unstarted or settled run while retaining definitions. Not an emergency cancellation operation. | +| `reset` | Drop idle definitions and collect, retaining the compiler. | +| `collect` | Explicit idle collection; active sessions retain their own tree roots. | | `stats` | Actual WASM `linearMemoryBytes`. | Compilation environment semantics are explicit: omitted means the retained @@ -250,26 +249,29 @@ named exports from an authenticated environment bundle. Missing names, duplicate names and wrong hashes are errors. The shared site host owns profile selection and checks its compiler pin against ready's `compilerHash`. -### Resumable browser IO - -`BrowserRuntime.hs` handles only `pure`, `bind`, `abortIO`, `putStr`, and `getLine`. -The native `IODriver` (filesystem, networking, threads, etc.) is not pulled in. -The machine returns `before`, `read`, `write`, `after`, or `done` events with -monotonically increasing session-local tokens. JS awaits the `before`/step hook -before acknowledging it; a later event requests the actual read/write effect. -`after` includes step/name/value trace fields without forcing arbitrary values. -On `done`, the retained result is available through `io-view`/`io-snapshot`. -No Haskell FFI call remains blocked while the user is entering a line. - -An adapter maps these events to the shared IO host's before/after/write/read/ -running callbacks. Page request IDs are independent of internal resume tokens. -It must cancel the interaction on terminal driver errors; an invalid line or -stale token leaves the pending state intact so a caller may retry. `io-cancel` -also permits cancelling a paused interaction without destroying compiled code. -Loading/compiling while an interaction is pending is rejected; `reset` or Worker -disposal also cancels it. There are no automatic effects or default terminal -fallbacks. Bounds match the site: 128 actions, 256-scalar single-line input with -no CR/LF, 4,096-scalar decoded strings and 16,384 total output scalars. +### Structured browser IO + +`BrowserTrees.hs` retains native trees while the shared v2 scheduler interprets +core/task and terminal operations. `BrowserRuntime.hs` handles tree observation +and codecs. Filesystem, networking and process facilities require native hosts. + +Terminal events and replies carry run identities and event IDs. Wrong-kind, +stale and malformed replies do not consume a pending operation. Input is an +explicit line of at most 256 Unicode scalars (no CR/LF or unpaired surrogates), +with separate EOF. Output is byte-oriented with lossless views. Step mode pauses +before scheduler turns. + +Stop requests structured cancellation and waits for physical settlement before +releasing the run; definitions remain reusable. Emergency Worker disposal is +separate: it discards the runtime and does not fabricate cleanup acknowledgement. +There is no hard cancellation deadline for a non-returning native reduction. +Loading/compiling while a session is active is rejected. No FFI call remains +blocked merely waiting for terminal input. + +Bounds include 65,536 actions, 4,096-byte chunks and 16,384 attempted output bytes; +heap and reduction-time limits require separate controls. The Chromium and Firefox parity +probes check full native goldens, stale identities, failure laziness, structured +cancellation and reuse. Current framing accepts only IO version 2, without retry. Upstream references: diff --git a/ext/haskell-wasm/Reactor.hs b/ext/haskell-wasm/Reactor.hs @@ -4,6 +4,7 @@ module Reactor where import qualified CompilerProbe as C import qualified BrowserRuntime as R +import qualified BrowserTrees as BT import qualified BrowserModules as M import Data.Array (Array, (!)) import Text.Read (readMaybe) @@ -99,7 +100,6 @@ compileWith compiler env source = do writeIORef current defs writeIORef expressionBindings defs writeIORef pendingModule Nothing - writeIORef ioResult Nothing observe :: Ptr () -> CInt -> IO CInt observe ptr len = guardRequest $ do @@ -121,8 +121,6 @@ reset = guardRequest $ do writeIORef current [] writeIORef expressionBindings [] writeIORef pendingModule Nothing - writeIORef interaction Nothing - writeIORef ioResult Nothing writeIORef response BS.empty performGC reply "ok\nreset" @@ -141,32 +139,18 @@ responseCopy ptr capacity = do pure (fromIntegral (BS.length bytes)) -{-# NOINLINE interaction #-} -interaction :: IORef (Maybe (Integer,R.Pending)) -interaction = unsafePerformIO (newIORef Nothing) -{-# NOINLINE nextToken #-} -nextToken :: IORef Integer -nextToken = unsafePerformIO (newIORef 0) -{-# NOINLINE ioResult #-} -ioResult :: IORef (Maybe T) -ioResult = unsafePerformIO (newIORef Nothing) +-- Only protocol 2 may allocate a private tree run. Framing rejection leaves +-- definitions intact and never evaluates the computation or calls the host. +beginSelectedIO :: T -> IO () +beginSelectedIO term = do + (_,action)<-R.ioEnvelope term + handles<-BT.begin action + reply("ok\n"++R.json(R.O [("kind",R.S "v2"),("tree",handles)])) requireIdle :: IO () requireIdle = do - active<-readIORef interaction - case active of Nothing->pure ();Just _->fail "IO interaction is pending" - -emitEvent :: R.Pending -> IO () -emitEvent pending=do - token<-(+1) <$> readIORef nextToken - value<-R.event token pending - reply ("ok\n"++R.json value) - writeIORef nextToken token - case pending of - R.Pending (R.Done value)->do - writeIORef ioResult (Just value) - writeIORef interaction Nothing - _->writeIORef interaction (Just(token,pending)) + treeActive<-BT.active + unless(not treeActive)(fail "private tree run is pending") select :: BS.ByteString -> BS.ByteString -> [BS.ByteString] -> IO T select nameBytes dag roots=do @@ -214,7 +198,6 @@ request ptr len = guardRequest $ do writeIORef current [] writeIORef expressionBindings [] writeIORef pendingModule Nothing - writeIORef ioResult Nothing initialized<-readIORef context case initialized of Nothing->fail "compiler not initialized"; Just _->pure () library<-readIORef compilerLibrary @@ -253,7 +236,6 @@ request ptr len = guardRequest $ do writeIORef current [] writeIORef expressionBindings [] writeIORef pendingModule Nothing - writeIORef ioResult Nothing reply "ok\naborted" ("compile",sourceBytes:dag:entries)->do requireIdle @@ -285,9 +267,7 @@ request ptr len = guardRequest $ do value<-R.view kind term reply("ok\n"++R.json value) "io"->do - phase<-R.beginIO term - emitEvent(R.Pending phase) - writeIORef ioResult Nothing + beginSelectedIO term _->fail "invalid expression mode" ("load",dag:entries)->do requireIdle @@ -298,7 +278,6 @@ request ptr len = guardRequest $ do writeIORef current defs writeIORef expressionBindings defs writeIORef pendingModule Nothing - writeIORef ioResult Nothing ("inspect-definition",[nameBytes,codec])->do requireIdle name<-R.utf8 nameBytes @@ -333,40 +312,25 @@ request ptr len = guardRequest $ do name<-R.utf8 nameBytes defs<-readIORef expressionBindings term<-maybe (fail "missing definition") pure (lookup name defs) - phase<-R.beginIO term - emitEvent(R.Pending phase) - writeIORef ioResult Nothing - ("io-start",name:dag:roots)->do + beginSelectedIO term + ("io-v2-tree-start",name:dag:roots)->do requireIdle term<-select name dag roots - phase<-R.beginIO term - emitEvent(R.Pending phase) - writeIORef ioResult Nothing - ("io-resume",tokenBytes:valueBytes:[])->do - tokenText<-R.utf8 tokenBytes - supplied<-maybe (fail "invalid resume token") pure (readMaybe tokenText) - saved<-readIORef interaction - (token,pending)<-maybe (fail "no IO interaction") pure saved - unless(supplied==token)(fail "stale IO resume token") - value<-R.utf8 valueBytes - next<-R.resumeIO pending value - emitEvent next - ("io-cancel",[])->do - writeIORef interaction Nothing - writeIORef ioResult Nothing - reply("ok\n"++R.json(R.O[("kind",R.S "cancelled")])) - ("io-snapshot",[limit])->do - saved<-readIORef ioResult - result<-maybe (fail "no completed IO result") pure saved - size<-R.index 100001 limit - value<-R.preview size result + value<-BT.begin term reply("ok\n"++R.json value) - ("io-view",[codec])->do - saved<-readIORef ioResult - result<-maybe (fail "no completed IO result") pure saved - kind<-R.utf8 codec - value<-R.view kind result + ("io-v2-tree",fields)->do + decoded<-mapM R.utf8 fields + value<-BT.command decoded + reply("ok\n"++R.json value) + ("io-v2-header",name:dag:roots)->do + requireIdle + term<-select name dag roots + value<-R.ioV2Header term reply("ok\n"++R.json value) + ("io-start",name:dag:roots)->do + requireIdle + term<-select name dag roots + beginSelectedIO term _->fail "invalid operation fields" initializeEmpty :: IO CInt diff --git a/ext/haskell-wasm/browser-client.mjs b/ext/haskell-wasm/browser-client.mjs @@ -3,6 +3,7 @@ export class CompilerWorker { constructor(){ this.worker=new Worker(new URL('./worker.mjs',import.meta.url),{type:'module'}); + this.lifetime=new AbortController();this.signal=this.lifetime.signal; this.next=0;this.pending=null;this.disposed=false; this.worker.onmessage=({data})=>{ const pending=this.pending;if(!pending||pending.id!==data.id)return; @@ -32,7 +33,7 @@ export class CompilerWorker { } dispose(reason='Worker terminated'){ if(this.disposed)return; - this.disposed=true;this.worker.terminate(); + this.disposed=true;this.worker.terminate();this.lifetime.abort(Error(reason)); if(this.pending){clearTimeout(this.pending.timer);this.pending.reject(Error(reason));this.pending=null;} } } diff --git a/ext/haskell-wasm/browser-parity.mjs b/ext/haskell-wasm/browser-parity.mjs @@ -1,5 +1,8 @@ import {CompilerWorker} from './client.mjs'; -import {decodeBundle} from './site/bundle.js'; +import {drive} from './site/io-v2-driver.js'; +import {Terminal} from './site/io-v2-terminal.js'; +import {treeResult} from './site/tree-result.js'; +import {terminalHost} from './terminal-host.mjs'; import {snapshotBundle,exportedTree} from './bridge.mjs'; const assert=(value,message)=>{if(!value)throw Error(message);}; const equal=(a,b,message)=>assert(JSON.stringify(a)===JSON.stringify(b),message+': '+JSON.stringify(a).slice(0,200)); @@ -14,27 +17,19 @@ export async function runParity(){ const names=await request({type:'load',bytes,hash:index[name]}); return {bytes,names}; }; - async function runIO(name,args,input){ - let event=await request({type:'io-start',name,arguments:args}),terminal='',steps=0; - const trace=[];let awaitingAfter=null; - while(event.kind!=='done'){ - assert(++steps<600,'IO event loop failed to finish'); - trace.push(event.kind+':'+(event.name??'')); - if(event.kind==='before'){assert(awaitingAfter===null,'Missing after trace');awaitingAfter={step:event.step,name:event.name};} - if(event.kind==='after'){assert(awaitingAfter&&event.step===awaitingAfter.step&&event.name===awaitingAfter.name&&typeof event.value==='string','After trace contract');awaitingAfter=null;} - if(event.kind==='write')terminal+=event.text; - const old=event.token; - if(event.kind==='read'){ - await rejects(()=>request({type:'io-resume',token:old,value:'bad\nline'}),/one bounded line/); - await w.request({type:'collect'}); // Paused continuations survive a major GC. - } - event=await request({type:'io-resume',token:old,value:event.kind==='read'?(input??''):''}); - await rejects(()=>request({type:'io-resume',token:old,value:''}),/stale|no IO interaction/); - } - const snapshot=await request({type:'io-snapshot'}); - const tree=exportedTree(snapshotBundle(snapshot,'result'),'result','ternary').text; - report.ioRuns++; - return {terminal,tree,trace}; + async function runIO(name,args,input,worker=w){ + const call=async m=>JSON.parse((await worker.request(m)).text); + const selected=await call({type:'io-start',selectIO:true,name,arguments:args}); + assert(selected.kind==='v2','Only v2 selection'); + const host=terminalHost(Terminal,input),run=selected.run; + try { + const value=await drive({run,signal:worker.signal,discard:()=>worker.dispose(), + turn:command=>call({type:'io-v2-session-turn',...command}), + view:async()=>treeResult((await call({type:'io-v2-public-view',run,output:'ternary'})).value,'ternary'), + },host); + report.ioRuns++; + return {terminal:host.terminalText,tree:value.value}; + }finally{if(!worker.disposed)await call({type:'io-v2-public-clear',run});} } try{ const ready=await w.request({type:'init',compiler:false}); @@ -91,19 +86,28 @@ export async function runParity(){ assert(stored.extension==='dag'&&stored.bytes>0,'Stored definition export'); } report.checks.push('all curated published packages and Haskell result/terminal goldens'); - for(const [name,pattern] of [['bad-version',/version/],['unsupported-io',/Unsupported browser IO action/],['abort-io',/aborted/],['output-limit',/output limit/],['action-limit',/action limit/]]){ + for(const [name,pattern] of [['bad-version',/version/],['unsupported-io',/IO v2 failed/],['abort-io',/IO v2 failed/],['retired-output',/version/],['retired-actions',/version/]]){ await load(name); await rejects(()=>runIO('root',[],''),pattern); - await request({type:'io-cancel'}); } + await load('version-effect'); + await rejects(()=>request({type:'io-start',selectIO:true,name:'root',arguments:[]}),/version/); + report.checks.push('unsupported and retired IO versions reject before events; no retry'); await load('greet'); - let event=await request({type:'io-start',name:demos.demos.greet.haskellCases[0].export,arguments:[]}); + const selected=await request({type:'io-start',selectIO:true,name:demos.demos.greet.haskellCases[0].export,arguments:[]}); await rejects(()=>load('identity'),/pending/); - await request({type:'io-cancel'}); - await rejects(()=>request({type:'io-resume',token:event.token,value:''}),/no IO interaction/); + await rejects(()=>request({type:'io-v2-session-turn',run:'0',kind:'poll'}),/Stale/); + const host=terminalHost(Terminal,null),open=host.v2.open; + host.v2.open=(run,command)=>{open(run,command);command({run,kind:'stop'});}; + await rejects(()=>drive({run:selected.run,signal:w.signal,discard:()=>w.dispose(), + turn:command=>request({type:'io-v2-session-turn',...command}), + view:async()=>{throw Error('Cancelled result was decoded');}, + },host),/cancelled/); + await request({type:'io-v2-public-clear',run:selected.run}); + await rejects(()=>request({type:'io-v2-session-turn',run:selected.run,kind:'poll'}),/Stale/); await load('identity'); equal((await request({type:'evaluate',name:id,arguments:[{type:'string',value:'recovered'}],output:'string'})).value,'recovered','IO errors poisoned runtime'); - report.checks.push('IO envelope/version/action/output limits; abort without forcing huge payload; stale-token rejection; GC while awaiting input; explicit cancellation and recovery'); + report.checks.push('v2 failed diagnostics; stale run rejection; acknowledged cancellation and recovery; legacy limit fixtures reject inertly'); }finally{w.dispose();} // A compiler-capable session exercises all profiles without package loading // replacing the retained compiler/environment. @@ -126,15 +130,12 @@ export async function runParity(){ await rejects(()=>c.request({type:'compile',source:'x = t',environment:{bytes:support,hash:'0'.repeat(64),names:profile.names}}),/SHA-256 mismatch/); const previous=Object.values(demos.demos).at(-1).haskellCases[0].export; await rejects(()=>c.request({type:'observe',name:previous,arguments:[]}),/missing export/); - await c.request({type:'compile',source:'q x = x x\nlazy = t (q q) t\naborted = t "tricuIO" (t 1 (t 2 (q q)))'},null); + await c.request({type:'compile',source:'q x = x x\nlazy = t (q q) t\naborted = t "tricuIO" (t 2 (t 2 (q q)))'},null); const partial=JSON.parse((await c.request({type:'preview',name:'lazy',limit:1},2000)).text); assert(partial.nodes.length===1&&partial.truncated,'Preview forced excluded divergent child'); - const before=JSON.parse((await c.request({type:'io-start',name:'aborted'},2000)).text); - assert(before.kind==='before'&&before.name==='abortIO','Abort before event'); - await rejects(()=>c.request({type:'io-resume',token:before.token,value:''},2000),/aborted/); - await c.request({type:'io-cancel'}); - report.checks.push('all ten native demo sources compile with explicit imports; compiled graph-preserving export; environment authentication; ready compiler pin'); - report.checks.push('preview and abort trace do not force excluded divergent payloads; null/Infinity disable request timer'); + await rejects(()=>runIO('aborted',[],'',c),/IO v2 failed/); + report.checks.push('all native demo sources compile with explicit imports; compiled graph-preserving export; environment authentication; ready compiler pin'); + report.checks.push('preview and terminal failure do not force excluded divergent payloads; null/Infinity disable request timer'); }finally{c.dispose();} return report; } diff --git a/ext/haskell-wasm/browser-worker.mjs b/ext/haskell-wasm/browser-worker.mjs @@ -1,4 +1,6 @@ import {WASI,File,OpenFile,ConsoleStdout,PreopenDirectory} from './shim/index.js'; +import {Session} from './site/io-v2-session.js'; +import {constructors,runPrivate} from './site/io-v2-private.js'; import pins,{compilerHash} from './pins.mjs'; import {moduleDependencies} from './site/module-stage.js'; import {fetchBytes} from './site/bundle.js'; @@ -6,6 +8,7 @@ import {environmentProfile} from './site/packages.js'; import {frame,authenticatedBundle,loadFields,application,evaluation,snapshotFields,snapshotBundle,exportedTree,environmentFields,emptyEnvironment,dagBytes} from './bridge.mjs'; let instance,booted=false,dead=false,stderr="",storedBundle=null,busy=false,compilerReady=false; const dataFiles=new Map(); +let privateRun=0n,privateSession=null; const encoder=new TextEncoder(),decoder=new TextDecoder('utf-8',{fatal:true}); function scalarText(s,limit){ if(typeof s!=='string')throw Error('Expected string'); @@ -78,12 +81,48 @@ onmessage=async({data:m})=>{ phases={data_ms:fetched-begin,wasm_fetch_compile_ms:compiled-fetched,instantiate_rts_ms:initialized-compiled,load_ms:performance.now()-initialized}; }else{ if(!instance)throw Error('Not initialized'); + if(privateSession&&!['io-v2-session-turn','io-v2-public-view','io-v2-public-clear'].includes(m.type))await safe(()=>{throw Error('Private v2 session is pending');}); if(m.type==='compiler-init'){ if(!compilerReady){ const files=await safe(()=>Promise.all(Object.keys(pins).map(async name=>[name,await checkedFile(name)]))); for(const [name,file] of files)dataFiles.set(name,file); bytes=call('tricu_initialize');compilerReady=true; }else bytes=encoder.encode('ready').buffer; + }else if(m.type==='io-v2-session-start'){ + const args=await safe(()=>application(m.name,m.arguments)); + if(privateRun===0xffffffffffffffffn)throw Error('Run identity exhausted'); + const start=JSON.parse(decoder.decode(operation(['io-v2-tree-start',...args]))); + const invoke=(name,...fields)=>JSON.parse(decoder.decode(operation(['io-v2-tree',name,...fields.map(String)]))); + try{ + const tree=constructors({leaf:start.leaf,observe:h=>invoke('observe',h),stem:h=>invoke('stem',h),fork:(a,b)=>invoke('fork',a,b),app:(a,b)=>invoke('app',a,b)}); + const run=String(++privateRun),session=new Session(tree,start.action,run,roots=>invoke('retain',...new Set(roots))); + privateSession={session,clear:()=>invoke('clear')}; + const report=await session.turn({run,kind:'poll'});bytes=encode(report); + if(report.done){privateSession.clear();privateSession=null;} + }catch(error){privateSession=null;try{invoke('clear');}catch{}throw error;} + }else if(m.type==='io-v2-public-view'){ + await safe(()=>{if(!privateSession?.public||m.run!==privateSession.session.run||privateSession.session.core.result?.kind!=='completed')throw Error('No completed v2 result');}); + bytes=encode(privateSession.view(m.output)); + }else if(m.type==='io-v2-public-clear'){ + await safe(()=>{if(!privateSession?.public||m.run!==privateSession.session.run||privateSession.started&&!privateSession.session.core.result)throw Error('V2 cleanup is pending');}); + privateSession.clear();privateSession=null;bytes=encode({}); + }else if(m.type==='io-v2-session-turn'){ + await safe(()=>{if(!privateSession||m.run!==privateSession.session.run)throw Error('Stale session run');if(!['poll','reply','stop'].includes(m.kind))throw Error('Invalid session command');}); + privateSession.started=true; + const report=await privateSession.session.turn({run:m.run,kind:m.kind,reply:m.reply});bytes=encode(report); + if(report.done&&!privateSession.public){privateSession.clear();privateSession=null;} + }else if(m.type==='io-v2-core'){ + const args=await safe(()=>application(m.name,m.arguments)); + if(privateRun===0xffffffffffffffffn)throw Error('Run identity exhausted'); + const start=JSON.parse(decoder.decode(operation(['io-v2-tree-start',...args]))); + const invoke=(name,...fields)=>JSON.parse(decoder.decode(operation(['io-v2-tree',name,...fields.map(String)]))); + try{ + const tree=constructors({leaf:start.leaf,observe:h=>invoke('observe',h),stem:h=>invoke('stem',h),fork:(a,b)=>invoke('fork',a,b),app:(a,b)=>invoke('app',a,b)}); + bytes=encode(await runPrivate(tree,start.action,++privateRun,roots=>invoke('retain',...new Set(roots)))); + }finally{invoke('clear');} + }else if(m.type==='io-v2-header'){ + const args=await safe(()=>application(m.name,m.arguments)); + bytes=operation(['io-v2-header',...args]); }else if(m.type==='expression'){ await safe(()=>{scalarText(m.source,16384);if(typeof m.name!=='string'||!/^browserExpression[0-9]+$/.test(m.name)||!['pure','io'].includes(m.mode))throw Error('Invalid expression request');}); bytes=operation(['expression',m.name,m.source,m.mode,m.output]); @@ -160,17 +199,8 @@ onmessage=async({data:m})=>{ }else if(m.type==='io-start'){ const args=await safe(()=>application(m.name,m.arguments)); bytes=operation(['io-start',...args]); - }else if(m.type==='io-resume'){ - await safe(()=>{if(!Number.isSafeInteger(m.token)||m.token<1)throw Error('Invalid IO token');scalarText(m.value??'',256);}); - bytes=operation(['io-resume',String(m.token),m.value??'']); }else if(m.type==='stats'){ bytes=encode({linearMemoryBytes:instance.exports.memory.buffer.byteLength}); - }else if(m.type==='io-cancel'){ - bytes=operation(['io-cancel']); - }else if(m.type==='io-snapshot'){ - bytes=operation(['io-snapshot',String(m.limit??100000)]); - }else if(m.type==='io-view'){ - bytes=operation(['io-view',String(m.output)]); }else if(m.type==='observe'){ // The private wire uses newline-separated names/ternary arguments. if(typeof m.name!=='string'||!m.name||m.name.length>128||/[\r\n]/.test(m.name)||!Array.isArray(m.arguments)||m.arguments.length>16||m.arguments.some(x=>typeof x!=='string'||x.length>50000||!/^[012]+$/.test(x))){const e=Error('Invalid observation request');e.recoverable=true;throw e;} @@ -178,6 +208,22 @@ onmessage=async({data:m})=>{ }else if(['dump','reset','collect'].includes(m.type)){bytes=call('tricu_'+m.type);if(m.type==='reset')storedBundle=null;} else{const e=Error('Unknown operation');e.recoverable=true;throw e;} } + if(['io-start','io-definition-start'].includes(m.type)||m.type==='expression'&&m.mode==='io'){ + const selected=JSON.parse(decoder.decode(bytes)); + if(selected.kind==='v2'){ + if(privateRun===0xffffffffffffffffn)throw Error('Run identity exhausted'); + const operation=fields=>call('tricu_request',frame(fields)); + const invoke=(name,...fields)=>JSON.parse(decoder.decode(operation(['io-v2-tree',name,...fields.map(String)]))); + if(m.selectIO!==true){invoke('clear');const error=Error('Unsupported tricuIO version for legacy worker client');error.recoverable=true;throw error;} + try{ + const start=selected.tree,run=String(++privateRun); + const tree=constructors({leaf:start.leaf,observe:h=>invoke('observe',h),stem:h=>invoke('stem',h),fork:(a,b)=>invoke('fork',a,b),app:(a,b)=>invoke('app',a,b)}); + const session=new Session(tree,start.action,run,roots=>invoke('retain',...new Set(roots)),{summarize:false,steps:m.v2Step===true?1:128,backend:'haskell-wasm'}); + privateSession={session,public:true,started:false,clear:()=>invoke('clear'),view:output=>invoke('view',session.core.result.value,output)}; + bytes=encoder.encode(JSON.stringify({kind:'v2',run})).buffer; + }catch(error){privateSession=null;try{invoke('clear');}catch{}throw error;} + } + } postMessage({id:m.id,ok:true,bytes,milliseconds:performance.now()-begin,linearBytes:instance.exports.memory.buffer.byteLength,phases,scopeNames,compilerHash:['init','compiler-init'].includes(m.type)?(compilerReady?compilerHash:null):undefined},[bytes]); }catch(error){ const fatal=m.type==='init'||!error.recoverable; diff --git a/ext/haskell-wasm/browser.nix b/ext/haskell-wasm/browser.nix @@ -10,6 +10,7 @@ pkgs.runCommand "tricu-haskell-wasm-browser" { cp ${shimSource}/LICENSE-* $out/shim/ cp ${./CompilerProbe.hs} CompilerProbe.hs cp ${./BrowserRuntime.hs} BrowserRuntime.hs + cp ${./BrowserTrees.hs} BrowserTrees.hs cp ${./BrowserModules.hs} BrowserModules.hs wasm32-wasi-ghc -O2 -rtsopts -i${../../src} -i. -outputdir objects \ ${./Reactor.hs} ${./reactor-init.c} -no-hs-main -optl-mexec-model=reactor \ @@ -27,6 +28,7 @@ pkgs.runCommand "tricu-haskell-wasm-browser" { cp ${./browser-worker.mjs} $out/worker.mjs cp ${./browser-client.mjs} $out/client.mjs node ${./prepare-parity.mjs} ${../demo-site} $out/parity ${../../test/fixtures} + cp ${../demo-site/test/terminal-host.mjs} $out/terminal-host.mjs cp ${./browser-parity.mjs} $out/parity.mjs cp ${./browser-page.mjs} $out/page.mjs cp -r ${probe}/compiler-data $out/data diff --git a/ext/haskell-wasm/prepare-parity.mjs b/ext/haskell-wasm/prepare-parity.mjs @@ -24,15 +24,16 @@ function fixture(name,build){ return save(name,makeBundle(nodes,[['root',result]])); } const io=(x,action,version=1)=>x.fork(x.text('tricuIO'),x.fork(x.num(version),action)); -await fixture('bad-version',x=>io(x,x.action(0,x.leaf),2)); -await fixture('unsupported-io',x=>io(x,x.action(99,x.leaf))); -await fixture('abort-io',x=>{let t=x.leaf;for(let i=0;i<40;i++)t=x.fork(t,t);return io(x,x.action(2,t));}); -await fixture('output-limit',x=>{ +await fixture('bad-version',x=>io(x,x.action(0,x.leaf),3)); +await fixture('version-effect',x=>io(x,x.action(10,x.text('MUST NOT WRITE')),3)); +await fixture('unsupported-io',x=>io(x,x.action(99,x.leaf),2)); +await fixture('abort-io',x=>{let t=x.leaf;for(let i=0;i<40;i++)t=x.fork(t,t);return io(x,x.action(2,t),2);}); +await fixture('retired-output',x=>{ let action=x.action(0,x.leaf);const output=x.action(10,x.text('x'.repeat(4096))); for(let i=0;i<5;i++)action=x.action(1,x.fork(output,x.constant(action))); return io(x,action); }); -await fixture('action-limit',x=>{ +await fixture('retired-actions',x=>{ let action=x.action(0,x.leaf); for(let i=0;i<65;i++)action=x.action(1,x.fork(x.action(0,x.leaf),x.constant(action))); return io(x,action); diff --git a/ext/js/src/lib.js b/ext/js/src/lib.js @@ -66,7 +66,7 @@ function getLib() { export function init(libPath) { if (libPath) loadLib(libPath); const lib = getLib(); - if (lib.func('uint32_t arb_abi_version(void)')() !== 2) throw new Error('Arboricx ABI v2 required'); + if (lib.func('uint32_t arb_abi_version(void)')() !== 3) throw new Error('Arboricx ABI v3 required'); const ctx = lib.func('arb_ctx_t *arboricx_init(void)')(); if (!ctx) throw new Error('arboricx_init failed'); return ctx; diff --git a/ext/php/src/ffi.php b/ext/php/src/ffi.php @@ -42,8 +42,8 @@ final class ArboricxFFI $header = preg_replace('/\n\s*\n+/', "\n", $header); self::$ffi = \FFI::cdef($header, $libPath); - if (self::$ffi->arb_abi_version() !== 2) { - throw new \RuntimeException('Arboricx ABI v2 required'); + if (self::$ffi->arb_abi_version() !== 3) { + throw new \RuntimeException('Arboricx ABI v3 required'); } } diff --git a/ext/wasm/check.mjs b/ext/wasm/check.mjs @@ -29,7 +29,7 @@ try { const dead = g.stem(g.leaf); g.release(dead); assert.throws(() => g.force(dead), /status 1/); assert.throws(() => g.load(new Uint8Array([1,2,3])), /status 5/); - assert.throws(() => g.handle('arb_run_io', g.leaf, 0), /status 6/); + assert.throws(() => g.handle('arb_run_io_v2', g.leaf), /status 6/); const bytes = Uint8Array.from([0,127,128,255]); assert.deepEqual(g.toBytes(g.ofBytes(bytes)),bytes); const id = g.load(await readFile('test/fixtures/id.arboricx')); diff --git a/ext/wasm/runtime.mjs b/ext/wasm/runtime.mjs @@ -10,7 +10,7 @@ export class WasmRuntime { } constructor(instance) { this.e = instance.exports; - if (this.e.arb_abi_version() !== 2) throw new Error('ABI v2 required'); + if (this.e.arb_abi_version() !== 3) throw new Error('ABI v3 required'); this.ctx = this.e.arboricx_init(); if (!this.ctx) throw new Error('WASM initialization failed'); this.scratch = this.e.arb_host_alloc(16); @@ -72,6 +72,16 @@ export class WasmRuntime { try { return await fn(); } finally { this.scopes.pop(); for (const h of handles) this.release(h); } } + // Interpreter turn boundary inside an exclusive scopeAsync. Validate every + // live root first, then retire only handles owned by this scope. Borrowed + // caller roots remain caller-owned; no stale root can partially sweep state. + sweepScope(roots) { + const scope=this.scopes.at(-1);if(!scope)throw Error('Root sweep requires an owned scope'); + const live=new Set(roots); + for(const h of live)if(!this.owners.has(h))throw Error('Stale interpreter root'); + for(const h of scope)if(!live.has(h))this.release(h); + this.collect(); + } retain(x) { return this.handle('arb_retain', x); } collect() { this.call('arb_collect'); } stats() { diff --git a/ext/wasm/site-session.mjs b/ext/wasm/site-session.mjs @@ -2,13 +2,13 @@ // native owned handles stay here. Shared helpers validate/authenticate inputs. import {WasmRuntime} from './runtime.mjs'; export async function create({base,compiler}) { - const {bundle,codec,comp,contracts,packages,expressions,treeResults,modules,moduleBundle}=await import(new URL('dist/zig-helpers.js',base).href); + const {bundle,codec,comp,contracts,packages,expressions,treeResults,modules,moduleBundle,ioV2Private,ioV2Profile,ioV2Session,ioV2Driver}=await import(new URL('dist/zig-helpers.js',base).href); const bytes=await bundle.fetchBytes(new URL('runtime/zig/arboricx.wasm',base)); const g=await WasmRuntime.create(bytes,{unbounded:true}); // The shared DAG reader's tick is JS accounting, not a native work ceiling. g.tick=()=>{}; let closed=false,busy=false,definitions=new Map(),library=new Map(),expressionScope=null,compilerHash; - let cancel;const cancelled=new Promise((_,reject)=>{cancel=reject;});cancelled.catch(()=>{}); + let nextRun=0n;const lifetime=new AbortController(); const alive=()=>{if(closed)throw Error('Disposed Zig session');}; const cleanup=()=>{try{g.dispose();}catch{/* A poisoned instance is discarded, never reused. */}}; async function exclusive(fn) { @@ -38,31 +38,15 @@ export async function create({base,compiler}) { if(type==='list')return {type,value:codec.readList(g,h,32).map(x=>({...g.preview(x,8),aliases:[]}))}; return codec.view(g,h,type); } - const wait=promise=>Promise.race([promise,cancelled]); - async function io(handle,host) { - const [sentinel,body]=g.pair(handle); - if(g.toText(sentinel,7)!=='tricuIO')throw Error('Missing tricuIO sentinel'); - const [version,initial]=g.pair(body); - if(codec.readNatural(g,version)!==1n)throw Error('Unsupported tricuIO version'); - const frames=[];let action=initial,output=0; - const names=new Map([[0n,'pure'],[1n,'bind'],[2n,'abortIO'],[10n,'putStr'],[11n,'getLine']]); - for(let step=1;step<=128;step++) { - alive();g.collect(); - const [tag,payload]=g.pair(action),code=codec.readNatural(g,tag),name=names.get(code); - if(!name)throw Error(`Unsupported browser IO action ${code}`); - const detail=code===10n?g.toText(payload):'retained value'; - await wait(host.before({step,name,detail,depth:frames.length}));alive(); - if(code===1n){const [left,continuation]=g.pair(payload);frames.push(continuation);action=left;host.after({step,name,value:'retained value'});continue;} - if(code===2n)throw Error('Execution aborted (retained diagnostic; no payload forcing).'); - let value; - if(code===0n)value=payload; - else if(code===10n){output+=Array.from(detail).length;if(output>16384)throw Error('Terminal output limit exceeded');host.write(detail);value=g.leaf;} - else {const line=await wait(host.read());alive();if(/[\r\n]/u.test(line))throw Error('Input must be one line');value=g.ofText(line,256);} - host.after({step,name,value:'retained value'}); - if(!frames.length)return value; - host.running();action=g.app(frames.pop(),value); - } - throw Error('IO action limit exceeded'); + async function io(handle,host,reader=x=>x) { + const tree=ioV2Private.constructors({leaf:g.leaf,observe:g.observe.bind(g),stem:g.stem.bind(g),fork:g.fork.bind(g),app:g.app.bind(g)}); + const selected=ioV2Profile.envelope(tree,handle); + if(!host.v2)throw Error('IO version 2 requires a structured terminal host'); + host.selected?.(2); + if(nextRun===0xffffffffffffffffn)throw Error('Run identity exhausted'); + const run=String(++nextRun), interpreter=new ioV2Session.Session(tree,selected.action,run,roots=>g.sweepScope(roots), + {summarize:false,steps:host.v2.stepping===true?1:128,backend:'zig-wasm'}); + return ioV2Driver.drive({run,signal:lifetime.signal,discard:()=>session.dispose(),turn:command=>{alive();return interpreter.turn(command);},view:async()=>{alive();return reader(interpreter.core.result.value);}},host); } const execute=(name,args,mode,host,reader)=>exclusive(async()=>{ const root=definitions.get(name);if(root===undefined)throw Error('Missing definition'); @@ -70,7 +54,7 @@ export async function create({base,compiler}) { if(!['pure','io'].includes(mode))throw Error('Unsupported execution mode'); try{return await g.scopeAsync(async()=>{ const applied=g.apply(root,args.map(literal)); - return reader(mode==='io'?await io(applied,host):applied); + return mode==='io'?await io(applied,host,reader):reader(applied); });}finally{if(!g.poisoned)g.collect();} }); async function ensureCompiler(){ @@ -157,13 +141,12 @@ export async function create({base,compiler}) { runDefinitionIO(name,output,host){return exclusive(async()=>{ const root=(expressionScope??definitions).get(name);if(root===undefined)throw Error('Missing definition'); output=codec.outputType(output); - try{return await g.scopeAsync(async()=>view(await io(root,host),output));}finally{if(!g.poisoned)g.collect();} + try{return await g.scopeAsync(async()=>io(root,host,h=>view(h,output)));}finally{if(!g.poisoned)g.collect();} });}, probeIO(name){return exclusive(async()=>{ const root=(expressionScope??definitions).get(name);if(root===undefined)throw Error('Missing definition'); try{return g.scope(()=>{ - try{const [sentinel,body]=g.pair(root);if(g.toText(sentinel,7)!=='tricuIO')return false; - const [version]=g.pair(body);return codec.readNatural(g,version)===1n; + try{ioV2Profile.envelope({observe:g.observe.bind(g)},root);return true; }catch(error){if(g.poisoned||error instanceof WebAssembly.RuntimeError)throw error;return false;} });}finally{if(!g.poisoned)g.collect();} });}, @@ -186,7 +169,7 @@ export async function create({base,compiler}) { const values=comp.compiledDefinitions(g,g.apply(library.get(comp.COMPILER_EXPORT),[environment,g.ofText(unit.source,16384)])); if(values.size!==1||!values.has(unit.name))throw Error('Enter one expression, not a source unit'); const result=values.get(unit.name); - return view(mode==='io'?await io(result,host):result,output); + return mode==='io'?await io(result,host,h=>view(h,output)):view(result,output); });}catch(error){throw expressions.expressionDiagnostic(error);}finally{if(!g.poisoned)g.collect();} });}, // Explicit, bounded full observation for independent embedding conformance; @@ -199,7 +182,7 @@ export async function create({base,compiler}) { collect(){alive();try{g.collect();}catch(error){if(g.poisoned)session.dispose();throw error;}}, diagnostics(){alive();return {...g.stats(),linearMemoryBytes:g.e.memory.buffer.byteLength};}, stats(){alive();return {backend:'zig-wasm',linearMemoryBytes:g.e.memory.buffer.byteLength};}, - dispose(){if(closed)return;closed=true;cancel(Error('Disposed Zig session'));if(!busy)cleanup();}, + dispose(){if(closed)return;closed=true;lifetime.abort(Error('Disposed Zig session'));if(!busy)cleanup();}, }; try { if(compiler)await ensureCompiler(); diff --git a/ext/zig/README.md b/ext/zig/README.md @@ -18,10 +18,11 @@ includes independent object audits, program smokes and retention checks. Haskell supplies common artifact transport and packaging. The [WASM adapter](../wasm/README.md) reuses this reducer without native IO. -The [demo site](../demo-site/README.md#runtime-selection) selects it alongside JS -and Haskell, with browser IO implemented by the owning session. Browser work -uses explicit unbounded reducer/Unicode/preview entry points and hard Worker -termination; existing finite-fuel native entry points retain their semantics. +The [demo site](../demo-site/README.md#runtime-selection) selects Zig or Haskell +for native browser execution, with IO implemented by the owning session. The JS +reference graph supports pure probes. Browser work uses unbounded reducer/Unicode/ +preview entry points. Structured Stop waits for IO settlement; emergency disposal +terminates the Worker. Finite-fuel native entry points retain their semantics. ## Basic performance smoke (Linux) @@ -146,51 +147,32 @@ for compiler fixed-point acceptance. ## Indexed tree transport -The IO actions `readIndexedTree` (50), `hashIndexedTree` (51), and -`writeIndexedTreeIfAbsent` (52) implement the -[verified native transport contract](../../docs/storage.md#verified-native-tree-transport). -They use the `arboricx.indexed-tree.v1` domain and Haskell-compatible canonical -bytes. - -The `src/indexed.zig` codec uses iterative traversal, memoizes physical node -identities, and interns exact constructor/child-ID keys. Equal finite trees -produce the same bytes regardless of physical sharing. Serialization observes -the tree's constructor structure. The validated reader is shared with C ABI/CLI -bundle loading and retains DAG sharing. Transport authenticates each object's -domain-separated digest before decoding. - -The IO adapters combine the codec with byte-buffer hashing and native filesystem -operations. Reads require read permission; atomic no-replace publication requires -both read and write permission, checked before demanding the supplied tree. -Writes verify the expected digest before publication and compare the winning -file byte-for-byte, including an existing winner. Atomic link publication and -file/directory syncing use the same primitives as byte writes. A post-publication -failure can leave a valid object visible. tricu chooses paths, creates directories, -and manages manifests, caches and publication order. - -These operations occupy a synchronous scheduler turn and retain complete -serialized buffers in memory. Apply external time and memory limits to the -containing IO program. +The `indexed-tree` v1 facility supplies authenticated read/hash/write/verify +operations under IO protocol 2. The domain remains `arboricx.indexed-tree.v1`; +facility revisions and content domains are not IO protocol versions. -## Source-text IO +The codec uses iterative traversal, memoizes physical identities and interns +constructor/child-ID keys. Equal finite trees produce the same canonical bytes +regardless of physical sharing. Reads authenticate before decoding and preserve +DAG sharing. Native facilities use OS process authority. -`readSourceFile` (37) requires read permission and returns a Result containing -both the exact raw octets and a Unicode scalar list. It preserves Unicode and -newline sequences and rejects malformed UTF-8 and an initial BOM. Paths use -Unicode scalars encoded as UTF-8, with embedded NUL rejected. +Source-owned storage composes expected-digest checks, no-replace publication, +exact winner verification and synchronization. Publication is not rolled back +on a later failure. Serialized buffers and decoded trees can occupy substantial +memory; use external process limits. See the +[storage contract](../../docs/storage.md#verified-native-tree-transport). -`encodeSourceText` (38) is permission-free and returns the exact UTF-8 bytes of a -canonical scalar list. Surrogates, out-of-range values, noncanonical numeral -trees and malformed lists are errors. Encoding U+FEFF is allowed; initial-BOM -rejection belongs to source-file reading. +## Source-text IO -ABI string/binary codecs and `readFile` operate on bytes. Source reads run -synchronously within one scheduler turn. Allocation failures propagate to the -host. +The `utf8` facility encodes/decodes strict Unicode scalar lists and byte chunks. +Source-owned whole-file readers preserve raw octets and decoded text, reject +malformed UTF-8 and an initial BOM, and do not normalize newlines or Unicode. +Compiler policy supplies the source-size budget. Ordinary text encoding permits +U+FEFF; source-file reading owns initial-BOM rejection. -## ABI v2 ownership +## ABI v3 ownership -`include/arboricx.h` defines ABI v2. `arb_abi_version()` returns 2; the JS, PHP and +`include/arboricx.h` defines ABI v3. `arb_abi_version()` returns 3; the JS, PHP and Python test bindings check this version before using handles. * A handle is an opaque 64-bit integer identifying an owned root. Each successful @@ -351,3 +333,248 @@ For a full experiment stopped before a job creates its generation directory, `--continue-experiment` reverifies frozen sources, runtimes and completed generations, preserves the failed launch and original tooling, and starts the pending job fresh. A partially executed generation requires a new experiment. + +## IO protocol boundary + +The native CLI validates canonical `tricuIO` version 2 before demanding its action. +Historical v1 execution requires a retained runtime. V2 implements the structured core and the +compiler/file profile: regular files, path/directory mutations and sync, stages, +SHA-256, strict UTF-8 and authenticated indexed transport. `IO.capabilities` +reports exactly those operations and their restrictions; `IO.inspect` classifies +one header without demanding its arguments. Standard streams, directory cursors, +networking, processes, clocks and context are not implemented in this Zig profile. +Unknown named facilities/revisions return ordinary unsupported Results. + +V2 uses OS process authority and rejects `--unsafe-io` before action demand. Unknown +envelope versions fail without interpreter fallback. Terminal failures/cancellation +fail the CLI without rendering opaque user payloads. Each native run gets a unique, +nonwrapping identity. Synchronous filesystem calls make no prompt-cancellation or +deadline promise. Explicit `text:VALUE` arguments encode Unicode scalars (including +Unicode paths); historical unprefixed argument forms retain byte-string semantics. +Owned-handle **C ABI version3** remains a separate contract, not an IO interpreter. +Pure evaluation is independent of IO-envelope execution, and the freestanding WASM +ABI has no native IO driver; the site's adapter supplies the shared structured v2 core/task/terminal profile. See the [validated program/backend matrix](../../test/io-v2/README.md#validated-programbackend-boundary) +before selecting an execution host or attempting a compiler/default transition. + +`src/io_v2_protocol.zig` is the host-independent foundation for the separate v2 +port: bounded canonical core/header decoding, with temporary GC rooting and opaque +payloads. It does not execute requests, resolve facilities, or enable v2 in the CLI, +C ABI or browser. Its Debug/ReleaseSafe controls run with `tricu-zig-tests`, including +computed headers, excess-element non-demand, divergent payloads, and allocator/GC +failure unwinding. Reducer fuel is explicitly per demand, not a request deadline. + +The separate v2 resource/task state machines and private pure-core probe are also +validated through these test targets. The native CLI uses these state machines through the separate v2 boundary. Resource admission, active leases and asynchronous close claims have +separate lifetimes; cancellation does not detach outstanding host callbacks. Task +results survive only an unconsumed ticket or a claimed wait, not a completion +archive. Lexical scopes share their task identity for join-cycle detection. + +For actual source/handler controls on both Haskell and the private Zig core: + +```sh +nix build .# .#tricu-zig-tests --no-link +ZIG=$(nix path-info .#tricu-zig) +HASKELL=$(nix path-info .#) +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_zig_core.py "$HASKELL/bin/tricu" \ + "$ZIG/bin/io-v2-core-probe" --directory /tmp/zig-v2-core-new +``` + +Use a fresh output directory. The probe supports the structured core only; all +named facilities return ordinary unsupported Results. It rejects v1 rather than +retrying it and never renders terminal diagnostic/reason payloads. It is not a +compiler execution host, native filesystem port or browser adapter. The source +controls include fresh forced 4096-cell payloads in repeated joined/discarded tasks; +the 128-MiB Zig process guard covers those workloads, not arbitrary programs or +future native IO. Unit tests additionally sweep allocation failure and force GC. + +A separate **private** `io-v2-file-probe` exercises synchronous regular-file IO +through that core, including the compiler storage transports described below. +It uses the same facilities/capability catalogue as the native CLI, with extra +private raw-tree argument support for controls. Path opens reject known nonregular types, +use nonblocking/no-controlling-terminal opens, verify the opened descriptor before +truncation, and follow ordinary host symlinks. These checks are backend type +support, not confinement: raced device opens can have effects. Standard streams +are separate and are not implemented by this probe. Synchronous regular-file +calls may delay all tasks and cancellation; they have no hard deadline. + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_zig_files.py "$HASKELL/bin/tricu" \ + "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-files-new +``` + +Controls compare exact file bytes and result trees, including borrowed/transferred +handles, implicit joins, cancellation without rollback, positioned/update/append +operations and malformed/unsupported requests. Native unit controls inject a path +substitution after preflight and close failures after descriptor release, and +sweep allocation failures through real acquisition and core unwind. Host cleanup +error codes have fixed-size storage even when allocator/reducer failure prevents +a tree reply. This synchronous host interface cannot be used for asynchronous +browser input or detached native work: those need an explicit settlement pump. + +The private file probe also implements `path/stat`, `lstat`, `remove-file`, `rename` +and `directory/create`, `remove`, `sync`. Stat/lstat are observations, not pinned +claims about future paths. Rename uses the OS namespace operation, without copy +fallback or rollback. Directory synchronization opens/checks a directory descriptor +with nonblocking and close-on-exec flags, preserves primary failures, and attempts +close exactly once. Directory enumeration is not implemented by this subset. + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_zig_paths.py "$HASKELL/bin/tricu" \ + "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-paths-new +``` + +The private host implements scoped `digest/open`, `update`, `finish` for SHA-256. +It retains fixed-size hash state, not prior chunks; finish consumes the active +state. Algorithm rejection preserves at most128 Unicode scalars of diagnostic +text. Typed backend tags keep digest handles distinct from file/stream operations. + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_zig_digest.py "$HASKELL/bin/tricu" \ + "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-digest-new +``` + +Private `stage/open`, `seal`, `publish` supports scope-owned private writers, +same-filesystem cross-directory link/rename publication, no-replace `exists`, and +explicit replacement. Seal synchronizes and closes the file before publication; +publication synchronizes destination then distinct source directories. Syscall +failure poisons the stage; uncertain publication is never retried or copied across +filesystems. Later failure/cancellation/OOM cleans private names, never rolls back +a visible destination. `exists` does not prove identity or durability: caller code +must authenticate the winner. Source artifact policy is not in the native host. + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_zig_publication.py "$HASKELL/bin/tricu" \ + "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-publication-new +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_zig_stages.py "$HASKELL/bin/tricu" \ + "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-stages-new +``` + +The latter control requires a writable `/dev/shm` on a different filesystem for +actual EXDEV validation; absence is a failed/incomplete control, not a silent skip. +Fault-injected state-machine tests cover every publication step; allocator sweeps +cover acquisition and reply encoding before/after visibility with real private +files. These remain private integration tools, not release/compiler acceptance. + +Private `utf8/encode` and `utf8/decode` implement bounded stateless strict +transport (1024 scalars / 4096 bytes). Nonfinal decode returns at most three carry +bytes; invalid available prefixes fail immediately. No replacement, normalization +or BOM stripping. Source code retains text/source policy and streaming carry. + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_text.py "$HASKELL/bin/tricu" \ + --zig-probe "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-text-new +``` + +The shared independent controls cover binary NUL, Unicode, carry boundaries, +source BOM rejection, partial writes and excess-list non-demand. Private probes +accept `tree-file:PATH` arguments containing exact raw 0/1/2 constructor trees +(maximum 10 MiB per argument), parsed iteratively with trailing roots rejected. +These private transport arguments are not production CLI options. + +The indexed codec also offers a graph-proportional canonical `Plan`, with repeatable +`emit` calls using at most 4096-byte borrowed chunks and no retained encoded payload. +The existing whole-value `encode` interface wraps that emitter; its bytes remain +unchanged. Preparation still needs a resident DAG and graph-proportional tables, +not a heap/CPU quota. Sink failure stops emission without retry. This foundation +alone does not implement the private host's authenticated indexed-tree facilities. + +The private file host implements `indexed-tree/hash`, `write`, and `verify` with +that plan. Writes preflight size and digest before sending bytes, including to +stages; verification compares exact canonical bytes and consumes only one frame, +without an EOF probe. Resource kind/lease checks precede tree preparation. Caller +code still owns winner authentication, EOF policy and publication. Authenticated +reads use the snapshot transport described below. + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_indexed_write.py "$HASKELL/bin/tricu" \ + --zig-writer-probe "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-indexed-new +``` + +That explicitly writer-only subset does not run or claim authenticated round-trip +controls. Omitting the option retains the complete Haskell suite. Controls include +independent wire bytes, deep linear trees, corrupt/excess winners, preflight +preservation, exact frame tails and wrong-kind non-demand; native allocation +sweeps cover lease cleanup and failure after a completed write without rollback. + +Private `indexed-tree/read` now authenticates an exact frame into a scope-registered +anonymous file before decoding. The private name is unlinked before input is +consumed; disk usage is proportional to frame length, transfers/cache are <=4096 +bytes, metadata is limited to 64 sections / 65536 manifest bytes, and node tables +are graph-proportional. No original-input reread, following-byte EOF probe, encoded +payload buffer, implicit publication or durable snapshot promise. Normal Results +and terminal allocation failures retire the internal descriptor; release errors +cannot replace a primary failure. These synchronous calls have no deadline. + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_indexed.py "$HASKELL/bin/tricu" \ + --zig-probe "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-snapshots-new +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_indexed_write.py "$HASKELL/bin/tricu" \ + --zig-probe "$ZIG/bin/io-v2-file-probe" --directory /tmp/zig-v2-roundtrips-new +``` + +The complete private mode includes authenticated shared-DAG round trips. Read +controls include exact cursor boundaries, authentication before malformed-format +rejection, failed preflight without consumption, and opaque transport growth to +64 MiB under a sampled RSS guard. This does not establish large-DAG capacity or +constant aggregate memory. Format diagnostics currently use `invalid indexed +frame` rather than Haskell's more specific text; category/progress remain explicit. +Public native/browser v2 entrypoints and release/compiler acceptance are unchanged. + +Actual compiler callers can be exercised without enabling the production driver: + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_compiler_store.py "$HASKELL/bin/tricu" \ + --zig-probe "$ZIG/bin/io-v2-file-probe" --compiler-smoke \ + --directory /tmp/zig-v2-compiler-new +``` + +This builds a current source-mapped candidate on Haskell, runs its v2 compiler +worker on private Zig for cold/warm dependency builds, authenticates the returned +receipt, loads/executes the resulting store on Haskell, and compares the complete +application bundle. Invalid source controls compare ordinary failure trees across +hosts. Source-object, corrupt-winner, concurrent alias and cleanup controls remain +included; pure helper checks run on Haskell and are distinguished in the report. +This is a compiler-workflow smoke, not generation-two acceptance, browser support, +production driver enablement, or support-catalogue publication. + + +Production native boundary and compiler smoke controls: + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_native_cli.py "$HASKELL/bin/tricu" \ + "$ZIG/bin/tricu-zig" --directory /tmp/zig-v2-native-new \ + --compiler-bundle .bootstrap-run/io-v2-cohort/generation2/build-stable.arboricx +``` + +The compiler bundle is an explicit v2 `sourceBuildGraph` worker, not inferred from +its filename. The optional compiler smoke selects that export into a separate +bundle, executes cold/warm dependency builds through the real CLI, and authenticates/ +executes the resulting store on Haskell. It neither builds a new compiler generation +nor changes packaged compiler, browser, catalogue or registry defaults. + +Native owned-ABI clients select `arb_run_io_v2` for IO protocol 2. It uses the +CLI interpreter/profile, takes no grants, preserves ordinary Results and leaves +the output handle untouched on error. Native IO is rejected in WASM; the browser +adapter uses the shared structured scheduler instead. + +Owned-handle C ABI **3** removes `arb_run_io` and the legacy permission struct. +IO protocol **2** is independent of that ABI version. Explicit historical +execution requires a retained ABI-2 library; current clients never fall back. + +The generation executor selects the symbol from captured execution-version +metadata, validates it against generation inputs, and uses Unicode-scalar trees +for v2 arguments. Historical records lacking version metadata select v1 explicitly. +The bounded comparison is reproducible with an explicit authenticated v2 kit: + +```sh +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_generation.py "$HASKELL/bin/tricu" \ + --kit /path/to/v2-kit --zig-library "$ZIG/lib/libarboricx.so" \ + --directory /tmp/zig-v2-generation-boundary-new +``` + +This exercises actual mapped-library builds and complete object inventories on +both hosts, plus version/identity-selection controls. It is not a full compiler +pair. Use the [isolated cross-host workflow](../../docs/compiler-rebuilding.md#isolated-cross-runtime-fixed-point-reproduction) +with explicit `--io-version 2 --bootstrap-kit /path/to/v2-kit` for full +H1/H2/H3/Z3 reproduction. Its pair, inventory, program-smoke, corruption and +retention gates are independent of the bounded comparison. diff --git a/ext/zig/build.zig b/ext/zig/build.zig @@ -19,15 +19,122 @@ pub fn build(b: *std.Build) void { }) }); test_step.dependOn(&b.addRunArtifact(preview_tests).step); - const io_tests_mod = b.createModule(.{ - .root_source_file = b.path("src/io_driver.zig"), + const v2_protocol_tests = b.addTest(.{ .root_module = b.createModule(.{ + .root_source_file = b.path("src/io_v2_protocol.zig"), + .target = target, + .optimize = optimize, + }) }); + test_step.dependOn(&b.addRunArtifact(v2_protocol_tests).step); + + const v2_resource_tests = b.addTest(.{ .root_module = b.createModule(.{ + .root_source_file = b.path("src/io_v2_resources.zig"), + .target = target, + .optimize = optimize, + }) }); + test_step.dependOn(&b.addRunArtifact(v2_resource_tests).step); + + const v2_task_tests = b.addTest(.{ .root_module = b.createModule(.{ + .root_source_file = b.path("src/io_v2_tasks.zig"), + .target = target, + .optimize = optimize, + }) }); + test_step.dependOn(&b.addRunArtifact(v2_task_tests).step); + + const v2_core_tests = b.addTest(.{ .root_module = b.createModule(.{ + .root_source_file = b.path("src/io_v2_core.zig"), + .target = target, + .optimize = optimize, + }) }); + test_step.dependOn(&b.addRunArtifact(v2_core_tests).step); + + const v2_files_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_files.zig"), + .target = target, + .optimize = optimize, + }); + v2_files_mod.link_libc = true; + v2_files_mod.linkSystemLibrary("uv", .{}); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_files_mod })).step); + + const v2_file_host_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_file_host.zig"), + .target = target, + .optimize = optimize, + }); + v2_file_host_mod.link_libc = true; + v2_file_host_mod.linkSystemLibrary("uv", .{}); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_file_host_mod })).step); + + const v2_native_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_native.zig"), + .target = target, + .optimize = optimize, + }); + v2_native_mod.link_libc = true; + v2_native_mod.linkSystemLibrary("uv", .{}); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_native_mod })).step); + + const v2_paths_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_paths.zig"), + .target = target, + .optimize = optimize, + }); + v2_paths_mod.link_libc = true; + v2_paths_mod.linkSystemLibrary("uv", .{}); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_paths_mod })).step); + + const v2_digest_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_digest.zig"), + .target = target, + .optimize = optimize, + }); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_digest_mod })).step); + + const v2_stage_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_stage_state.zig"), + .target = target, + .optimize = optimize, + }); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_stage_mod })).step); + + const v2_native_stage_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_stage.zig"), + .target = target, + .optimize = optimize, + }); + v2_native_stage_mod.link_libc = true; + v2_native_stage_mod.linkSystemLibrary("uv", .{}); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_native_stage_mod })).step); + + const v2_utf8_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_utf8.zig"), + .target = target, + .optimize = optimize, + }); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_utf8_mod })).step); + + const v2_probe_tests = b.createModule(.{ + .root_source_file = b.path("src/io_v2_core_probe.zig"), + .target = target, + .optimize = optimize, + }); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = v2_probe_tests })).step); + + const core_probe = b.addExecutable(.{ .name = "io-v2-core-probe", .root_module = b.createModule(.{ + .root_source_file = b.path("src/io_v2_core_probe.zig"), + .target = target, + .optimize = optimize, + }) }); + b.installArtifact(core_probe); + + const file_probe_mod = b.createModule(.{ + .root_source_file = b.path("src/io_v2_file_probe.zig"), .target = target, .optimize = optimize, }); - io_tests_mod.link_libc = true; - io_tests_mod.linkSystemLibrary("uv", .{}); - const io_tests = b.addTest(.{ .root_module = io_tests_mod }); - test_step.dependOn(&b.addRunArtifact(io_tests).step); + file_probe_mod.link_libc = true; + file_probe_mod.linkSystemLibrary("uv", .{}); + b.installArtifact(b.addExecutable(.{ .name = "io-v2-file-probe", .root_module = file_probe_mod })); // -- kernel generator tool (runs on build host) -- const gen_kernel_mod = b.createModule(.{ diff --git a/ext/zig/include/arboricx.h b/ext/zig/include/arboricx.h @@ -16,7 +16,7 @@ typedef enum { ARB_IO_ERROR = 6, ARB_HANDLE_EXHAUSTED = 7 } arb_status_t; -/* ABI v2: each successful handle output is independently owned. Inputs are +/* ABI v3: each successful handle output is independently owned. Inputs are * borrowed. arb_retain creates another owned handle; arb_release invalidates * only that handle. Handles are opaque, context-bound and never serialize. * Reduction is lazy and in place: aliases observe shared evaluation progress, @@ -79,13 +79,12 @@ arb_status_t arb_get_fork_children(arb_ctx_t* ctx, arb_handle_t root, arb_status_t arb_get_app_func_arg(arb_ctx_t* ctx, arb_handle_t root, arb_handle_t* out_func, arb_handle_t* out_arg); -/* IO driver (Layer 2 — POSIX interaction-tree runtime) */ -typedef struct { - int allow_read_all; - int allow_write_all; -} arb_io_perms_t; - -arb_status_t arb_run_io(arb_ctx_t* ctx, arb_handle_t program, const arb_io_perms_t* perms, arb_handle_t* out); +/* Native IO v2 compiler/file profile. OS process authority, no v1 grants. + * Rejects non-v2 envelopes; never probes or retries another protocol. WASM: ARB_IO_ERROR. + * Successful ordinary Results remain data; terminal failure/cancellation returns + * ARB_IO_ERROR without forcing its payload. On error *out is unchanged. + * ABI version 3 removes the legacy arb_run_io entrypoint and grant struct. */ +arb_status_t arb_run_io_v2(arb_ctx_t* ctx, arb_handle_t program, arb_handle_t* out); /* Kernel entrypoints */ arb_status_t arb_kernel_root(arb_ctx_t* ctx, arb_handle_t* out); diff --git a/ext/zig/src/c_abi.zig b/ext/zig/src/c_abi.zig @@ -6,7 +6,6 @@ const reduce = @import("reduce.zig"); const codecs = @import("codecs.zig"); const kernel = @import("kernel.zig"); const bundle = @import("bundle.zig"); -const io_driver = @import("io_driver.zig"); const handles = @import("handles.zig"); const Handle = handles.Handle; @@ -61,7 +60,7 @@ pub const ArbCtx = struct { }; export fn arb_abi_version() u32 { - return 2; + return 3; } export fn arboricx_init() ?*ArbCtx { const gpa = if (wasm) std.heap.wasm_allocator else std.heap.smp_allocator; @@ -220,16 +219,13 @@ export fn arb_unwrap_host_value(ctx: *ArbCtx, root: Handle, out_tag: *u64, out_p if (result == .ok) out_tag.* = hv.tag; return result; } -pub const arb_io_perms_t = extern struct { allow_read_all: c_int, allow_write_all: c_int }; -export fn arb_run_io(ctx: *ArbCtx, program: Handle, perms: ?*const arb_io_perms_t, out: *Handle) Status { - if (wasm) return .io_error; // Native IO is unavailable in the browser prototype. +// Explicit version-specific native entrypoint; ABI v3 is NOT an IO selector. +// No legacy grant struct is accepted. Failed calls never publish an output handle. +export fn arb_run_io_v2(ctx: *ArbCtx, program: Handle, out: *Handle) Status { + if (wasm) return .io_error; const node = ctx.handles.get(program) catch |err| return status(err); ctx.prepare() catch |err| return status(err); - const zig_perms = if (perms) |p| io_driver.IOPerms{ - .allow_read_all = p.allow_read_all != 0, - .allow_write_all = p.allow_write_all != 0, - } else io_driver.IOPerms{}; - const result = io_driver.runIO(ctx.gpa, &ctx.arena, node, zig_perms) catch |err| return switch (err) { + const result = @import("io_v2_native.zig").run(&ctx.arena, node, std.math.maxInt(u64)) catch |err| return switch (err) { error.OutOfMemory => .out_of_memory, error.FuelExhausted => .fuel_exhausted, else => .io_error, diff --git a/ext/zig/src/indexed.zig b/ext/zig/src/indexed.zig @@ -10,6 +10,9 @@ const Key = struct { tag: u8, left: u32 = 0, right: u32 = 0 }; const Writer = struct { allocator: std.mem.Allocator, bytes: std.ArrayList(u8) = .empty, + pub fn writeAll(w: *Writer, data: []const u8) !void { + try w.raw(data); + } fn raw(w: *Writer, data: []const u8) !void { try w.bytes.appendSlice(w.allocator, data); } @@ -35,7 +38,7 @@ fn intern(nodes: *std.ArrayList(Key), keys: *std.AutoHashMap(Key, u32), key: Key return id; } -pub fn encode(arena: *Arena, root: u32) ![]u8 { +pub fn prepare(arena: *Arena, root: u32) !Plan { const allocator = arena.allocator; const base = arena.roots.items.len; defer arena.roots.shrinkRetainingCapacity(base); @@ -45,7 +48,7 @@ pub fn encode(arena: *Arena, root: u32) ![]u8 { var keys = std.AutoHashMap(Key, u32).init(allocator); defer keys.deinit(); var nodes: std.ArrayList(Key) = .empty; - defer nodes.deinit(allocator); + errdefer nodes.deinit(allocator); const Frame = union(enum) { visit: u32, finish: struct { id: u32, node: Node } }; var stack: std.ArrayList(Frame) = .empty; defer stack.deinit(allocator); @@ -104,38 +107,111 @@ pub fn encode(arena: *Arena, root: u32) ![]u8 { try manifest.int(u16, 5); try manifest.text("arboricx"); try manifest.int(u32, 0); - var node_bytes = Writer{ .allocator = allocator }; - defer node_bytes.deinit(); - try node_bytes.int(u64, nodes.items.len); - for (nodes.items) |key| { - try node_bytes.int(u32, 1 + 4 * @as(u32, key.tag)); - try node_bytes.int(u8, key.tag); - if (key.tag >= 1) try node_bytes.int(u32, key.left); - if (key.tag == 2) try node_bytes.int(u32, key.right); + var payload_length: u64 = 8; + for (nodes.items) |key| payload_length += 5 + 4 * @as(u64, key.tag); + const manifest_bytes = try manifest.bytes.toOwnedSlice(allocator); + errdefer allocator.free(manifest_bytes); + return .{ .allocator = allocator, .nodes = try nodes.toOwnedSlice(allocator), .manifest = manifest_bytes, .payload_length = payload_length }; +} + +/// Graph-proportional canonical plan. It contains no encoded payload and no arena +/// IDs; emission can be repeated without retaining a previous byte stream. +pub const Plan = struct { + allocator: std.mem.Allocator, + nodes: []Key, + manifest: []u8, + payload_length: u64, + pub fn deinit(self: *Plan) void { + self.allocator.free(self.nodes); + self.allocator.free(self.manifest); + self.* = undefined; } - var output = Writer{ .allocator = allocator }; - errdefer output.deinit(); - try output.raw("ARBORICX"); - try output.int(u16, 1); - try output.int(u16, 0); - try output.int(u32, 2); - try output.int(u64, 0); - try output.int(u64, 32); - var offset: u64 = 96; - for ([_][]const u8{ manifest.bytes.items, node_bytes.bytes.items }, 1..) |data, kind| { - try output.int(u32, @intCast(kind)); - try output.int(u16, 1); + pub fn encodedLength(self: *const Plan) u64 { + return 96 + @as(u64, self.manifest.len) + self.payload_length; + } + /// Sink.writeAll consumes borrowed chunks synchronously; <=4096 bytes each. + /// A failing sink is never retried by this emitter. + pub fn emit(self: *const Plan, sink: anytype) !void { + var output = ChunkWriter(@TypeOf(sink)){ .sink = sink }; + try output.raw("ARBORICX"); try output.int(u16, 1); try output.int(u16, 0); - try output.int(u16, 0); - try output.int(u64, offset); - try output.int(u64, data.len); - try output.int(u32, 0); - offset += data.len; + try output.int(u32, 2); + try output.int(u64, 0); + try output.int(u64, 32); + var offset: u64 = 96; + for ([_]u64{ self.manifest.len, self.payload_length }, 1..) |length, kind| { + try output.int(u32, @intCast(kind)); + try output.int(u16, 1); + try output.int(u16, 1); + try output.int(u16, 0); + try output.int(u16, 0); + try output.int(u64, offset); + try output.int(u64, length); + try output.int(u32, 0); + offset += length; + } + try output.raw(self.manifest); + try output.int(u64, self.nodes.len); + for (self.nodes) |key| { + try output.int(u32, 1 + 4 * @as(u32, key.tag)); + try output.int(u8, key.tag); + if (key.tag >= 1) try output.int(u32, key.left); + if (key.tag == 2) try output.int(u32, key.right); + } + try output.flush(); + } + pub fn hash(self: *const Plan) ![64]u8 { + var sink = HashSink{}; + sink.context.update(domain ++ "\x00"); + try self.emit(&sink); + var raw: [32]u8 = undefined; + sink.context.final(&raw); + return std.fmt.bytesToHex(raw, .lower); } - try output.raw(manifest.bytes.items); - try output.raw(node_bytes.bytes.items); - return output.bytes.toOwnedSlice(allocator); +}; +const HashSink = struct { + context: std.crypto.hash.sha2.Sha256 = .init(.{}), + pub fn writeAll(self: *HashSink, bytes: []const u8) !void { + self.context.update(bytes); + } +}; +fn ChunkWriter(comptime Sink: type) type { + return struct { + sink: Sink, + buffer: [4096]u8 = undefined, + used: usize = 0, + fn flush(self: *@This()) !void { + if (self.used == 0) return; + try self.sink.writeAll(self.buffer[0..self.used]); + self.used = 0; + } + fn raw(self: *@This(), data: []const u8) !void { + var offset: usize = 0; + while (offset < data.len) { + const count = @min(data.len - offset, self.buffer.len - self.used); + @memcpy(self.buffer[self.used..][0..count], data[offset..][0..count]); + self.used += count; + offset += count; + if (self.used == self.buffer.len) try self.flush(); + } + } + fn int(self: *@This(), comptime T: type, value: T) !void { + var bytes: [@sizeOf(T)]u8 = undefined; + std.mem.writeInt(T, &bytes, value, .big); + try self.raw(&bytes); + } + }; +} + +/// Compatibility whole-value encoder; bounded transports use Plan.emit instead. +pub fn encode(arena: *Arena, root: u32) ![]u8 { + var plan = try prepare(arena, root); + defer plan.deinit(); + var output = Writer{ .allocator = arena.allocator }; + errdefer output.deinit(); + try plan.emit(&output); + return output.bytes.toOwnedSlice(arena.allocator); } pub fn digest(bytes: []const u8) [64]u8 { @@ -167,6 +243,9 @@ const Reader = struct { fn expect(r: *Reader, data: []const u8) !void { if (!std.mem.eql(u8, try r.take(data.len), data)) return error.InvalidData; } + fn remaining(r: *const Reader) usize { + return r.bytes.len - r.pos; + } fn end(r: *Reader) !void { if (r.pos != r.bytes.len) return error.InvalidData; } @@ -211,7 +290,13 @@ pub fn load(arena: *Arena, bytes: []const u8, name: ?[]const u8, single: bool) ! } } else if (flags & 1 != 0) return error.InvalidData; } - var m = Reader{ .bytes = manifest orelse return error.InvalidData }; + const selection = try parseManifest(arena, manifest orelse return error.InvalidData, name, single); + var n = Reader{ .bytes = payload orelse return error.InvalidData }; + return loadNodes(arena, &n, selection); +} +const Selection = struct { max_root: u32, selected: ?u32 }; +fn parseManifest(arena: *Arena, bytes: []const u8, name: ?[]const u8, single: bool) !Selection { + var m = Reader{ .bytes = bytes }; try m.expect("ARBMNFST"); if (try m.int(u16) != 1 or try m.int(u16) != 1) return error.InvalidData; for (fields) |field| if (!std.mem.eql(u8, try m.text(), field)) return error.InvalidData; @@ -258,9 +343,11 @@ pub fn load(arena: *Arena, bytes: []const u8, name: ?[]const u8, single: bool) ! } } try m.end(); - var n = Reader{ .bytes = payload orelse return error.InvalidData }; + return .{ .max_root = max_root, .selected = selected }; +} +fn loadNodes(arena: *Arena, n: anytype, selection: Selection) !u32 { const node_count = try n.int(u64); - if (node_count == 0 or node_count > std.math.maxInt(u32) or node_count > (n.bytes.len - n.pos) / 5 or max_root >= node_count) return error.InvalidData; + if (node_count == 0 or node_count > std.math.maxInt(u32) or node_count > n.remaining() / 5 or selection.max_root >= node_count) return error.InvalidData; const ids = try arena.allocator.alloc(u32, @intCast(node_count)); defer arena.allocator.free(ids); var keys = std.AutoHashMap(Key, void).init(arena.allocator); @@ -288,7 +375,100 @@ pub fn load(arena: *Arena, bytes: []const u8, name: ?[]const u8, single: bool) ! }); } try n.end(); - return ids[selected orelse return error.ExportNotFound]; + return ids[selection.selected orelse return error.ExportNotFound]; +} + +/// Decode only an already-authenticated stable snapshot. The source exposes a +/// length and exact bounded positional reads; neither original-input rereading +/// nor whole encoded-payload allocation occurs here. External padding is allowed. +pub fn loadSnapshot(arena: *Arena, source: anytype) !u32 { + var header_bytes: [32]u8 = undefined; + if (source.length < header_bytes.len) return error.InvalidData; + try source.readAt(0, &header_bytes); + var header = Reader{ .bytes = &header_bytes }; + try header.expect("ARBORICX"); + if (try header.int(u16) != 1) return error.InvalidData; + _ = try header.int(u16); + const count = try header.int(u32); + if (count > 64) return error.InvalidData; + _ = try header.int(u64); + const start = try header.int(u64); + try snapshotRange(source.length, start, @as(u64, count) * 32); + const Section = struct { offset: u64, length: u64 }; + var manifest: ?Section = null; + var payload: ?Section = null; + for (0..count) |i| { + var raw: [32]u8 = undefined; + try source.readAt(start + @as(u64, i) * 32, &raw); + var entry = Reader{ .bytes = &raw }; + const kind = try entry.int(u32); + _ = try entry.int(u16); + const flags = try entry.int(u16); + const compression = try entry.int(u16); + _ = try entry.int(u16); + const offset = try entry.int(u64); + const length = try entry.int(u64); + _ = try entry.int(u32); + if (kind == 1 or kind == 2) { + if (compression != 0) return error.InvalidData; + try snapshotRange(source.length, offset, length); + if (kind == 1) { + if (manifest != null or length > 65536) return error.InvalidData; + manifest = .{ .offset = offset, .length = length }; + } else { + if (payload != null) return error.InvalidData; + payload = .{ .offset = offset, .length = length }; + } + } else if (flags & 1 != 0) return error.InvalidData; + } + const m = manifest orelse return error.InvalidData; + const bytes = try arena.allocator.alloc(u8, @intCast(m.length)); + defer arena.allocator.free(bytes); + var at: usize = 0; + while (at < bytes.len) { + const size = @min(4096, bytes.len - at); + try source.readAt(m.offset + at, bytes[at..][0..size]); + at += size; + } + const selection = try parseManifest(arena, bytes, "root", true); + const p = payload orelse return error.InvalidData; + var nodes = SnapshotReader(@TypeOf(source)){ .source = source, .start = p.offset, .length = p.length }; + return loadNodes(arena, &nodes, selection); +} +fn snapshotRange(length: u64, offset: u64, count: u64) !void { + if (offset > length or count > length - offset) return error.InvalidData; +} +fn SnapshotReader(comptime Source: type) type { + return struct { + source: Source, + start: u64, + length: u64, + pos: u64 = 0, + cache: [4096]u8 = undefined, + used: usize = 0, + available: usize = 0, + fn remaining(self: *const @This()) u64 { + return self.length - self.pos; + } + fn int(self: *@This(), comptime T: type) !T { + if (self.remaining() < @sizeOf(T)) return error.InvalidData; + var bytes: [@sizeOf(T)]u8 = undefined; + for (&bytes) |*byte| { + if (self.used == self.available) { + self.available = @intCast(@min(self.remaining(), self.cache.len)); + try self.source.readAt(self.start + self.pos, self.cache[0..self.available]); + self.used = 0; + } + byte.* = self.cache[self.used]; + self.used += 1; + self.pos += 1; + } + return std.mem.readInt(T, &bytes, .big); + } + fn end(self: *const @This()) !void { + if (self.remaining() != 0) return error.InvalidData; + } + }; } test "canonical leaf matches independent writer profile and domain digest" { @@ -406,3 +586,81 @@ test "reader rejects duplicate constructors, forward edges and mismatched single std.mem.writeInt(u64, duplicate[node_offset..][0..8], 2, .big); try std.testing.expectError(error.InvalidData, load(&arena, duplicate, "root", true)); } + +test "canonical plan streams bounded repeatable chunks and stops at sink failure" { + const t = std.testing; + var arena = Arena.init(t.allocator); + defer arena.deinit(); + var root = try arena.alloc(.leaf); + for (0..10000) |_| root = try arena.alloc(.{ .fork = .{ .left = root, .right = root } }); + var plan = try prepare(&arena, root); + defer plan.deinit(); + try t.expectEqual(@as(u64, 130431), plan.encodedLength()); + const Sink = struct { + count: u64 = 0, + calls: usize = 0, + fail_at: ?usize = null, + pub fn writeAll(self: *@This(), bytes: []const u8) !void { + try t.expect(bytes.len > 0 and bytes.len <= 4096); + self.calls += 1; + if (self.fail_at == self.calls) return error.InjectedFailure; + self.count += bytes.len; + } + }; + var sink = Sink{}; + try plan.emit(&sink); + try t.expectEqual(plan.encodedLength(), sink.count); + const calls = sink.calls; + for (1..calls + 1) |step| { + var failing = Sink{ .fail_at = step }; + try t.expectError(error.InjectedFailure, plan.emit(&failing)); + try t.expectEqual(step, failing.calls); + } + // Independent Python wire profile: io_v2_indexed_write.dag(10000, True). + try t.expectEqualStrings("9039b73aff5b088b972a22e41728147bda274d1af9ca01a92afa747a930722b9", &(try plan.hash())); + const whole = try encode(&arena, root); + defer t.allocator.free(whole); + try t.expectEqualSlices(u8, &digest(whole), &(try plan.hash())); + var second = Writer{ .allocator = t.allocator }; + defer second.deinit(); + try plan.emit(&second); + try t.expectEqualSlices(u8, whole, second.bytes.items); + try t.expectEqual(@as(usize, 0), arena.roots.items.len); +} + +const MemorySnapshot = struct { + bytes: []const u8, + length: u64, + pub fn readAt(self: *@This(), offset: u64, target: []u8) !void { + try std.testing.expect(target.len <= 4096); + if (offset > self.length or target.len > self.length - offset) return error.InvalidData; + @memcpy(target, self.bytes[@intCast(offset)..][0..target.len]); + } +}; +test "bounded snapshot decoder rejects every truncation and mutation without unchecked ranges" { + var arena = Arena.init(std.testing.allocator); + defer arena.deinit(); + const bytes = try encode(&arena, try arena.alloc(.leaf)); + defer arena.allocator.free(bytes); + for (0..bytes.len) |len| { + var source = MemorySnapshot{ .bytes = bytes, .length = len }; + try std.testing.expectError(error.InvalidData, loadSnapshot(&arena, &source)); + } + var source = MemorySnapshot{ .bytes = bytes, .length = bytes.len }; + for (bytes) |*byte| { + byte.* ^= 0xff; + _ = loadSnapshot(&arena, &source) catch |err| switch (err) { + error.InvalidData, error.ExportNotFound => 0, + else => return err, + }; + byte.* ^= 0xff; + } + const root = try loadSnapshot(&arena, &source); + try std.testing.expect(arena.get(root).* == .leaf); + for ([_]usize{ 24, 44, 52, 76, 84 }) |offset| { + const old = std.mem.readInt(u64, bytes[offset..][0..8], .big); + std.mem.writeInt(u64, bytes[offset..][0..8], std.math.maxInt(u64), .big); + try std.testing.expectError(error.InvalidData, loadSnapshot(&arena, &source)); + std.mem.writeInt(u64, bytes[offset..][0..8], old, .big); + } +} diff --git a/ext/zig/src/io_driver.zig b/ext/zig/src/io_driver.zig @@ -1,1593 +0,0 @@ -const std = @import("std"); -const Arena = @import("arena.zig").Arena; -const codecs = @import("codecs.zig"); -const source_text = @import("source_text.zig"); -const indexed = @import("indexed.zig"); -const reduce = @import("reduce.zig"); -const tree = @import("tree.zig"); - -const c = @cImport({ - // Import declarations, not glibc's fortified C inline wrappers. With the - // pinned Zig/glibc, optimized translate-c diagnoses the variadic open/openat - // wrapper bodies even though this driver calls uv_fs_open, not libc open. - // This is local to header translation: libuv and C callers retain their - // normal build hardening, and Zig ReleaseSafe retains runtime checks. - @cUndef("_FORTIFY_SOURCE"); - @cDefine("_FORTIFY_SOURCE", "0"); - @cInclude("uv.h"); -}); - -// --------------------------------------------------------------------------- -// Action tag constants (must match lib/io.tri and IODriver.hs) -// --------------------------------------------------------------------------- - -pub const ActionTag = enum(u8) { - pure = 0, - bind = 1, - putStr = 10, - getLine = 11, - readFile = 20, - readSourceFile = 37, - encodeSourceText = 38, - readIndexedTree = 50, - hashIndexedTree = 51, - writeIndexedTreeIfAbsent = 52, - writeFile = 21, - writeBytes = 22, - renameFile = 24, - createDirectory = 25, - fileExists = 27, - sha256Hex = 28, - writeBytesIfAbsent = 32, - atomicWriteBytes = 33, - ask = 30, - local = 31, - get = 40, - put = 41, - fork = 60, - await = 61, - yield = 62, - sleep = 63, -}; - -const FileWrite = struct { path: u32, contents: u32 }; - -pub const Action = union(ActionTag) { - pure: u32, - bind: struct { left: u32, k: u32 }, - putStr: u32, - getLine, - readFile: u32, - readSourceFile: u32, - encodeSourceText: u32, - readIndexedTree: struct { path: u32, digest: u32 }, - hashIndexedTree: u32, - writeIndexedTreeIfAbsent: struct { path: u32, digest: u32, tree: u32 }, - writeFile: FileWrite, - writeBytes: FileWrite, - renameFile: struct { old: u32, new: u32 }, - createDirectory: u32, - fileExists: u32, - sha256Hex: u32, - writeBytesIfAbsent: FileWrite, - atomicWriteBytes: FileWrite, - ask, - local: struct { f: u32, action: u32 }, - get, - put: u32, - fork: u32, - await: u32, - yield, - sleep: u32, -}; - -// --------------------------------------------------------------------------- -// Error codes (must match IODriver.hs) -// --------------------------------------------------------------------------- - -const ERR_DOES_NOT_EXIST: u64 = 1; -const ERR_PERMISSION: u64 = 2; -const ERR_ALREADY_EXISTS: u64 = 3; -const ERR_IO_OTHER: u64 = 4; -const ERR_POLICY_DENY: u64 = 20; -const ERR_INVALID_ACTION: u64 = 40; -const ERR_INVALID_STRING: u64 = 41; - -// --------------------------------------------------------------------------- -// Permissions -// --------------------------------------------------------------------------- - -pub const IOPerms = struct { - allow_read_all: bool = false, - allow_write_all: bool = false, -}; - -// --------------------------------------------------------------------------- -// IO sentinel detection -// --------------------------------------------------------------------------- - -pub fn isIOSentinel(arena: *Arena, root: u32) !?u32 { - const node = arena.get(try reduce.reduce(root, arena, 1_000_000)).*; - if (node != .fork) return null; - - const sentinel = node.fork.left; - const rest = node.fork.right; - - const sentinel_str = try codecs.toString(arena, sentinel); - defer { - if (sentinel_str) |s| { - arena.allocator.free(s); - } - } - if (sentinel_str == null) return null; - if (!std.mem.eql(u8, sentinel_str.?, "tricuIO")) return null; - - const rest_node = arena.get(try reduce.reduce(rest, arena, 1_000_000)).*; - if (rest_node != .fork) return null; - - const version_num = try codecs.toNumber(arena, rest_node.fork.left); - if (version_num == null or version_num.? != 1) return null; - - return rest_node.fork.right; -} - -// --------------------------------------------------------------------------- -// Action decoding -// --------------------------------------------------------------------------- - -pub fn decodeAction(arena: *Arena, root: u32) !?Action { - const node = arena.get(root).*; - if (node != .fork) return null; - - const tag_num = try codecs.toNumber(arena, node.fork.left); - if (tag_num == null) return null; - - const tag: ActionTag = switch (tag_num.?) { - 0 => .pure, - 1 => .bind, - 10 => .putStr, - 11 => .getLine, - 20 => .readFile, - 37 => .readSourceFile, - 38 => .encodeSourceText, - 50 => .readIndexedTree, - 51 => .hashIndexedTree, - 52 => .writeIndexedTreeIfAbsent, - 21 => .writeFile, - 22 => .writeBytes, - 24 => .renameFile, - 25 => .createDirectory, - 27 => .fileExists, - 28 => .sha256Hex, - 32 => .writeBytesIfAbsent, - 33 => .atomicWriteBytes, - 30 => .ask, - 31 => .local, - 40 => .get, - 41 => .put, - 60 => .fork, - 61 => .await, - 62 => .yield, - 63 => .sleep, - else => return null, - }; - - const payload = node.fork.right; - - return switch (tag) { - .pure => Action{ .pure = payload }, - .bind => blk: { - const payload_node = arena.get(try reduce.reduce(payload, arena, 1_000_000)); - if (payload_node.* != .fork) return null; - break :blk Action{ .bind = .{ .left = payload_node.fork.left, .k = payload_node.fork.right } }; - }, - .putStr => Action{ .putStr = payload }, - .getLine => Action.getLine, - .readFile => Action{ .readFile = payload }, - .readSourceFile => Action{ .readSourceFile = payload }, - .encodeSourceText => Action{ .encodeSourceText = payload }, - .hashIndexedTree => Action{ .hashIndexedTree = payload }, - .readIndexedTree, .writeIndexedTreeIfAbsent => blk: { - const pair = arena.get(try reduce.reduce(payload, arena, 1_000_000)).*; - if (pair != .fork) return null; - if (tag == .readIndexedTree) break :blk .{ .readIndexedTree = .{ .path = pair.fork.left, .digest = pair.fork.right } }; - const rest = arena.get(try reduce.reduce(pair.fork.right, arena, 1_000_000)).*; - if (rest != .fork) return null; - break :blk .{ .writeIndexedTreeIfAbsent = .{ .path = pair.fork.left, .digest = rest.fork.left, .tree = rest.fork.right } }; - }, - .writeFile, .writeBytes, .writeBytesIfAbsent, .atomicWriteBytes, .renameFile => blk: { - const pair = arena.get(try reduce.reduce(payload, arena, 1_000_000)); - if (pair.* != .fork) return null; - break :blk switch (tag) { - .writeFile => Action{ .writeFile = .{ .path = pair.fork.left, .contents = pair.fork.right } }, - .writeBytes => Action{ .writeBytes = .{ .path = pair.fork.left, .contents = pair.fork.right } }, - .writeBytesIfAbsent => Action{ .writeBytesIfAbsent = .{ .path = pair.fork.left, .contents = pair.fork.right } }, - .atomicWriteBytes => Action{ .atomicWriteBytes = .{ .path = pair.fork.left, .contents = pair.fork.right } }, - .renameFile => Action{ .renameFile = .{ .old = pair.fork.left, .new = pair.fork.right } }, - else => unreachable, - }; - }, - .createDirectory => Action{ .createDirectory = payload }, - .fileExists => Action{ .fileExists = payload }, - .sha256Hex => Action{ .sha256Hex = payload }, - .ask => Action.ask, - .local => blk: { - const payload_node = arena.get(try reduce.reduce(payload, arena, 1_000_000)); - if (payload_node.* != .fork) return null; - break :blk Action{ .local = .{ .f = payload_node.fork.left, .action = payload_node.fork.right } }; - }, - .get => Action.get, - .put => Action{ .put = payload }, - .fork => Action{ .fork = payload }, - .await => Action{ .await = payload }, - .yield => Action.yield, - .sleep => Action{ .sleep = payload }, - }; -} - -// --------------------------------------------------------------------------- -// Response tree constructors -// --------------------------------------------------------------------------- - -pub fn makePure(arena: *Arena, val: u32) !u32 { - const tag = try codecs.ofNumber(arena, 0); - return try arena.alloc(.{ .fork = .{ .left = tag, .right = val } }); -} - -pub fn makeOkResult(arena: *Arena, val: u32) !u32 { - const ok_tag = try arena.alloc(.{ .stem = .{ .child = try arena.alloc(.leaf) } }); - const val_pair = try arena.alloc(.{ .fork = .{ .left = val, .right = try arena.alloc(.leaf) } }); - return try arena.alloc(.{ .fork = .{ .left = ok_tag, .right = val_pair } }); -} - -pub fn makeErrString(arena: *Arena, message: []const u8) !u32 { - return makeErrValue(arena, try codecs.ofString(arena, message)); -} - -fn makeErrValue(arena: *Arena, text: u32) !u32 { - const value = try arena.alloc(.{ .fork = .{ .left = text, .right = try arena.alloc(.leaf) } }); - return try arena.alloc(.{ .fork = .{ .left = try arena.alloc(.leaf), .right = value } }); -} - -fn sourceReadError(arena: *Arena, path: []const u8, reason: []const u8) !u32 { - const message = try std.fmt.allocPrint(arena.allocator, "{s}: {s}", .{ path, reason }); - defer arena.allocator.free(message); - return makeErrValue(arena, try source_text.fromUtf8(arena, message)); -} - -fn sourceReadResult(arena: *Arena, path: []const u8, bytes: []const u8) !u32 { - const scalars = source_text.fromUtf8(arena, bytes) catch |err| switch (err) { - error.InvalidUtf8 => return sourceReadError(arena, path, "invalid UTF-8 source"), - else => return err, - }; - if (std.mem.startsWith(u8, bytes, "\xef\xbb\xbf")) - return sourceReadError(arena, path, "UTF-8 source BOM is not permitted"); - const raw = try codecs.ofBytes(arena, bytes); - const pair = try arena.alloc(.{ .fork = .{ .left = raw, .right = scalars } }); - return makeOkResult(arena, pair); -} - -pub fn makeErrResult(arena: *Arena, code: u64) !u32 { - // The portable IO protocol carries text errors, matching the Haskell - // driver; numeric values remain internal filesystem status codes. - return makeErrString(arena, fsErrorMessage(code)); -} - -// A null callback makes libuv filesystem operations synchronous. Each -// request is cleaned up; temp files live in the destination directory so -// both link (no replace) and rename (replace) are atomic on that filesystem. -const FsWriteResult = union(enum) { written: bool, failure: u64 }; - -const FsReadResult = union(enum) { bytes: []u8, failure: u64 }; - -fn readFs(loop: *c.uv_loop_t, gpa: std.mem.Allocator, path: [:0]const u8) !FsReadResult { - var req: c.uv_fs_t = undefined; - const fd = c.uv_fs_open(loop, &req, path.ptr, c.O_RDONLY, 0, null); - c.uv_fs_req_cleanup(&req); - if (fd < 0) return .{ .failure = mapUvErr(fd) }; - defer { - _ = c.uv_fs_close(loop, &req, fd, null); - c.uv_fs_req_cleanup(&req); - } - var bytes: std.ArrayList(u8) = .empty; - defer bytes.deinit(gpa); - var buffer: [4096]u8 = undefined; - var uv_buffer = c.uv_buf_init(&buffer, buffer.len); - while (true) { - const count = c.uv_fs_read(loop, &req, fd, &uv_buffer, 1, -1, null); - c.uv_fs_req_cleanup(&req); - if (count < 0) return .{ .failure = mapUvErr(count) }; - if (count == 0) return .{ .bytes = try bytes.toOwnedSlice(gpa) }; - try bytes.appendSlice(gpa, buffer[0..@intCast(count)]); - } -} - -fn uvFileError(rc: c_int) u64 { - return mapUvErr(rc); -} - -fn syncFsPath(loop: *c.uv_loop_t, path: [*:0]const u8, directory: bool) ?u64 { - var open_req: c.uv_fs_t = undefined; - const fd = c.uv_fs_open(loop, &open_req, path, c.O_RDONLY | (if (directory) @as(c_int, c.O_DIRECTORY) else 0), 0, null); - c.uv_fs_req_cleanup(&open_req); - if (fd < 0) return uvFileError(fd); - var sync_req: c.uv_fs_t = undefined; - const synced = c.uv_fs_fsync(loop, &sync_req, @intCast(fd), null); - c.uv_fs_req_cleanup(&sync_req); - var close_req: c.uv_fs_t = undefined; - const closed = c.uv_fs_close(loop, &close_req, @intCast(fd), null); - c.uv_fs_req_cleanup(&close_req); - if (synced < 0) return uvFileError(synced); - if (closed < 0) return uvFileError(closed); - return null; -} - -fn syncFsParent(loop: *c.uv_loop_t, gpa: std.mem.Allocator, path: []const u8) !?u64 { - const parent = std.fs.path.dirname(path) orelse "."; - const parent_z = try gpa.dupeZ(u8, parent); - defer gpa.free(parent_z); - return syncFsPath(loop, parent_z, true); -} - -fn writeAtomicFs(loop: *c.uv_loop_t, gpa: std.mem.Allocator, path: [:0]const u8, contents: []const u8, exclusive: bool) !FsWriteResult { - const template = try std.fmt.allocPrintSentinel(gpa, "{s}.tmp.XXXXXX", .{path}, 0); - defer gpa.free(template); - var req: c.uv_fs_t = undefined; - const fd = c.uv_fs_mkstemp(loop, &req, template.ptr, null); - if (fd < 0) { - c.uv_fs_req_cleanup(&req); - return .{ .failure = uvFileError(fd) }; - } - const tmp = gpa.dupeZ(u8, std.mem.span(req.path)) catch |err| { - var close_req: c.uv_fs_t = undefined; - _ = c.uv_fs_close(loop, &close_req, fd, null); - c.uv_fs_req_cleanup(&close_req); - var unlink_req: c.uv_fs_t = undefined; - _ = c.uv_fs_unlink(loop, &unlink_req, req.path, null); - c.uv_fs_req_cleanup(&unlink_req); - c.uv_fs_req_cleanup(&req); - return err; - }; - c.uv_fs_req_cleanup(&req); - defer gpa.free(tmp); - defer { - var unlink_req: c.uv_fs_t = undefined; - _ = c.uv_fs_unlink(loop, &unlink_req, tmp.ptr, null); - c.uv_fs_req_cleanup(&unlink_req); - } - var offset: usize = 0; - var failure: ?u64 = null; - while (offset < contents.len) { - const remain = contents[offset..]; - var buf = c.uv_buf_init(@ptrCast(@constCast(remain.ptr)), @intCast(@min(remain.len, std.math.maxInt(c_uint)))); - var write_req: c.uv_fs_t = undefined; - const n = c.uv_fs_write(loop, &write_req, fd, &buf, 1, @intCast(offset), null); - c.uv_fs_req_cleanup(&write_req); - if (n <= 0) { - failure = if (n == 0) ERR_IO_OTHER else uvFileError(n); - break; - } - offset += @intCast(n); - } - var close_req: c.uv_fs_t = undefined; - const closed = c.uv_fs_close(loop, &close_req, fd, null); - c.uv_fs_req_cleanup(&close_req); - if (failure) |err| return .{ .failure = err }; - if (closed < 0) return .{ .failure = uvFileError(closed) }; - if (syncFsPath(loop, tmp.ptr, false)) |err| return .{ .failure = err }; - var publish_req: c.uv_fs_t = undefined; - const published = if (exclusive) - c.uv_fs_link(loop, &publish_req, tmp.ptr, path.ptr, null) - else - c.uv_fs_rename(loop, &publish_req, tmp.ptr, path.ptr, null); - c.uv_fs_req_cleanup(&publish_req); - if (published == c.UV_EEXIST and exclusive) { - if (syncFsPath(loop, path.ptr, false)) |err| return .{ .failure = err }; - if (try syncFsParent(loop, gpa, path)) |err| return .{ .failure = err }; - return .{ .written = false }; - } - if (published < 0) return .{ .failure = uvFileError(published) }; - if (try syncFsParent(loop, gpa, path)) |err| return .{ .failure = err }; - return .{ .written = true }; -} - -fn fsErrorMessage(err: u64) []const u8 { - return switch (err) { - ERR_DOES_NOT_EXIST => "does not exist", - ERR_PERMISSION, ERR_POLICY_DENY => "permission denied", - ERR_ALREADY_EXISTS => "already exists", - ERR_INVALID_STRING => "invalid string", - ERR_INVALID_ACTION => "invalid action", - else => "io error", - }; -} - -// --------------------------------------------------------------------------- -// Frame stack and runtime -// --------------------------------------------------------------------------- - -const Frame = union(enum) { - bind: u32, // continuation k - local: u32, // old env -}; - -const Runtime = struct { - env: u32, - state: u32, -}; - -// --------------------------------------------------------------------------- -// Task -// --------------------------------------------------------------------------- - -const Task = struct { - id: u64, - parent: ?*Task, - frames: std.ArrayList(Frame), - runtime: Runtime, - current: u32, - status: enum { runnable, blocked, completed }, - result: ?u32, - waiting_for: ?u64, - - fn init(gpa: std.mem.Allocator, id: u64, parent: ?*Task, env: u32, state: u32, current: u32) !*Task { - const task = try gpa.create(Task); - task.* = .{ - .id = id, - .parent = parent, - .frames = std.ArrayList(Frame).empty, - .runtime = .{ .env = env, .state = state }, - .current = current, - .status = .runnable, - .result = null, - .waiting_for = null, - }; - return task; - } - - fn deinit(self: *Task, gpa: std.mem.Allocator) void { - self.frames.deinit(gpa); - gpa.destroy(self); - } - - // finishValue processes a value through the frame stack. - // Returns true if the task has completed (no more frames). - fn finishValue(self: *Task, arena: *Arena, value: u32) !bool { - if (self.frames.pop()) |frame| { - switch (frame) { - .bind => |k| { - self.current = try arena.alloc(.{ .app = .{ .func = k, .arg = value } }); - return false; - }, - .local => |old_env| { - self.runtime.env = old_env; - self.current = try makePure(arena, value); - return false; - }, - } - } else { - self.current = value; - return true; - } - } -}; - -// --------------------------------------------------------------------------- -// Scheduler -// --------------------------------------------------------------------------- - -const Scheduler = struct { - gpa: std.mem.Allocator, - loop: *c.uv_loop_t, - arena: *Arena, - tasks: std.ArrayList(*Task), - runnable: std.ArrayList(*Task), - next_id: u64, - perms: IOPerms, - - fn init(gpa: std.mem.Allocator, loop: *c.uv_loop_t, arena: *Arena, perms: IOPerms) !Scheduler { - const sched = Scheduler{ - .gpa = gpa, - .loop = loop, - .arena = arena, - .tasks = std.ArrayList(*Task).empty, - .runnable = std.ArrayList(*Task).empty, - .next_id = 1, - .perms = perms, - }; - return sched; - } - - fn deinit(self: *Scheduler) void { - for (self.tasks.items) |task| { - task.deinit(self.gpa); - } - self.tasks.deinit(self.gpa); - self.runnable.deinit(self.gpa); - } - - fn createTask(self: *Scheduler, parent: ?*Task, env: u32, state: u32, current: u32) !*Task { - const id = self.next_id; - self.next_id += 1; - const task = try Task.init(self.gpa, id, parent, env, state, current); - errdefer task.deinit(self.gpa); - try self.tasks.append(self.gpa, task); - return task; - } - - fn run(self: *Scheduler) !void { - while (true) { - if (self.runnable.items.len > 0) { - const task = self.runnable.orderedRemove(0); - try self.stepTask(task); - } else if (self.hasPendingHandles()) { - _ = c.uv_run(self.loop, c.UV_RUN_ONCE); - } else { - break; - } - } - } - - fn hasPendingHandles(self: *Scheduler) bool { - return c.uv_loop_alive(self.loop) != 0; - } - - fn completeTask(self: *Scheduler, task: *Task) !void { - task.status = .completed; - task.result = task.current; - // Unblock any tasks waiting for this one - for (self.tasks.items) |t| { - if (t.status == .blocked and t.waiting_for == task.id) { - t.status = .runnable; - t.waiting_for = null; - t.current = try makePure(self.arena, task.result.?); - try self.runnable.append(self.gpa, t); - } - } - } - - fn completeIO(self: *Scheduler, task: *Task, result: u32) !void { - if (try task.finishValue(self.arena, result)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - } - - fn fsReply(self: *Scheduler, task: *Task, err: u64) !void { - try self.completeIO(task, try makeErrString(self.arena, fsErrorMessage(err))); - } - - // Like the native CAS operations, source reads use synchronous libuv calls. - // This avoids entering callback/reducer code while local trees are unrooted; - // allocation and filesystem failures propagate normally to the host. - // A source read occupies one scheduler turn (it is not a streaming action). - fn readSource(self: *Scheduler, task: *Task, path_tree: u32) !void { - const path = source_text.toUtf8(self.arena, path_tree) catch |err| switch (err) { - error.InvalidScalarList => return self.fsReply(task, ERR_INVALID_STRING), - else => return err, - }; - defer self.arena.allocator.free(path); - if (!self.perms.allow_read_all) return self.fsReply(task, ERR_POLICY_DENY); - if (std.mem.indexOfScalar(u8, path, 0) != null) return self.fsReply(task, ERR_INVALID_STRING); - const path_z = try self.gpa.dupeZ(u8, path); - defer self.gpa.free(path_z); - const read = try readFs(self.loop, self.gpa, path_z); - switch (read) { - .failure => |code| try self.fsReply(task, code), - .bytes => |bytes| { - defer self.gpa.free(bytes); - try self.completeIO(task, try sourceReadResult(self.arena, path, bytes)); - }, - } - } - - // IO protocol adapters: codec and filesystem helpers know nothing about - // compiler manifests, cache aliases, path selection or publication order. - fn indexedIO(self: *Scheduler, task: *Task, path_tree: u32, expected_tree: u32, value: ?u32) !void { - const path = source_text.toUtf8(self.arena, path_tree) catch |err| switch (err) { - error.InvalidScalarList => return self.fsReply(task, ERR_INVALID_STRING), - else => return err, - }; - defer self.arena.allocator.free(path); - if (std.mem.indexOfScalar(u8, path, 0) != null) return self.fsReply(task, ERR_INVALID_STRING); - const expected = source_text.toUtf8(self.arena, expected_tree) catch |err| switch (err) { - error.InvalidScalarList => return self.completeIO(task, try makeErrString(self.arena, "invalid CAS digest")), - else => return err, - }; - defer self.arena.allocator.free(expected); - if (expected.len != 64) return self.completeIO(task, try makeErrString(self.arena, "invalid CAS digest")); - for (expected) |ch| if (!(ch >= '0' and ch <= '9' or ch >= 'a' and ch <= 'f')) - return self.completeIO(task, try makeErrString(self.arena, "invalid CAS digest")); - if (!self.perms.allow_read_all or (value != null and !self.perms.allow_write_all)) return self.fsReply(task, ERR_POLICY_DENY); - const path_z = try self.gpa.dupeZ(u8, path); - defer self.gpa.free(path_z); - var encoded: ?[]u8 = null; - defer if (encoded) |bytes| self.arena.allocator.free(bytes); - var fresh = false; - if (value) |tree_value| { - encoded = try indexed.encode(self.arena, tree_value); - if (!std.mem.eql(u8, &indexed.digest(encoded.?), expected)) - return self.completeIO(task, try makeErrString(self.arena, "CAS digest mismatch")); - switch (try writeAtomicFs(self.loop, self.gpa, path_z, encoded.?, true)) { - .failure => |code| return self.fsReply(task, code), - .written => |written| fresh = written, - } - } - // Authenticate before decode; for publication compare the actual winner - // byte-for-byte, including after an EEXIST race. Never overwrite it. - switch (try readFs(self.loop, self.gpa, path_z)) { - .failure => |code| return self.fsReply(task, code), - .bytes => |bytes| { - defer self.gpa.free(bytes); - const result = if (encoded) |ours| blk: { - if (!std.mem.eql(u8, bytes, ours)) return self.completeIO(task, try makeErrString(self.arena, "CAS object exists with different bytes")); - break :blk if (fresh) try self.arena.alloc(.{ .stem = .{ .child = try self.arena.alloc(.leaf) } }) else try self.arena.alloc(.leaf); - } else blk: { - if (!std.mem.eql(u8, &indexed.digest(bytes), expected)) return self.completeIO(task, try makeErrString(self.arena, "CAS digest mismatch")); - break :blk indexed.load(self.arena, bytes, "root", true) catch |err| switch (err) { - error.OutOfMemory => return err, - else => return self.completeIO(task, try makeErrString(self.arena, "invalid indexed tree")), - }; - }; - try self.completeIO(task, try makeOkResult(self.arena, result)); - }, - } - } - - fn stepTask(self: *Scheduler, task: *Task) !void { - // Snapshot every scheduler-owned node for this turn. A turn may force - // repeatedly; frames popped during it remain rooted until it completes. - // Callbacks only allocate/link trees and never enter the reducer. - const base = self.arena.roots.items.len; - defer self.arena.roots.shrinkRetainingCapacity(base); - for (self.tasks.items) |t| { - try self.arena.roots.appendSlice(self.gpa, &.{ t.current, t.runtime.env, t.runtime.state }); - if (t.result) |result| try self.arena.roots.append(self.gpa, result); - for (t.frames.items) |frame| switch (frame) { - .bind => |k| try self.arena.roots.append(self.gpa, k), - .local => |env| try self.arena.roots.append(self.gpa, env), - }; - } - const reduced = try reduce.reduce(task.current, self.arena, std.math.maxInt(u64)); - - const decoded = try decodeAction(self.arena, reduced); - if (decoded == null) { - // Not a recognized action — if no frames, it's the final result. - // Otherwise treat as invalid. - if (task.frames.items.len == 0) { - task.current = reduced; - try self.completeTask(task); - return; - } - const invalid = self.arena.get(reduced).*; - const tag = if (invalid == .fork) try codecs.toNumber(self.arena, invalid.fork.left) else null; - var diagnostic: [96]u8 = undefined; - const message = if (tag) |n| try std.fmt.bufPrint(&diagnostic, "invalid or unsupported IO action tag {d}", .{n}) else "invalid action"; - const err = try makeErrString(self.arena, message); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - } - - const action = decoded.?; - switch (action) { - .pure => |val| { - if (try task.finishValue(self.arena, val)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - }, - - .bind => |b| { - try task.frames.append(self.gpa, .{ .bind = b.k }); - task.current = b.left; - try self.runnable.append(self.gpa, task); - }, - - .putStr => |str_tree| { - const str = try codecs.toString(self.arena, str_tree) orelse { - const err = try makeErrResult(self.arena, ERR_INVALID_STRING); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - }; - defer self.gpa.free(str); - var written: usize = 0; - while (written < str.len) { - const n = std.c.write(1, str[written..].ptr, str.len - written); - if (n <= 0) return error.WriteFailed; - written += @intCast(n); - } - const leaf = try self.arena.alloc(.leaf); - if (try task.finishValue(self.arena, leaf)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - }, - - .getLine => { - var buf: [4096]u8 = undefined; - var len: usize = 0; - while (len < buf.len) { - const n = std.c.read(0, buf[len..].ptr, 1); - if (n <= 0) break; - if (buf[len] == '\n') break; - len += 1; - } - const line = buf[0..len]; - const str_tree = try codecs.ofString(self.arena, line); - if (try task.finishValue(self.arena, str_tree)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - }, - - .readSourceFile => |path_tree| try self.readSource(task, path_tree), - .readIndexedTree => |read| try self.indexedIO(task, read.path, read.digest, null), - .writeIndexedTreeIfAbsent => |write| try self.indexedIO(task, write.path, write.digest, write.tree), - .hashIndexedTree => |value| { - const bytes = try indexed.encode(self.arena, value); - defer self.arena.allocator.free(bytes); - const hex = indexed.digest(bytes); - try self.completeIO(task, try makeOkResult(self.arena, try codecs.ofString(self.arena, &hex))); - }, - - .encodeSourceText => |text| { - const bytes = source_text.toUtf8(self.arena, text) catch |err| switch (err) { - error.InvalidScalarList => { - try self.completeIO(task, try makeErrString(self.arena, "invalid source scalar list")); - return; - }, - else => return err, - }; - defer self.arena.allocator.free(bytes); - try self.completeIO(task, try makeOkResult(self.arena, try codecs.ofBytes(self.arena, bytes))); - }, - - .readFile => |path_tree| { - const path = try codecs.toString(self.arena, path_tree) orelse { - const err = try makeErrResult(self.arena, ERR_INVALID_STRING); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - }; - - if (!self.perms.allow_read_all) { - self.arena.allocator.free(path); - const err = try makeErrResult(self.arena, ERR_POLICY_DENY); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - } - - const ctx = try self.gpa.create(FileReadCtx); - ctx.* = .{ - .scheduler = self, - .task = task, - .arena = self.arena, - .gpa = self.gpa, - .fd = -1, - .buf = std.ArrayList(u8).empty, - .path = try self.gpa.dupeZ(u8, path), - .req = undefined, - .read_buf = null, - }; - self.arena.allocator.free(path); - ctx.req.data = ctx; - _ = c.uv_fs_open(self.loop, &ctx.req, ctx.path.ptr, c.O_RDONLY, 0, file_open_cb); - }, - - .writeFile => |wf| { - const path = try codecs.toString(self.arena, wf.path) orelse { - const err = try makeErrResult(self.arena, ERR_INVALID_STRING); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - }; - - const contents = try codecs.toString(self.arena, wf.contents) orelse { - self.arena.allocator.free(path); - const err = try makeErrResult(self.arena, ERR_INVALID_STRING); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - }; - - if (!self.perms.allow_write_all) { - self.arena.allocator.free(path); - self.arena.allocator.free(contents); - const err = try makeErrResult(self.arena, ERR_POLICY_DENY); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - } - - const ctx = try self.gpa.create(FileWriteCtx); - ctx.* = .{ - .scheduler = self, - .task = task, - .arena = self.arena, - .gpa = self.gpa, - .fd = -1, - .path = try self.gpa.dupeZ(u8, path), - .contents = contents, - .offset = 0, - .req = undefined, - }; - self.arena.allocator.free(path); - ctx.req.data = ctx; - const flags = c.O_WRONLY | c.O_CREAT | c.O_TRUNC; - _ = c.uv_fs_open(self.loop, &ctx.req, ctx.path.ptr, flags, 0o644, file_write_open_cb); - }, - - .writeBytes, .writeBytesIfAbsent, .atomicWriteBytes => |write| { - const path = try codecs.toString(self.arena, write.path) orelse { - try self.fsReply(task, ERR_INVALID_STRING); - return; - }; - defer self.arena.allocator.free(path); - const contents = try codecs.toBytes(self.arena, write.contents) orelse { - try self.completeIO(task, try makeErrString(self.arena, "invalid bytes")); - return; - }; - defer self.arena.allocator.free(contents); - if (!self.perms.allow_write_all) { - try self.fsReply(task, ERR_POLICY_DENY); - return; - } - const path_z = try self.gpa.dupeZ(u8, path); - defer self.gpa.free(path_z); - const exclusive = action == .writeBytesIfAbsent; - const published = try writeAtomicFs(self.loop, self.gpa, path_z, contents, exclusive); - switch (published) { - .failure => |code| try self.fsReply(task, code), - .written => |fresh| { - const value = if (action == .writeBytes) try self.arena.alloc(.leaf) else if (fresh) try self.arena.alloc(.{ .stem = .{ .child = try self.arena.alloc(.leaf) } }) else try self.arena.alloc(.leaf); - try self.completeIO(task, try makeOkResult(self.arena, value)); - }, - } - }, - - .createDirectory, .fileExists => |path_tree| { - const path = try codecs.toString(self.arena, path_tree) orelse { - try self.fsReply(task, ERR_INVALID_STRING); - return; - }; - defer self.arena.allocator.free(path); - const is_create = action == .createDirectory; - if (is_create and !self.perms.allow_write_all or - !is_create and !self.perms.allow_read_all) - { - try self.fsReply(task, ERR_POLICY_DENY); - return; - } - const path_z = try self.gpa.dupeZ(u8, path); - defer self.gpa.free(path_z); - var req: c.uv_fs_t = undefined; - const rc = if (is_create) c.uv_fs_mkdir(self.loop, &req, path_z.ptr, 0o755, null) else c.uv_fs_stat(self.loop, &req, path_z.ptr, null); - c.uv_fs_req_cleanup(&req); - if (rc < 0 and (is_create and rc != c.UV_EEXIST or - !is_create and rc != c.UV_ENOENT)) - { - try self.fsReply(task, uvFileError(rc)); - return; - } - if (is_create and rc == c.UV_EEXIST) { - var stat_req: c.uv_fs_t = undefined; - const stat = c.uv_fs_stat(self.loop, &stat_req, path_z.ptr, null); - const is_dir = stat >= 0 and stat_req.statbuf.st_mode & c.S_IFMT == c.S_IFDIR; - c.uv_fs_req_cleanup(&stat_req); - if (!is_dir) { - try self.fsReply(task, ERR_ALREADY_EXISTS); - return; - } - } - if (is_create) { - if (syncFsPath(self.loop, path_z.ptr, true)) |err| { - try self.fsReply(task, err); - return; - } - if (try syncFsParent(self.loop, self.gpa, path_z)) |err| { - try self.fsReply(task, err); - return; - } - } - const value = if (is_create) try self.arena.alloc(.leaf) else if (rc == 0) try self.arena.alloc(.{ .stem = .{ .child = try self.arena.alloc(.leaf) } }) else try self.arena.alloc(.leaf); - try self.completeIO(task, try makeOkResult(self.arena, value)); - }, - - .renameFile => |paths| { - const old = try codecs.toString(self.arena, paths.old) orelse { - try self.fsReply(task, ERR_INVALID_STRING); - return; - }; - defer self.arena.allocator.free(old); - const new = try codecs.toString(self.arena, paths.new) orelse { - try self.fsReply(task, ERR_INVALID_STRING); - return; - }; - defer self.arena.allocator.free(new); - if (!self.perms.allow_write_all) { - try self.fsReply(task, ERR_POLICY_DENY); - return; - } - const old_z = try self.gpa.dupeZ(u8, old); - defer self.gpa.free(old_z); - const new_z = try self.gpa.dupeZ(u8, new); - defer self.gpa.free(new_z); - var req: c.uv_fs_t = undefined; - const rc = c.uv_fs_rename(self.loop, &req, old_z.ptr, new_z.ptr, null); - c.uv_fs_req_cleanup(&req); - if (rc < 0) return self.fsReply(task, uvFileError(rc)); - try self.completeIO(task, try makeOkResult(self.arena, try self.arena.alloc(.leaf))); - }, - - .sha256Hex => |bytes_tree| { - const bytes = try codecs.toBytes(self.arena, bytes_tree) orelse { - try self.completeIO(task, try makeErrString(self.arena, "invalid bytes")); - return; - }; - defer self.arena.allocator.free(bytes); - var digest: [32]u8 = undefined; - std.crypto.hash.sha2.Sha256.hash(bytes, &digest, .{}); - const hex = std.fmt.bytesToHex(digest, .lower); - try self.completeIO(task, try makeOkResult(self.arena, try codecs.ofString(self.arena, &hex))); - }, - - .ask => { - if (try task.finishValue(self.arena, task.runtime.env)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - }, - - .local => |loc| { - const new_env = try reduce.reduce(try self.arena.alloc(.{ .app = .{ .func = loc.f, .arg = task.runtime.env } }), self.arena, std.math.maxInt(u64)); - try task.frames.append(self.gpa, .{ .local = task.runtime.env }); - task.runtime.env = new_env; - task.current = loc.action; - try self.runnable.append(self.gpa, task); - }, - - .get => { - if (try task.finishValue(self.arena, task.runtime.state)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - }, - - .put => |new_state| { - task.runtime.state = new_state; - const leaf = try self.arena.alloc(.leaf); - if (try task.finishValue(self.arena, leaf)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - }, - - .fork => |child_action| { - const child = try self.createTask(task, task.runtime.env, task.runtime.state, child_action); - try self.runnable.append(self.gpa, child); - const handle = try codecs.ofNumber(self.arena, child.id); - if (try task.finishValue(self.arena, handle)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - }, - - .await => |handle_tree| { - const handle = try codecs.toNumber(self.arena, handle_tree) orelse { - const err = try makeErrResult(self.arena, ERR_INVALID_ACTION); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - }; - var found: ?*Task = null; - for (self.tasks.items) |t| { - if (t.id == handle) { - found = t; - break; - } - } - if (found == null) { - const err = try makeErrResult(self.arena, ERR_INVALID_ACTION); - if (try task.finishValue(self.arena, err)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - return; - } - if (found.?.status == .completed) { - const result = found.?.result.?; - if (try task.finishValue(self.arena, result)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - } else { - task.status = .blocked; - task.waiting_for = handle; - // Task remains out of runnable until child completes - } - }, - - .yield => { - const leaf = try self.arena.alloc(.leaf); - if (try task.finishValue(self.arena, leaf)) { - try self.completeTask(task); - } else { - try self.runnable.append(self.gpa, task); - } - }, - - .sleep => |ms_tree| { - const ms = try codecs.toNumber(self.arena, ms_tree) orelse 0; - const ctx = try self.gpa.create(SleepCtx); - ctx.* = .{ - .scheduler = self, - .task = task, - .arena = self.arena, - .timer = undefined, - }; - ctx.timer.data = ctx; - _ = c.uv_timer_init(self.loop, &ctx.timer); - _ = c.uv_timer_start(&ctx.timer, sleep_cb, @intCast(ms), 0); - }, - } - } -}; - -// --------------------------------------------------------------------------- -// Async file read -// --------------------------------------------------------------------------- - -const FileReadCtx = struct { - scheduler: *Scheduler, - task: *Task, - arena: *Arena, - gpa: std.mem.Allocator, - fd: c_int, - buf: std.ArrayList(u8), - path: [:0]const u8, - req: c.uv_fs_t, - read_buf: ?[]u8, -}; - -fn mapUvErr(uv_err: c_int) u64 { - return switch (uv_err) { - c.UV_ENOENT => ERR_DOES_NOT_EXIST, - c.UV_EACCES => ERR_PERMISSION, - c.UV_EEXIST => ERR_ALREADY_EXISTS, - else => ERR_IO_OTHER, - }; -} - -fn file_open_cb(req: [*c]c.uv_fs_t) callconv(.c) void { - const ctx = @as(*FileReadCtx, @ptrCast(@alignCast(req.*.data))); - const result = req.*.result; - c.uv_fs_req_cleanup(req); - if (result < 0) { - const err = makeErrResult(ctx.arena, mapUvErr(@intCast(result))) catch { - ctx.gpa.destroy(ctx); - return; - }; - if (ctx.task.finishValue(ctx.arena, err) catch false) { - ctx.scheduler.completeTask(ctx.task) catch {}; - } else { - ctx.scheduler.runnable.append(ctx.scheduler.gpa, ctx.task) catch {}; - } - ctx.buf.deinit(ctx.gpa); - ctx.gpa.free(ctx.path); - ctx.gpa.destroy(ctx); - return; - } - ctx.fd = @intCast(result); - const read_buf = ctx.gpa.alloc(u8, 4096) catch unreachable; - ctx.read_buf = read_buf; - var uv_buf = c.uv_buf_init(@ptrCast(read_buf.ptr), @intCast(read_buf.len)); - _ = c.uv_fs_read(ctx.scheduler.loop, req, ctx.fd, &uv_buf, 1, -1, file_read_cb); -} - -fn file_read_cb(req: [*c]c.uv_fs_t) callconv(.c) void { - const ctx = @as(*FileReadCtx, @ptrCast(@alignCast(req.*.data))); - const nread = req.*.result; - c.uv_fs_req_cleanup(req); - if (nread < 0) { - _ = c.uv_fs_close(ctx.scheduler.loop, req, ctx.fd, null); - const err = makeErrResult(ctx.arena, mapUvErr(@intCast(nread))) catch { - ctx.gpa.destroy(ctx); - return; - }; - if (ctx.task.finishValue(ctx.arena, err) catch false) { - ctx.scheduler.completeTask(ctx.task) catch {}; - } else { - ctx.scheduler.runnable.append(ctx.scheduler.gpa, ctx.task) catch {}; - } - if (ctx.read_buf) |b| ctx.gpa.free(b); - ctx.buf.deinit(ctx.gpa); - ctx.gpa.free(ctx.path); - ctx.gpa.destroy(ctx); - return; - } - if (nread == 0) { - // EOF - _ = c.uv_fs_close(ctx.scheduler.loop, req, ctx.fd, null); - const bytes_tree = codecs.ofBytes(ctx.arena, ctx.buf.items) catch { - ctx.gpa.destroy(ctx); - return; - }; - const ok = makeOkResult(ctx.arena, bytes_tree) catch { - ctx.gpa.destroy(ctx); - return; - }; - if (ctx.task.finishValue(ctx.arena, ok) catch false) { - ctx.scheduler.completeTask(ctx.task) catch {}; - } else { - ctx.scheduler.runnable.append(ctx.scheduler.gpa, ctx.task) catch {}; - } - if (ctx.read_buf) |b| ctx.gpa.free(b); - ctx.buf.deinit(ctx.gpa); - ctx.gpa.free(ctx.path); - ctx.gpa.destroy(ctx); - return; - } - const data = ctx.read_buf.?[0..@intCast(nread)]; - ctx.buf.appendSlice(ctx.gpa, data) catch unreachable; - ctx.gpa.free(ctx.read_buf.?); - const read_buf = ctx.gpa.alloc(u8, 4096) catch unreachable; - ctx.read_buf = read_buf; - var uv_buf = c.uv_buf_init(@ptrCast(read_buf.ptr), @intCast(read_buf.len)); - _ = c.uv_fs_read(ctx.scheduler.loop, req, ctx.fd, &uv_buf, 1, -1, file_read_cb); -} - -// --------------------------------------------------------------------------- -// Async file write -// --------------------------------------------------------------------------- - -const FileWriteCtx = struct { - scheduler: *Scheduler, - task: *Task, - arena: *Arena, - gpa: std.mem.Allocator, - fd: c_int, - path: [:0]const u8, - contents: []const u8, - offset: usize, - req: c.uv_fs_t, -}; - -fn file_write_open_cb(req: [*c]c.uv_fs_t) callconv(.c) void { - const ctx = @as(*FileWriteCtx, @ptrCast(@alignCast(req.*.data))); - const result = req.*.result; - c.uv_fs_req_cleanup(req); - if (result < 0) { - const err = makeErrResult(ctx.arena, mapUvErr(@intCast(result))) catch { - ctx.gpa.destroy(ctx); - return; - }; - if (ctx.task.finishValue(ctx.arena, err) catch false) { - ctx.scheduler.completeTask(ctx.task) catch {}; - } else { - ctx.scheduler.runnable.append(ctx.scheduler.gpa, ctx.task) catch {}; - } - ctx.gpa.free(ctx.path); - ctx.gpa.free(ctx.contents); - ctx.gpa.destroy(ctx); - return; - } - ctx.fd = @intCast(result); - if (ctx.contents.len == 0) { - _ = c.uv_fs_close(ctx.scheduler.loop, req, ctx.fd, file_write_close_cb); - return; - } - file_write_next(ctx, req); -} - -fn file_write_next(ctx: *FileWriteCtx, req: [*c]c.uv_fs_t) void { - const remaining = ctx.contents[ctx.offset..]; - var uv_buf = c.uv_buf_init(@ptrCast(@constCast(remaining.ptr)), @intCast(@min(remaining.len, std.math.maxInt(c_uint)))); - _ = c.uv_fs_write(ctx.scheduler.loop, req, ctx.fd, &uv_buf, 1, @intCast(ctx.offset), file_write_cb); -} - -fn file_write_cb(req: [*c]c.uv_fs_t) callconv(.c) void { - const ctx = @as(*FileWriteCtx, @ptrCast(@alignCast(req.*.data))); - const nwrite = req.*.result; - c.uv_fs_req_cleanup(req); - if (nwrite <= 0) { - _ = c.uv_fs_close(ctx.scheduler.loop, req, ctx.fd, null); - const err = makeErrResult(ctx.arena, if (nwrite == 0) ERR_IO_OTHER else mapUvErr(@intCast(nwrite))) catch { - ctx.gpa.destroy(ctx); - return; - }; - if (ctx.task.finishValue(ctx.arena, err) catch false) { - ctx.scheduler.completeTask(ctx.task) catch {}; - } else { - ctx.scheduler.runnable.append(ctx.scheduler.gpa, ctx.task) catch {}; - } - ctx.gpa.free(ctx.path); - ctx.gpa.free(ctx.contents); - ctx.gpa.destroy(ctx); - return; - } - ctx.offset += @intCast(nwrite); - if (ctx.offset < ctx.contents.len) { - file_write_next(ctx, req); - return; - } - _ = c.uv_fs_close(ctx.scheduler.loop, req, ctx.fd, file_write_close_cb); -} - -fn file_write_close_cb(req: [*c]c.uv_fs_t) callconv(.c) void { - const ctx = @as(*FileWriteCtx, @ptrCast(@alignCast(req.*.data))); - c.uv_fs_req_cleanup(req); - const leaf = ctx.arena.alloc(.leaf) catch { - ctx.gpa.destroy(ctx); - return; - }; - const ok = makeOkResult(ctx.arena, leaf) catch { - ctx.gpa.destroy(ctx); - return; - }; - if (ctx.task.finishValue(ctx.arena, ok) catch false) { - ctx.scheduler.completeTask(ctx.task) catch {}; - } else { - ctx.scheduler.runnable.append(ctx.scheduler.gpa, ctx.task) catch {}; - } - ctx.gpa.free(ctx.path); - ctx.gpa.free(ctx.contents); - ctx.gpa.destroy(ctx); -} - -// --------------------------------------------------------------------------- -// Async sleep -// --------------------------------------------------------------------------- - -const SleepCtx = struct { - scheduler: *Scheduler, - task: *Task, - arena: *Arena, - timer: c.uv_timer_t, -}; - -fn sleep_cb(handle: [*c]c.uv_timer_t) callconv(.c) void { - const ctx = @as(*SleepCtx, @ptrCast(@alignCast(handle.*.data))); - defer ctx.scheduler.gpa.destroy(ctx); - const leaf = ctx.arena.alloc(.leaf) catch { - ctx.scheduler.runnable.append(ctx.scheduler.gpa, ctx.task) catch {}; - return; - }; - if (ctx.task.finishValue(ctx.arena, leaf) catch false) { - ctx.scheduler.completeTask(ctx.task) catch {}; - } else { - ctx.scheduler.runnable.append(ctx.scheduler.gpa, ctx.task) catch {}; - } -} - -// --------------------------------------------------------------------------- -// Public entry point -// --------------------------------------------------------------------------- - -pub fn runIO(gpa: std.mem.Allocator, arena: *Arena, program: u32, perms: IOPerms) !u32 { - const base = arena.roots.items.len; - defer arena.roots.shrinkRetainingCapacity(base); - try arena.roots.append(gpa, program); - const action_tree = try isIOSentinel(arena, program) orelse { - return error.InvalidIOSentinel; - }; - - var loop: c.uv_loop_t = undefined; - const rc = c.uv_loop_init(&loop); - if (rc != 0) return error.LoopInitFailed; - defer _ = c.uv_loop_close(&loop); - - var scheduler = try Scheduler.init(gpa, &loop, arena, perms); - defer scheduler.deinit(); - - const main_task = try scheduler.createTask(null, try arena.alloc(.leaf), try arena.alloc(.leaf), action_tree); - try scheduler.runnable.append(gpa, main_task); - - try scheduler.run(); - - // Return the main task's result - return main_task.result orelse program; -} - -test "scheduler safepoint roots include blocked tasks, frames, environment, state and results" { - const testing = std.testing; - var arena = Arena.init(testing.allocator); - defer arena.deinit(); - var loop: c.uv_loop_t = undefined; - try testing.expectEqual(@as(c_int, 0), c.uv_loop_init(&loop)); - defer _ = c.uv_loop_close(&loop); - var scheduler = try Scheduler.init(testing.allocator, &loop, &arena, .{}); - defer scheduler.deinit(); - const leaf = try arena.alloc(.leaf); - const external = try codecs.ofNumber(&arena, 99); - try arena.roots.append(arena.allocator, external); - const value = try codecs.ofNumber(&arena, 42); - const action = try makePure(&arena, value); - const task = try scheduler.createTask(null, leaf, leaf, action); - const blocked = try scheduler.createTask(null, try codecs.ofNumber(&arena, 17), try codecs.ofNumber(&arena, 23), leaf); - blocked.status = .blocked; - blocked.result = try codecs.ofNumber(&arena, 31); - const saved_env = try codecs.ofNumber(&arena, 47); - const continuation = try codecs.ofNumber(&arena, 53); - try blocked.frames.append(arena.allocator, .{ .local = saved_env }); - try blocked.frames.append(arena.allocator, .{ .bind = continuation }); - for (0..1000) |_| _ = try arena.alloc(.leaf); - const before_collection = arena.len(); - arena.gc_enabled = true; - try arena.setGcPolicy(1, 1); - try scheduler.stepTask(task); - try testing.expect(arena.collections > 0); - try testing.expect(before_collection - arena.len() + arena.free.items.len >= 1000); - try testing.expectEqual(@as(usize, 1), arena.roots.items.len); - // Observations below are outside a scheduler turn; explicitly hold its - // roots by stopping automatic collection, not by accidentally relying on - // an allocation threshold. - arena.gc_enabled = false; - const ids = [_]u32{ external, task.result.?, blocked.runtime.env, blocked.runtime.state, blocked.result.?, saved_env, continuation }; - const expected = [_]u64{ 99, 42, 17, 23, 31, 47, 53 }; - for (ids, expected) |id, n| try testing.expectEqual(n, (try codecs.toNumber(&arena, id)).?); -} - -test "IO sentinel forces delayed outer and version/action pair" { - const testing = std.testing; - var arena = Arena.init(testing.allocator); - defer arena.deinit(); - const leaf = try arena.alloc(.leaf); - const value = try codecs.ofNumber(&arena, 42); - const action = try makePure(&arena, value); - const version = try codecs.ofNumber(&arena, 1); - const pair = try arena.alloc(.{ .fork = .{ .left = version, .right = action } }); - const k_pair = try arena.alloc(.{ .fork = .{ .left = leaf, .right = pair } }); - const delayed_pair = try arena.alloc(.{ .app = .{ .func = k_pair, .arg = leaf } }); - const marker = try codecs.ofString(&arena, "tricuIO"); - const sentinel = try arena.alloc(.{ .fork = .{ .left = marker, .right = delayed_pair } }); - const k_sentinel = try arena.alloc(.{ .fork = .{ .left = leaf, .right = sentinel } }); - const program = try arena.alloc(.{ .app = .{ .func = k_sentinel, .arg = leaf } }); - arena.gc_enabled = true; - try arena.setGcPolicy(1, 1); - const result = try runIO(arena.allocator, &arena, program, .{}); - arena.gc_enabled = false; // result is held locally, outside a registered scope - try testing.expectEqual(@as(u64, 42), (try codecs.toNumber(&arena, result)).?); - try testing.expectEqual(@as(usize, 0), arena.roots.items.len); -} - -fn testAction(arena: *Arena, tag: u64, payload: u32, perms: IOPerms) !codecs.UnwrapResult { - const action = try arena.alloc(.{ .fork = .{ .left = try codecs.ofNumber(arena, tag), .right = payload } }); - const rest = try arena.alloc(.{ .fork = .{ .left = try codecs.ofNumber(arena, 1), .right = action } }); - const program = try arena.alloc(.{ .fork = .{ .left = try codecs.ofString(arena, "tricuIO"), .right = rest } }); - arena.gc_enabled = true; - try arena.setGcPolicy(1, 1); - const result = try runIO(arena.allocator, arena, program, perms); - arena.gc_enabled = false; // observations are local, outside the scheduler - return (try codecs.unwrapResult(arena, result)).?; -} - -fn expectErrorText(arena: *Arena, reply: codecs.UnwrapResult, text: []const u8) !void { - try std.testing.expect(!reply.ok); - const message = try source_text.toUtf8(arena, reply.value); - defer arena.allocator.free(message); - try std.testing.expect(std.mem.indexOf(u8, message, text) != null); -} - -test "encodeSourceText is strict, lazy, permission-free and distinct from byte codecs" { - var arena = Arena.init(std.testing.allocator); - defer arena.deinit(); - const expected = "\u{feff}aé😀\x00\r\ne\u{301}"; - const list = try source_text.fromUtf8(&arena, expected); - const leaf = try arena.alloc(.leaf); - const constant = try arena.alloc(.{ .fork = .{ .left = leaf, .right = list } }); - const delayed = try arena.alloc(.{ .app = .{ .func = constant, .arg = leaf } }); - const result = try testAction(&arena, 38, delayed, .{}); - try std.testing.expect(result.ok); - const bytes = (try codecs.toBytes(&arena, result.value)).?; - defer arena.allocator.free(bytes); - try std.testing.expectEqualStrings(expected, bytes); - for ([_]u64{ 0xd800, 0x110000 }) |n| { - const bad = try codecs.ofList(&arena, &.{try codecs.ofNumber(&arena, n)}); - try expectErrorText(&arena, try testAction(&arena, 38, bad, .{}), "invalid source scalar list"); - } - // Noncanonical zero has the right numeric value, but is not a scalar tree. - const zero = try arena.alloc(.leaf); - const redundant = try arena.alloc(.{ .fork = .{ .left = zero, .right = zero } }); - const bad = try codecs.ofList(&arena, &.{redundant}); - try expectErrorText(&arena, try testAction(&arena, 38, bad, .{}), "invalid source scalar list"); -} - -test "readSourceFile preserves bytes and scalars, rejects malformed input, and enforces read permission" { - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); - const directory = try tmp.dir.realPathFileAlloc(std.testing.io, ".", std.testing.allocator); - defer std.testing.allocator.free(directory); - const path = try std.fmt.allocPrint(std.testing.allocator, "{s}/é😀.tri", .{directory}); - defer std.testing.allocator.free(path); - var arena = Arena.init(std.testing.allocator); - defer arena.deinit(); - var bytes: std.ArrayList(u8) = .empty; - defer bytes.deinit(std.testing.allocator); - try bytes.appendNTimes(std.testing.allocator, 'a', 4095); - try bytes.appendSlice(std.testing.allocator, "😀\r\né\x00\u{feff}e\u{301}"); - for ([_][]const u8{ bytes.items, "" }) |contents| { - try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "é😀.tri", .data = contents }); - const reply = try testAction(&arena, 37, try source_text.fromUtf8(&arena, path), .{ .allow_read_all = true }); - try std.testing.expect(reply.ok); - const pair = arena.get(reply.value).fork; - const raw = (try codecs.toBytes(&arena, pair.left)).?; - defer arena.allocator.free(raw); - const text = try source_text.toUtf8(&arena, pair.right); - defer arena.allocator.free(text); - try std.testing.expectEqualStrings(contents, raw); - try std.testing.expectEqualStrings(contents, text); - } - try expectErrorText(&arena, try testAction(&arena, 37, try source_text.fromUtf8(&arena, path), .{}), "permission denied"); - for ([_][]const u8{ "\x80", "\xc0\xaf", "\xe2\x82", "\xed\xa0\x80", "\xf4\x90\x80\x80", "\xef\xbb\xbfok" }) |contents| { - try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "é😀.tri", .data = contents }); - const reply = try testAction(&arena, 37, try source_text.fromUtf8(&arena, path), .{ .allow_read_all = true }); - try expectErrorText(&arena, reply, path); - try expectErrorText(&arena, reply, if (std.mem.startsWith(u8, contents, "\xef\xbb\xbf")) "BOM is not permitted" else "invalid UTF-8 source"); - } - // An embedded NUL must not turn a different path into the existing file. - const nul_path = try std.fmt.allocPrint(std.testing.allocator, "{s}\x00ignored", .{path}); - defer std.testing.allocator.free(nul_path); - try expectErrorText(&arena, try testAction(&arena, 37, try source_text.fromUtf8(&arena, nul_path), .{ .allow_read_all = true }), "invalid string"); - try tmp.dir.deleteFile(std.testing.io, "é😀.tri"); - try expectErrorText(&arena, try testAction(&arena, 37, try source_text.fromUtf8(&arena, path), .{ .allow_read_all = true }), "does not exist"); - // Ordinary readFile remains a byte transport: malformed UTF-8 and BOMs - // are not interpreted. Use its existing byte-oriented path convention. - const binary = "\xff\x00\xef\xbb\xbf\r\n"; - try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "é😀.tri", .data = binary }); - const reply = try testAction(&arena, 20, try codecs.ofString(&arena, path), .{ .allow_read_all = true }); - try std.testing.expect(reply.ok); - const raw = (try codecs.toBytes(&arena, reply.value)).?; - defer arena.allocator.free(raw); - try std.testing.expectEqualStrings(binary, raw); -} - -fn sourceIOAllocationProbe(allocator: std.mem.Allocator, path: []const u8) !void { - var arena = Arena.init(allocator); - defer arena.deinit(); - const reply = try testAction(&arena, 37, try source_text.fromUtf8(&arena, path), .{ .allow_read_all = true }); - try std.testing.expect(reply.ok); - const scalars = arena.get(reply.value).fork.right; - const encoded = try testAction(&arena, 38, scalars, .{}); - try std.testing.expect(encoded.ok); - const raw = (try codecs.toBytes(&arena, encoded.value)).?; - defer allocator.free(raw); - try std.testing.expectEqualStrings("aé😀", raw); -} - -test "source IO unwinds allocation failures without leaking tasks, buffers or requests" { - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); - try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "source.tri", .data = "aé😀" }); - const path = try tmp.dir.realPathFileAlloc(std.testing.io, "source.tri", std.testing.allocator); - defer std.testing.allocator.free(path); - try std.testing.checkAllAllocationFailures(std.testing.allocator, sourceIOAllocationProbe, .{path}); -} - -fn testIndexedAction(arena: *Arena, tag: u64, path: []const u8, hash: []const u8, value: u32, perms: IOPerms) !codecs.UnwrapResult { - const path_tree = try source_text.fromUtf8(arena, path); - const digest_tree = try codecs.ofString(arena, hash); - const rest = if (tag == 52) try arena.alloc(.{ .fork = .{ .left = digest_tree, .right = value } }) else digest_tree; - const args = try arena.alloc(.{ .fork = .{ .left = path_tree, .right = rest } }); - return testAction(arena, tag, args, perms); -} - -test "indexed IO authenticates, enforces permissions before demand, and never replaces a different winner" { - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); - const directory = try tmp.dir.realPathFileAlloc(std.testing.io, ".", std.testing.allocator); - defer std.testing.allocator.free(directory); - const path = try std.fmt.allocPrint(std.testing.allocator, "{s}/é.arboricx", .{directory}); - defer std.testing.allocator.free(path); - var arena = Arena.init(std.testing.allocator); - defer arena.deinit(); - const leaf = try arena.alloc(.leaf); - // Test helpers turn on collection; retain the tree across independent IO calls. - try arena.roots.append(arena.allocator, leaf); - const bytes = try indexed.encode(&arena, leaf); - defer arena.allocator.free(bytes); - const hash = indexed.digest(bytes); - const perms = IOPerms{ .allow_read_all = true, .allow_write_all = true }; - const hashed = try testAction(&arena, 51, leaf, .{}); - try std.testing.expect(hashed.ok); - const actual = (try codecs.toString(&arena, hashed.value)).?; - defer arena.allocator.free(actual); - try std.testing.expectEqualStrings(&hash, actual); - for ([_]bool{ true, false }) |fresh| { - const result = try testIndexedAction(&arena, 52, path, &hash, leaf, perms); - try std.testing.expect(result.ok); - try std.testing.expectEqual(fresh, (try codecs.toBool(&arena, result.value)).?); - } - const read = try testIndexedAction(&arena, 50, path, &hash, leaf, .{ .allow_read_all = true }); - try std.testing.expect(read.ok and arena.get(read.value).* == .leaf); - // A denied write must not try to normalize a divergent supplied tree. - const poison = try arena.alloc(.leaf); - arena.get(poison).* = .{ .app = .{ .func = poison, .arg = leaf } }; - try arena.roots.append(arena.allocator, poison); - for ([_]IOPerms{ .{}, .{ .allow_read_all = true }, .{ .allow_write_all = true } }) |denied| { - try expectErrorText(&arena, try testIndexedAction(&arena, 52, path, &hash, poison, denied), "permission denied"); - } - try expectErrorText(&arena, try testIndexedAction(&arena, 50, path, &hash, leaf, .{}), "permission denied"); - try expectErrorText(&arena, try testIndexedAction(&arena, 50, path, "ABC", leaf, perms), "invalid CAS digest"); - const wrong = "0" ** 64; - try expectErrorText(&arena, try testIndexedAction(&arena, 50, path, wrong, leaf, perms), "CAS digest mismatch"); - try expectErrorText(&arena, try testIndexedAction(&arena, 52, path, wrong, leaf, perms), "CAS digest mismatch"); - try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "é.arboricx", .data = "corrupt" }); - try expectErrorText(&arena, try testIndexedAction(&arena, 52, path, &hash, leaf, perms), "different bytes"); - const still_corrupt = try readFsForTest(&arena, path); - defer arena.allocator.free(still_corrupt); - try std.testing.expectEqualStrings("corrupt", still_corrupt); - const corrupt_hash = indexed.digest("corrupt"); - try expectErrorText(&arena, try testIndexedAction(&arena, 50, path, &corrupt_hash, leaf, perms), "invalid indexed tree"); -} - -fn readFsForTest(arena: *Arena, path: []const u8) ![]u8 { - var loop: c.uv_loop_t = undefined; - try std.testing.expectEqual(@as(c_int, 0), c.uv_loop_init(&loop)); - defer _ = c.uv_loop_close(&loop); - const z = try arena.allocator.dupeZ(u8, path); - defer arena.allocator.free(z); - return switch (try readFs(&loop, arena.allocator, z)) { - .bytes => |b| b, - .failure => error.TestUnexpectedResult, - }; -} - -fn indexedIOAllocationProbe(allocator: std.mem.Allocator, path: []const u8) !void { - var arena = Arena.init(allocator); - defer arena.deinit(); - const leaf = try arena.alloc(.leaf); - try arena.roots.append(allocator, leaf); - const hash = "6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1"; - const perms = IOPerms{ .allow_read_all = true, .allow_write_all = true }; - const written = try testIndexedAction(&arena, 52, path, hash, leaf, perms); - try std.testing.expect(written.ok); - const read = try testIndexedAction(&arena, 50, path, hash, leaf, perms); - try std.testing.expect(read.ok and arena.get(read.value).* == .leaf); -} - -test "indexed publication and reads unwind every allocator failure" { - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); - const directory = try tmp.dir.realPathFileAlloc(std.testing.io, ".", std.testing.allocator); - defer std.testing.allocator.free(directory); - const path = try std.fmt.allocPrint(std.testing.allocator, "{s}/object", .{directory}); - defer std.testing.allocator.free(path); - try std.testing.checkAllAllocationFailures(std.testing.allocator, indexedIOAllocationProbe, .{path}); -} diff --git a/ext/zig/src/io_v2_core.zig b/ext/zig/src/io_v2_core.zig @@ -0,0 +1,445 @@ +//! Private structured core with a synchronous host interface. The default host +//! rejects all named requests. Optional test hosts provide real resources without +//! enabling any public v2 entrypoint. Host calls must settle before returning. +const std = @import("std"); +const Arena = @import("arena.zig").Arena; +const P = @import("io_v2_protocol.zig"); +const R = @import("io_v2_resources.zig"); +const T = @import("io_v2_tasks.zig"); +const C = @import("codecs.zig"); +pub const Outcome = T.Scheduler(u8, u32).Outcome; +const Body = struct { + action: u32, + frames: std.ArrayList(u32) = .empty, + mode: enum { runnable, lexical, joining, done } = .runnable, +}; +fn EngineType(comptime Host: type) type { + const S = T.Scheduler(Host.Backend, u32); + return struct { + const Engine = @This(); + host: *Host, + cleanup_failure: u32, + arena: *Arena, + scheduler: *S, + bodies: std.AutoHashMapUnmanaged(u64, Body) = .empty, + leaf: u32, + fuel: u64, + cursor: u64 = std.math.maxInt(u64), + fn deinit(self: *Engine) void { + var it = self.bodies.valueIterator(); + while (it.next()) |b| b.frames.deinit(self.arena.allocator); + self.bodies.deinit(self.arena.allocator); + } + pub fn reader(self: *Engine) P.Reader { + return .{ .arena = self.arena, .fuel = self.fuel }; + } + fn pair(self: *Engine, a: u32, b: u32) !u32 { + return self.arena.alloc(.{ .fork = .{ .left = a, .right = b } }); + } + pub fn text(self: *Engine, value: []const u8) !u32 { + return C.ofString(self.arena, value); + } + pub fn list(self: *Engine, values: []const u32) !u32 { + return C.ofList(self.arena, values); + } + pub fn problem(self: *Engine, category: []const u8, op: []const u8, detail: []const u8) !u32 { + return self.list(&.{ try self.text(category), try self.text(op), try self.text(detail), try self.text("none") }); + } + pub fn result(self: *Engine, success: bool, value: u32) !u32 { + return self.pair(if (success) try self.arena.alloc(.{ .stem = .{ .child = self.leaf } }) else self.leaf, try self.pair(value, self.leaf)); + } + pub fn marker(self: *Engine, name: []const u8, key: u64) !u32 { + return self.list(&.{ try self.text(name), try C.ofNumber(self.arena, self.scheduler.registry.identity), try C.ofNumber(self.arena, key) }); + } + fn literal(self: *Engine, id: u32, expected: []const u8) !bool { + var rest = id; + for (expected) |c| { + const cell = try self.reader().pair(rest) orelse return false; + if ((try self.reader().nat(cell.first, 7)) != @as(?u64, c)) return false; + rest = cell.second; + } + return try self.reader().node(rest) == .leaf; + } + pub fn token(self: *Engine, id: u32, name: []const u8) !?R.Token { + const tag = try self.reader().pair(id) orelse return null; + const run_field = try self.reader().pair(tag.second) orelse return null; + const key = try self.reader().pair(run_field.second) orelse return null; + if (try self.reader().node(key.second) != .leaf or !try self.literal(tag.first, name)) return null; + return .{ .run = try self.reader().nat(run_field.first, 64) orelse return null, .key = try self.reader().nat(key.first, 64) orelse return null }; + } + fn outcomeTree(self: *Engine, outcome: S.Outcome) !u32 { + if (outcome.kind == .completed) return self.list(&.{ try self.text("completed"), outcome.value }); + var diagnostics: [17]u32 = undefined; + @memcpy(diagnostics[0..outcome.cleanup.len], outcome.cleanup.items[0..outcome.cleanup.len]); + var len = outcome.cleanup.len; + if (outcome.cleanup.omitted != 0) { + var buffer: [20]u8 = undefined; + diagnostics[len] = try self.problem("cleanup-omitted", "close", try std.fmt.bufPrint(&buffer, "{d}", .{outcome.cleanup.omitted})); + len += 1; + } + return self.list(&.{ try self.text(if (outcome.kind == .failed) "failed" else "cancelled"), outcome.value, try self.list(diagnostics[0..len]) }); + } + fn done(self: *Engine, scope: u64, outcome: S.Outcome) !void { + try self.scheduler.bodyDone(scope, outcome); + const body = self.bodies.getPtr(scope).?; + body.mode = .done; + body.action = self.leaf; + body.frames.clearRetainingCapacity(); + } + fn resumeValue(self: *Engine, scope: u64, value: u32) !void { + const body = self.bodies.getPtr(scope).?; + if (body.frames.pop()) |continuation| { + body.action = try self.arena.allocApp(continuation, value); + body.mode = .runnable; + } else try self.done(scope, .{ .kind = .completed, .value = value }); + } + fn fault(self: *Engine, scope: u64, detail: []const u8) !void { + try self.done(scope, .{ .kind = .failed, .value = try self.problem("protocol-error", "core/decode", detail) }); + } + fn operationError(self: *Engine, scope: u64, op: []const u8, err: anyerror) !void { + const category = switch (err) { + error.StaleTask => "stale-task", + error.JoinCycle => "join-cycle", + error.OutOfScope => "out-of-scope", + error.StaleHandle => "stale-handle", + error.NotOwner => "not-owner", + error.BorrowedResource => "borrowed-resource", + error.InternalResource => "internal-resource", + error.ClosedHandle => "closed-handle", + error.Busy => "busy", + error.DuplicateHandle => "duplicate-handle", + error.ClosedScope => "closed-scope", + error.InvalidArgument => "invalid-argument", + else => return err, + }; + try self.resumeValue(scope, try self.result(false, try self.problem(category, op, ""))); + } + fn step(self: *Engine, scope: u64) !void { + const body = self.bodies.get(scope).?; + const group = self.scheduler.groups.get(scope).?; + if (body.mode == .done or body.mode == .lexical) return; + if (group.reason) |reason| return self.done(scope, reason); + if (body.mode == .joining) { + if (try self.scheduler.pollJoin(scope)) |outcome| try self.resumeValue(scope, try self.result(true, try self.outcomeTree(outcome))); + return; + } + const action = P.decode(self.arena, body.action, self.fuel) catch |err| return switch (err) { + error.InvalidComputationShape => self.fault(scope, "invalid computation shape"), + error.UnknownComputationConstructor => self.fault(scope, "unknown computation constructor"), + error.InvalidFacilityHeader => self.fault(scope, "invalid facility header"), + else => err, + }; + switch (action) { + .request => |request| { + return self.resumeValue(scope, try self.host.request(self, scope, request)); + }, + .core => |core| switch (core.code) { + .pure => try self.resumeValue(scope, core.payload), + .fail => try self.done(scope, .{ .kind = .failed, .value = core.payload }), + .cancelled => try self.done(scope, .{ .kind = .cancelled, .value = core.payload }), + .bind => { + const parts = try self.reader().pair(core.payload) orelse return self.fault(scope, "invalid v2 computation"); + try self.bodies.getPtr(scope).?.frames.append(self.arena.allocator, parts.second); + self.bodies.getPtr(scope).?.action = parts.first; + }, + .current_scope => try self.resumeValue(scope, try self.marker("io.v2.scope", scope)), + .scope => { + try self.bodies.ensureUnusedCapacity(self.arena.allocator, 1); + const child = try self.scheduler.nested(scope); + const child_action = try self.arena.allocApp(core.payload, try self.marker("io.v2.scope", child)); + self.bodies.putAssumeCapacityNoClobber(child, .{ .action = child_action }); + self.bodies.getPtr(scope).?.action = self.leaf; + self.bodies.getPtr(scope).?.mode = .lexical; + }, + .spawn => { + const supervised = try self.reader().pair(core.payload) orelse return self.operationError(scope, "core/spawn", error.InvalidArgument); + const keep = try self.reader().pair(supervised.second) orelse return self.operationError(scope, "core/spawn", error.InvalidArgument); + const handles = try self.reader().pair(keep.second) orelse return self.operationError(scope, "core/spawn", error.InvalidArgument); + const contained = try self.reader().nat(supervised.first, 1) orelse return self.operationError(scope, "core/spawn", error.InvalidArgument); + const retained = try self.reader().nat(keep.first, 1) orelse return self.operationError(scope, "core/spawn", error.InvalidArgument); + var tokens: [R.transfer_limit]R.Token = undefined; + var count: usize = 0; + var rest = handles.first; + while (try self.reader().node(rest) != .leaf) { + if (count == tokens.len) return self.operationError(scope, "core/spawn", error.InvalidArgument); + const cell = try self.reader().pair(rest) orelse return self.operationError(scope, "core/spawn", error.InvalidArgument); + tokens[count] = try self.token(cell.first, "io.v2.resource") orelse return self.operationError(scope, "core/spawn", error.InvalidArgument); + count += 1; + rest = cell.second; + } + try self.bodies.ensureUnusedCapacity(self.arena.allocator, 1); + const child = self.scheduler.spawn(scope, contained == 1, retained == 1, tokens[0..count]) catch |err| return self.operationError(scope, "core/spawn", err); + var fresh: [R.transfer_limit]u32 = undefined; + for (child.tokens[0..count], 0..) |t, i| fresh[i] = try self.marker("io.v2.resource", t.key); + const child_action = try self.arena.allocApp(handles.second, try self.list(fresh[0..count])); + self.bodies.putAssumeCapacityNoClobber(child.scope, .{ .action = child_action }); + try self.resumeValue(scope, try self.result(true, try self.list(&.{ try self.marker("io.v2.task", child.scope), if (retained == 1) try self.marker("io.v2.join", child.scope) else self.leaf }))); + }, + .join, .discard => { + const op: []const u8 = if (core.code == .join) "core/join" else "core/discard"; + const ref = try self.token(core.payload, "io.v2.join") orelse return self.operationError(scope, op, error.InvalidArgument); + if (core.code == .discard) { + self.scheduler.discard(scope, ref) catch |err| return self.operationError(scope, op, err); + try self.resumeValue(scope, try self.result(true, self.leaf)); + } else { + self.scheduler.claimJoin(scope, ref) catch |err| return self.operationError(scope, op, err); + self.bodies.getPtr(scope).?.action = self.leaf; + self.bodies.getPtr(scope).?.mode = .joining; + } + }, + .cancel => { + const parts = try self.reader().pair(core.payload) orelse return self.operationError(scope, "core/cancel", error.InvalidArgument); + const ref = try self.token(parts.first, "io.v2.task") orelse return self.operationError(scope, "core/cancel", error.InvalidArgument); + self.scheduler.cancel(scope, ref, parts.second) catch |err| return self.operationError(scope, "core/cancel", err); + try self.resumeValue(scope, try self.result(true, self.leaf)); + }, + }, + } + } + fn settleOne(self: *Engine) !?S.Outcome { + var it = self.scheduler.groups.iterator(); + while (it.next()) |entry| { + if (entry.value_ptr.phase != .joining) continue; + const scope = entry.key_ptr.*; + if (!try self.scheduler.beginRelease(scope)) continue; + const group = self.scheduler.groups.get(scope).?; + const cleanup = self.releaseScope(scope); + const outcome = try self.scheduler.settle(scope, cleanup); + var removed = self.bodies.fetchRemove(scope).?.value; + removed.frames.deinit(self.arena.allocator); + if (scope == 0) return outcome; + if (group.kind == .lexical) { + const parent = group.parent.?; + self.bodies.getPtr(parent).?.mode = .runnable; + if (outcome.kind == .completed) try self.resumeValue(parent, outcome.value) else try self.done(parent, outcome); + } + break; // iterator invalidated by settlement + } + return null; + } + // Synchronous hosts have no outstanding OS work. Abort settles every logical + // scope/task without allocating, including a child admitted just before its + // body/continuation allocation failed. Physical release is allocation-free. + // Asynchronous hosts require a separate cancellation/draining pump. + fn abort(self: *Engine) void { + self.scheduler.stop(0, .{ .kind = .cancelled, .value = self.leaf }); + while (self.scheduler.groups.count() != 0) { + var progressed = false; + var bodies = self.scheduler.groups.iterator(); + while (bodies.next()) |entry| { + if (entry.value_ptr.phase != .open or entry.value_ptr.lexical != null) continue; + self.scheduler.bodyDone(entry.key_ptr.*, entry.value_ptr.reason.?) catch unreachable; + if (self.bodies.getPtr(entry.key_ptr.*)) |body| { + body.mode = .done; + body.action = self.leaf; + body.frames.clearRetainingCapacity(); + } + progressed = true; + } + var groups = self.scheduler.groups.iterator(); + while (groups.next()) |entry| { + const scope = entry.key_ptr.*; + if (entry.value_ptr.phase != .joining) continue; + if (!(self.scheduler.beginRelease(scope) catch unreachable)) continue; + const cleanup = self.releaseScope(scope); + _ = self.scheduler.settle(scope, cleanup) catch unreachable; + if (self.bodies.fetchRemove(scope)) |removed| { + var body = removed.value; + body.frames.deinit(self.arena.allocator); + } + progressed = true; + break; + } + std.debug.assert(progressed); // no host callbacks exist in this harness + } + std.debug.assert(self.scheduler.tasks.count() == 0); + std.debug.assert(self.scheduler.registry.scopes.count() == 0); + std.debug.assert(self.scheduler.registry.resources.count() == 0); + } + fn releaseScope(self: *Engine, scope: u64) S.Diagnostics { + var cleanup: S.Diagnostics = .{}; + while (self.scheduler.registry.newestOwned(scope)) |ref| { + const resource = (self.scheduler.registry.claimClose(scope, ref, true) catch unreachable).?; + if (!resource.flags.borrowed) self.host.close(self.scheduler.registry, resource.backend) catch { + cleanup.append(S.Diagnostics.one(self.cleanup_failure)); + }; + self.scheduler.registry.finishClose(ref) catch unreachable; + } + return cleanup; + } + fn appendRoot(arena: *Arena, id: u32) !void { + try arena.roots.append(arena.allocator, id); + } + fn roots(self: *Engine, base: usize) !void { + self.arena.roots.shrinkRetainingCapacity(base); + try appendRoot(self.arena, self.leaf); + try appendRoot(self.arena, self.cleanup_failure); + try self.scheduler.visitPayloads(self.arena, appendRoot); + var it = self.bodies.valueIterator(); + while (it.next()) |body| { + try appendRoot(self.arena, body.action); + try self.arena.roots.appendSlice(self.arena.allocator, body.frames.items); + } + } + fn nextBody(self: *Engine) ?u64 { + var first: ?u64 = null; + var after: ?u64 = null; + var it = self.bodies.iterator(); + while (it.next()) |entry| { + if (entry.value_ptr.mode == .done or entry.value_ptr.mode == .lexical) continue; + const key = entry.key_ptr.*; + if (first == null or key < first.?) first = key; + if (key > self.cursor and (after == null or key < after.?)) after = key; + } + self.cursor = after orelse first orelse return null; + return self.cursor; + } + }; +} + +// Caller retains input/output roots. Fatal reducer/allocator errors drain the +// synchronous host before propagating; host-side cleanup records must not allocate. +// Host is deliberately SYNCHRONOUS: request and close must return only after +// their work settles. No pending callbacks, detached work or roots outside the +// scheduler are supported by this interface. close must not allocate and must +// retire its physical resource even on error. Async hosts need a different pump. +pub const UnsupportedHost = struct { + pub const Backend = u8; + pub const standard = [3]u8{ 0, 0, 0 }; + pub fn request(_: *@This(), engine: anytype, _: u64, req: anytype) !u32 { + return unsupported(engine, req); + } + pub fn close(_: *@This(), _: anytype, _: u8) error{}!void { + unreachable; + } +}; +pub fn unsupported(engine: anytype, request: anytype) !u32 { + var operation: [129]u8 = undefined; + var detail: [40]u8 = undefined; + const op = try std.fmt.bufPrint(&operation, "{s}/{s}", .{ request.group.slice(), request.operation.slice() }); + const message = try std.fmt.bufPrint(&detail, "group revision {d}", .{request.revision}); + return engine.result(false, try engine.problem("unsupported", op, message)); +} +pub fn run(arena: *Arena, action: u32, identity: u64, fuel: u64) !Outcome { + var host: UnsupportedHost = .{}; + return runWithHost(UnsupportedHost, &host, arena, action, identity, fuel); +} +pub fn runWithHost(comptime Host: type, host: *Host, arena: *Arena, action: u32, identity: u64, fuel: u64) !T.Scheduler(Host.Backend, u32).Outcome { + const S = T.Scheduler(Host.Backend, u32); + const Engine = EngineType(Host); + const base = arena.roots.items.len; + defer arena.roots.shrinkRetainingCapacity(base); + var registry = try S.Registry.init(arena.allocator, identity, Host.standard); + defer registry.deinit(); + var scheduler = try S.init(arena.allocator, &registry, try C.ofString(arena, "owning group stopped"), try C.ofString(arena, "task join cycle")); + defer scheduler.deinit(); + const cleanup_failure = try C.ofList(arena, &.{ try C.ofString(arena, "cleanup-failed"), try C.ofString(arena, "resource/close"), try C.ofString(arena, "host close failed"), try C.ofString(arena, "unknown") }); + var engine: Engine = .{ .host = host, .cleanup_failure = cleanup_failure, .arena = arena, .scheduler = &scheduler, .leaf = try arena.alloc(.leaf), .fuel = fuel }; + defer engine.deinit(); + errdefer engine.abort(); + try engine.bodies.put(arena.allocator, 0, .{ .action = action }); + while (true) { + try engine.roots(base); + if (try engine.settleOne()) |outcome| return outcome; + // Settlement may construct a resumed continuation. Refresh before demand. + try engine.roots(base); + if (engine.nextBody()) |scope| try engine.step(scope); + } +} + +const testing = std.testing; +fn pair(a: *Arena, x: u32, y: u32) !u32 { + return a.alloc(.{ .fork = .{ .left = x, .right = y } }); +} +fn makeAction(a: *Arena, tag: u64, payload: u32) !u32 { + return pair(a, try C.ofNumber(a, tag), payload); +} +fn constant(a: *Arena, value: u32) !u32 { + return pair(a, try a.alloc(.leaf), value); +} +fn poison(a: *Arena) !u32 { + const p = try a.alloc(.leaf); + a.get(p).* = .{ .app = .{ .func = p, .arg = p } }; + return p; +} +test "real bind and scope continuations with demanded GC and opaque discarded arguments" { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + var program = try makeAction(&a, 0, try C.ofNumber(&a, 42)); + for (0..100) |_| { + program = try makeAction(&a, 1, try pair(&a, try makeAction(&a, 0, p), try constant(&a, program))); + program = try makeAction(&a, 3, try constant(&a, program)); + } + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + const outcome = try run(&a, program, 1, 1000); + try testing.expectEqual(.completed, outcome.kind); + // No reduction until the result has an externally retained root. + try a.roots.append(a.allocator, outcome.value); + try testing.expectEqual(@as(?u64, 42), try C.toNumber(&a, outcome.value)); + try testing.expect(a.collections > 0); + a.roots.clearRetainingCapacity(); + try a.collect(&.{}); + try testing.expectEqual(@as(usize, 0), a.last_live); +} +test "terminal diagnostics and cancelled reasons are not demanded" { + for ([_]u64{ 2, 9 }) |tag| { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + const outcome = try run(&a, try makeAction(&a, tag, p), 1, 0); + try testing.expectEqual(p, outcome.value); + try testing.expectEqual(if (tag == 2) @TypeOf(outcome.kind).failed else .cancelled, outcome.kind); + try testing.expect(a.get(p).* == .app); + } +} +test "unknown valid facility is an ordinary Result without demanding arguments" { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + const request = try makeAction(&a, 10, try pair(&a, try C.ofString(&a, "future"), try pair(&a, try C.ofNumber(&a, 9), try pair(&a, try C.ofString(&a, "call"), p)))); + const outcome = try run(&a, request, 1, 0); + try testing.expectEqual(.completed, outcome.kind); + const result = a.get(outcome.value).fork; + try testing.expect(a.get(result.left).* == .leaf); + const problem = a.get(result.right).fork.left; + const category = a.get(problem).fork.left; + const name = (try C.toString(&a, category)).?; + defer testing.allocator.free(name); + try testing.expectEqualStrings("unsupported", name); + try testing.expect(a.get(p).* == .app); +} +fn allocationProbe(allocator: std.mem.Allocator) !void { + var a = Arena.init(allocator); + defer a.deinit(); + const value = try C.ofNumber(&a, 3); + var program = try makeAction(&a, 0, value); + for (0..8) |_| program = try makeAction(&a, 3, try constant(&a, program)); + _ = try run(&a, program, 1, 100); + try testing.expectEqual(@as(usize, 0), a.roots.items.len); +} +test "private core allocator failures unwind all host memory and temporary roots" { + try testing.checkAllAllocationFailures(testing.allocator, allocationProbe, .{}); +} + +fn spawnAllocationProbe(allocator: std.mem.Allocator) !void { + var a = Arena.init(allocator); + defer a.deinit(); + const leaf = try a.alloc(.leaf); + const one = try C.ofNumber(&a, 1); + const child = try constant(&a, try makeAction(&a, 0, try C.ofNumber(&a, 3))); + const spawned = try makeAction(&a, 5, try pair(&a, one, try pair(&a, one, try pair(&a, leaf, child)))); + // First child in this independent run owns ID4. Constructing the reference + // independently also checks the exact source token representation. + const ticket = try C.ofList(&a, &.{ try C.ofString(&a, "io.v2.join"), one, try C.ofNumber(&a, 4) }); + const program = try makeAction(&a, 1, try pair(&a, spawned, try constant(&a, try makeAction(&a, 6, ticket)))); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + const outcome = try run(&a, program, 1, 100); + try testing.expectEqual(.completed, outcome.kind); + try testing.expectEqual(@as(usize, 0), a.roots.items.len); +} +test "spawn publication and join reply allocation failures settle every admitted child" { + try testing.checkAllAllocationFailures(testing.allocator, spawnAllocationProbe, .{}); +} diff --git a/ext/zig/src/io_v2_core_probe.zig b/ext/zig/src/io_v2_core_probe.zig @@ -0,0 +1,114 @@ +//! Test-only executable: compiled source against the private core, no native IO +//! facilities. This is not the tricu-zig CLI or a compiler execution backend. +const std = @import("std"); +const Arena = @import("arena.zig").Arena; +const P = @import("io_v2_protocol.zig"); +const C = @import("codecs.zig"); +const Core = @import("io_v2_core.zig"); +const bundle = @import("bundle.zig"); +const reduce = @import("reduce.zig"); +pub fn main(init: std.process.Init) !void { + return mainFor(Core.UnsupportedHost, init); +} +pub fn mainFor(comptime Host: type, init: std.process.Init) !void { + const args = try init.minimal.args.toSlice(init.arena.allocator()); + if (args.len < 3) return error.ExpectedBundleAndExport; + const bytes = try std.Io.Dir.cwd().readFileAlloc(init.io, args[1], init.gpa, .limited(10 * 1024 * 1024)); + defer init.gpa.free(bytes); + var arena = Arena.init(init.gpa); + defer arena.deinit(); + var root = try bundle.loadBundleExport(&arena, bytes, args[2]); + for (args[3..]) |argument| { + const value = if (std.mem.startsWith(u8, argument, "tree-file:")) blk: { + const raw = try std.Io.Dir.cwd().readFileAlloc(init.io, argument[10..], init.gpa, .limited(10 * 1024 * 1024)); + defer init.gpa.free(raw); + break :blk try parseRaw(&arena, raw); + } else if (std.mem.startsWith(u8, argument, "text:")) try @import("source_text.zig").fromUtf8(&arena, argument[5..]) else try C.ofNumber(&arena, try std.fmt.parseUnsigned(u64, argument, 10)); + root = try arena.allocApp(root, value); + } + try arena.roots.append(init.gpa, root); + arena.gc_enabled = true; + try arena.setGcPolicy(128, 1); + const r: P.Reader = .{ .arena = &arena, .fuel = std.math.maxInt(u64) }; + const envelope = try r.pair(root) orelse return error.InvalidEnvelope; + var name = envelope.first; + for ("tricuIO") |ch| { + const cell = try r.pair(name) orelse return error.InvalidEnvelope; + if (try r.nat(cell.first, 7) != @as(?u64, ch)) return error.InvalidEnvelope; + name = cell.second; + } + if (try r.node(name) != .leaf) return error.InvalidEnvelope; + const body = try r.pair(envelope.second) orelse return error.InvalidEnvelope; + if (try r.nat(body.first, 16) != @as(?u64, 2)) return error.UnsupportedVersion; + // Drop the input envelope; private run will root the current computation. + arena.roots.clearRetainingCapacity(); + var host: Host = .{}; + const outcome = try Core.runWithHost(Host, &host, &arena, body.second, 1, std.math.maxInt(u64)); + if (outcome.kind != .completed) { + var error_output = std.Io.File.stderr().writer(init.io, &.{}); + try error_output.interface.writeAll(if (outcome.kind == .failed) "failed (payload not forced)\n" else "cancelled (reason not forced)\n"); + try error_output.flush(); + std.process.exit(1); + } + try arena.roots.append(init.gpa, outcome.value); + var stack: std.ArrayList(u32) = .empty; + defer stack.deinit(init.gpa); + try stack.append(init.gpa, outcome.value); + var buffer: [4096]u8 = undefined; + var output = std.Io.File.stdout().writer(init.io, &buffer); + var count: usize = 0; + while (stack.pop()) |id| { + count += 1; + if (count > 1048576) return error.ResultLimit; + const node = arena.get(try reduce.reduce(id, &arena, 1_000_000)).*; + switch (node) { + .leaf => try output.interface.writeByte('0'), + .stem => |s| { + try output.interface.writeByte('1'); + try stack.append(init.gpa, s.child); + }, + .fork => |f| { + try output.interface.writeByte('2'); + try stack.appendSlice(init.gpa, &.{ f.right, f.left }); + }, + .app => unreachable, + } + } + try output.interface.writeByte('\n'); + try output.flush(); +} + +// Exact raw constructor tree (0/1/2 bytes), parsed without recursion or ignored +// trailing roots. Private source controls can pass NUL and malformed scalar data. +fn parseRaw(arena: *Arena, bytes: []const u8) !u32 { + var stack: std.ArrayList(u32) = .empty; + defer stack.deinit(arena.allocator); + var at = bytes.len; + while (at != 0) { + at -= 1; + const node: @import("tree.zig").Node = switch (bytes[at]) { + 0 => .leaf, + 1 => .{ .stem = .{ .child = stack.pop() orelse return error.InvalidRawTree } }, + 2 => .{ .fork = .{ .left = stack.pop() orelse return error.InvalidRawTree, .right = stack.pop() orelse return error.InvalidRawTree } }, + else => return error.InvalidRawTree, + }; + try stack.append(arena.allocator, try arena.alloc(node)); + } + if (stack.items.len != 1) return error.InvalidRawTree; + return stack.items[0]; +} + +test "private raw argument parser is exact, iterative and preserves constructor order" { + const t = std.testing; + var a = Arena.init(t.allocator); + defer a.deinit(); + const root = try parseRaw(&a, &.{ 2, 1, 0, 0 }); + const node = a.get(root).fork; + try t.expect(a.get(node.left).* == .stem and a.get(node.right).* == .leaf); + for ([_][]const u8{ &.{}, &.{ 0, 0 }, &.{1}, &.{ 2, 0 }, &.{3}, "0" }) |raw| try t.expectError(error.InvalidRawTree, parseRaw(&a, raw)); + const deep = try t.allocator.alloc(u8, 100001); + defer t.allocator.free(deep); + @memset(deep, 1); + deep[deep.len - 1] = 0; + _ = try parseRaw(&a, deep); +} diff --git a/ext/zig/src/io_v2_digest.zig b/ext/zig/src/io_v2_digest.zig @@ -0,0 +1,41 @@ +//! Scoped, incremental SHA-256 state. No chunk/history retention and no writer or +//! publication policy. The host registry supplies ownership and exclusive leases. +const std = @import("std"); +pub const Digest = struct { + state: std.crypto.hash.sha2.Sha256 = std.crypto.hash.sha2.Sha256.init(.{}), + finished: bool = false, + pub fn update(self: *Digest, bytes: []const u8) error{ InvalidBound, InvalidState }!void { + if (bytes.len == 0 or bytes.len > 4096) return error.InvalidBound; + if (self.finished) return error.InvalidState; + self.state.update(bytes); + } + pub fn finish(self: *Digest) error{InvalidState}![64]u8 { + if (self.finished) return error.InvalidState; + var bytes: [32]u8 = undefined; + self.state.final(&bytes); + self.finished = true; + var hex: [64]u8 = undefined; + for (bytes, 0..) |byte, i| { + hex[2 * i] = "0123456789abcdef"[byte >> 4]; + hex[2 * i + 1] = "0123456789abcdef"[byte & 15]; + } + return hex; + } +}; +test "independent SHA-256 vectors, partitioning and terminal state" { + const t = std.testing; + var empty: Digest = .{}; + try t.expectEqualStrings("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", &(try empty.finish())); + try t.expectError(error.InvalidState, empty.finish()); + try t.expectError(error.InvalidState, empty.update("x")); + var abc: Digest = .{}; + try t.expectError(error.InvalidBound, abc.update("")); + try t.expectError(error.InvalidBound, abc.update(&([_]u8{'x'} ** 4097))); + try abc.update("a"); + try abc.update("bc"); + try t.expectEqualStrings("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", &(try abc.finish())); + var million: Digest = .{}; + const block = [_]u8{'a'} ** 1000; + for (0..1000) |_| try million.update(&block); + try t.expectEqualStrings("cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", &(try million.finish())); +} diff --git a/ext/zig/src/io_v2_file_host.zig b/ext/zig/src/io_v2_file_host.zig @@ -0,0 +1,864 @@ +//! Synchronous native compiler/file profile, shared by the CLI and private +//! integration probes. Explicit capabilities; no standard streams or broad OS parity. +const std = @import("std"); +const Protocol = @import("io_v2_protocol.zig"); +const Core = @import("io_v2_core.zig"); +const F = @import("io_v2_files.zig"); +const Paths = @import("io_v2_paths.zig"); +const UTF8 = @import("io_v2_utf8.zig"); +const Stage = @import("io_v2_stage.zig"); +const Digest = @import("io_v2_digest.zig").Digest; +const Snapshot = @import("io_v2_snapshot.zig"); +const Indexed = @import("indexed.zig"); +const C = @import("codecs.zig"); +const Text = @import("source_text.zig"); +pub const Host = struct { + // Fixed-size host diagnostics remain inspectable even when a reducer/allocator + // failure prevents producing a tree Result. No allocation or dangling node IDs. + algorithm_detail: [512]u8 = undefined, + algorithm_detail_len: usize = 0, + utf8_detail: [96]u8 = undefined, + utf8_detail_len: usize = 0, + snapshot_cleanup_failed: bool = false, + stage_operation: bool = false, + stage_status: Stage.Status = .{}, + published: usize = 0, + opened: usize = 0, + closed: usize = 0, + close_errors: [16]F.Status = undefined, + close_error_count: usize = 0, + close_errors_omitted: usize = 0, + fn recordCloseError(self: *Host, status: F.Status) void { + if (self.close_error_count < self.close_errors.len) { + self.close_errors[self.close_error_count] = status; + self.close_error_count += 1; + } else self.close_errors_omitted += 1; + } + pub const Backend = union(enum) { placeholder, file: *F.File, digest: *Digest, stage: *Stage.Stage }; + pub const standard = [3]Backend{ .placeholder, .placeholder, .placeholder }; + fn fileBackend(file: *F.File) Backend { + return .{ .file = file }; + } + pub fn close(self: *Host, registry: anytype, backend: Backend) !void { + var status: F.Status = .{}; + return self.closeWithStatus(registry, backend, &status); + } + fn closeWithStatus(self: *Host, registry: anytype, backend: Backend, status: *F.Status) !void { + defer self.closed += 1; + switch (backend) { + .placeholder => unreachable, + .digest => |digest| registry.allocator.destroy(digest), + .file => |file| { + defer registry.allocator.destroy(file); + file.close(status) catch |err| { + self.recordCloseError(status.*); + return err; + }; + }, + .stage => |stage| { + defer registry.allocator.destroy(stage); + defer stage.deinit(); + var released: Stage.Status = .{}; + stage.release(&released) catch |err| { + status.* = .{ .code = released.code, .cleanup_code = released.secondary }; + self.recordCloseError(status.*); + return err; + }; + }, + } + } + const Op = enum { open_reader, open_writer, open_appender, open_update, read, write, close, seek, position, size, resize, sync, stat, lstat, create_directory, remove_directory, remove_file, rename_path, sync_directory, open_digest, update_digest, finish_digest, open_stage, seal_stage, publish_stage, encode_utf8, decode_utf8, hash_indexed, write_indexed, verify_indexed, read_indexed, capabilities, inspect }; + const entries = .{ + .{ "runtime", "capabilities", Op.capabilities }, .{ "runtime", "inspect", Op.inspect }, + .{ "indexed-tree", "read", Op.read_indexed }, .{ "indexed-tree", "hash", Op.hash_indexed }, + .{ "indexed-tree", "write", Op.write_indexed }, .{ "indexed-tree", "verify", Op.verify_indexed }, + .{ "utf8", "encode", Op.encode_utf8 }, .{ "utf8", "decode", Op.decode_utf8 }, + .{ "stage", "open", Op.open_stage }, .{ "stage", "seal", Op.seal_stage }, + .{ "stage", "publish", Op.publish_stage }, .{ "digest", "open", Op.open_digest }, + .{ "digest", "update", Op.update_digest }, .{ "digest", "finish", Op.finish_digest }, + .{ "path", "stat", Op.stat }, .{ "path", "lstat", Op.lstat }, + .{ "path", "remove-file", Op.remove_file }, .{ "path", "rename", Op.rename_path }, + .{ "directory", "create", Op.create_directory }, .{ "directory", "remove", Op.remove_directory }, + .{ "directory", "sync", Op.sync_directory }, .{ "file", "open-reader", Op.open_reader }, + .{ "file", "open-writer", Op.open_writer }, .{ "file", "open-appender", Op.open_appender }, + .{ "file", "open-update", Op.open_update }, .{ "stream", "read", Op.read }, + .{ "stream", "write", Op.write }, .{ "resource", "close", Op.close }, + .{ "file", "seek", Op.seek }, .{ "file", "position", Op.position }, + .{ "file", "size", Op.size }, .{ "file", "resize", Op.resize }, + .{ "file", "sync", Op.sync }, + }; + fn resolve(group: []const u8, operation: []const u8) ?Op { + inline for (entries) |entry| if (std.mem.eql(u8, group, entry[0]) and std.mem.eql(u8, operation, entry[1])) return entry[2]; + return null; + } + pub fn request(self: *Host, engine: anytype, scope: u64, req: anytype) !u32 { + if (req.revision != 1) return Core.unsupported(engine, req); + const op = resolve(req.group.slice(), req.operation.slice()) orelse return Core.unsupported(engine, req); + var name_buffer: [129]u8 = undefined; + const name = try std.fmt.bufPrint(&name_buffer, "{s}/{s}", .{ req.group.slice(), req.operation.slice() }); + var status: F.Status = .{}; + self.stage_operation = false; + self.snapshot_cleanup_failed = false; + self.stage_status = .{}; + const value = self.dispatch(engine, scope, op, req.arguments, &status) catch |err| { + if (err == error.InvalidAlgorithm) return engine.result(false, try engine.list(&.{ + try engine.text("unsupported"), try engine.text(name), + try Text.fromUtf8(engine.arena, self.algorithm_detail[0..self.algorithm_detail_len]), try engine.text("none"), + })); + if (self.stage_operation) status.code = self.stage_status.code; + const category = switch (err) { + error.InvalidArgument, error.EmptySnapshotDirectory => "invalid-argument", + error.InvalidData, error.SnapshotTruncated, error.SnapshotInvalid => "invalid-data", + error.SnapshotBound => "invalid-bound", + error.SnapshotDigestMismatch => "integrity-failed", + error.SnapshotCleanup => "cleanup-failed", + error.InvalidState => "invalid-state", + error.InvalidDestination => "invalid-destination", + error.InvalidBound, error.EncodedLimit => "invalid-bound", + error.DigestMismatch, error.FrameMismatch => "integrity-failed", + error.UnsupportedFileType, error.UnsupportedMode => "unsupported", + error.WrongAccess => "wrong-kind", + error.NotDirectory => "not-directory", + error.Closed, error.ClosedHandle => "closed-handle", + error.StaleHandle => "stale-handle", + error.OutOfScope => "out-of-scope", + error.Busy => "busy", + error.ClosedScope => "closed-scope", + error.BorrowedResource => "borrowed-resource", + error.InternalResource => "internal-resource", + error.HostFailure => F.errorCategory(status.code), + else => return err, + }; + const detail = if (err == error.EmptySnapshotDirectory) "empty snapshot directory" else if (err == error.SnapshotBound) "invalid snapshot size limit" else if (err == error.SnapshotTruncated) "incomplete indexed frame" else if (err == error.SnapshotInvalid) "invalid indexed frame" else if (err == error.SnapshotDigestMismatch) "CAS digest mismatch" else if (err == error.SnapshotCleanup) "snapshot release failed" else if (err == error.EncodedLimit) "encoded tree exceeds limit" else if (err == error.DigestMismatch) "CAS digest mismatch" else if (err == error.FrameMismatch) "indexed frame differs from canonical tree" else if (err == error.InvalidData) self.utf8_detail[0..self.utf8_detail_len] else if (err == error.HostFailure) F.errorText(status.code) else if (err == error.UnsupportedFileType) "path-based files support regular files only" else if (err == error.UnsupportedMode) "operation unavailable in this file mode" else if (err == error.NotDirectory) "path is not a directory" else if (err == error.InvalidDestination) "destination must name a file outside the private staging namespace" else if (err == error.InvalidState) (if (self.stage_operation) "stage is not in the required phase" else "digest already finished") else ""; + // Mutating opens can create/truncate before a later failure. Never + // claim absence of effects; this is not transactional rollback. + const progress = if (err == error.SnapshotTruncated or err == error.SnapshotInvalid or err == error.SnapshotDigestMismatch) "unknown" else if (err == error.FrameMismatch) "unknown" else if (self.stage_operation) switch (self.stage_status.progress) { + .none => "none", + .not_published => "not-published", + .unknown => "unknown", + .published => "published", + } else if (err == error.HostFailure and op != .open_reader and op != .size and op != .position and op != .stat and op != .lstat) "unknown" else "none"; + var detail_buffer: [1024]u8 = undefined; + const reported = if (self.snapshot_cleanup_failed) try std.fmt.bufPrint(&detail_buffer, "{s}; cleanup: snapshot release failed", .{detail}) else detail; + return engine.result(false, try engine.list(&.{ try engine.text(category), try engine.text(name), try engine.text(reported), try engine.text(progress) })); + }; + return engine.result(true, value); + } + fn decodeText(engine: anytype, input: u32) ![]u8 { + var output: std.ArrayList(u8) = .empty; + errdefer output.deinit(engine.arena.allocator); + var rest = input; + const reader = engine.reader(); + while (try reader.node(rest) != .leaf) { + const cell = try reader.pair(rest) orelse return error.InvalidArgument; + const scalar = try reader.nat(cell.first, 21) orelse return error.InvalidArgument; + if (scalar > 0x10ffff or (scalar >= 0xd800 and scalar <= 0xdfff)) return error.InvalidArgument; + var encoded: [4]u8 = undefined; + const count = try std.unicode.utf8Encode(@intCast(scalar), &encoded); + try output.appendSlice(engine.arena.allocator, encoded[0..count]); + rest = cell.second; + } + return output.toOwnedSlice(engine.arena.allocator); + } + fn decodeChunk(engine: anytype, input: u32, bytes: *[4096]u8) !usize { + var count: usize = 0; + var rest = input; + const reader = engine.reader(); + while (try reader.node(rest) != .leaf) { + // Match the protocol decoder (invalid argument), not the lower-level + // byte-IO bound check. Observe excess spine, never excess head/tail. + if (count == bytes.len) return error.InvalidArgument; + const cell = try reader.pair(rest) orelse return error.InvalidArgument; + bytes[count] = @intCast(try reader.nat(cell.first, 8) orelse return error.InvalidArgument); + count += 1; + rest = cell.second; + } + return count; + } + fn readIndexed(self: *Host, engine: anytype, scope: u64, arguments: u32, status: *F.Status) !u32 { + const r = engine.reader(); + var fields: [5]u32 = undefined; + var rest = arguments; + for (&fields) |*field| { + const cell = try r.pair(rest) orelse return error.InvalidArgument; + field.* = cell.first; + rest = cell.second; + } + if (try r.node(rest) != .leaf) return error.InvalidArgument; + const token = try engine.token(fields[0], "io.v2.resource") orelse return error.InvalidArgument; + const directory = try decodeText(engine, fields[1]); + defer engine.arena.allocator.free(directory); + const path = try engine.arena.allocator.dupeZ(u8, directory); + defer engine.arena.allocator.free(path); + const limit = try r.nat(fields[2], 63) orelse return error.InvalidArgument; + const size = try r.nat(fields[3], 63) orelse return error.InvalidArgument; + var digest: [64]u8 = undefined; + rest = fields[4]; + for (&digest) |*byte| { + const cell = try r.pair(rest) orelse return error.InvalidArgument; + const scalar = try r.nat(cell.first, 7) orelse return error.InvalidArgument; + if (!((scalar >= '0' and scalar <= '9') or (scalar >= 'a' and scalar <= 'f'))) return error.InvalidArgument; + byte.* = @intCast(scalar); + rest = cell.second; + } + if (try r.node(rest) != .leaf) return error.InvalidArgument; + if (size > limit) return error.SnapshotBound; + if (directory.len == 0) return error.EmptySnapshotDirectory; + const registry = engine.scheduler.registry; + const backend = (try registry.lookup(scope, token)).backend; + if (backend != .file or (backend.file.mode != .reader and backend.file.mode != .update)) return error.WrongAccess; + const lease = try registry.beginLease(scope, token, .exclusive, false); + defer registry.endLease(lease.token) catch unreachable; + const temporary = blk: { + const admission = try registry.beginAcquire(scope); + errdefer registry.abortAcquire(admission) catch unreachable; + const file = try registry.allocator.create(F.File); + errdefer registry.allocator.destroy(file); + file.* = Snapshot.create(registry.allocator, status, path) catch |err| { + if (status.cleanup_code != 0) self.recordCloseError(.{ .code = status.cleanup_code }); + return err; + }; + errdefer { + var cleanup: F.Status = .{}; + file.close(&cleanup) catch self.recordCloseError(cleanup); + } + break :blk try registry.publish(admission, .{ .file = file }, .{ .internal = true }); + }; + self.opened += 1; + const file = (try registry.lookup(scope, temporary)).backend.file; + const result = blk: { + Snapshot.authenticate(file, backend.file, status, size, &digest) catch |err| break :blk @as(anyerror!u32, err); + var snapshot = Snapshot.Snapshot{ .file = file, .status = status, .length = size }; + break :blk Indexed.loadSnapshot(engine.arena, &snapshot) catch |err| switch (err) { + error.InvalidData, error.ExportNotFound => error.SnapshotInvalid, + else => err, + }; + }; + // All host work is synchronous. Retire the internal descriptor even on + // ordinary Result failure or OOM; cleanup must not replace a primary fault. + const owned = (try registry.claimClose(scope, temporary, true)).?; + defer registry.finishClose(temporary) catch unreachable; + var cleanup: F.Status = .{}; + self.closeWithStatus(registry, owned.backend, &cleanup) catch { + if (result) |_| return error.SnapshotCleanup else |primary| { + self.snapshot_cleanup_failed = true; + return primary; + } + }; + return result; + } + fn indexed(self: *Host, engine: anytype, scope: u64, op: Op, arguments: u32, status: *F.Status) !u32 { + const r = engine.reader(); + const first = try r.pair(arguments) orelse return error.InvalidArgument; + if (op == .hash_indexed) { + const limit = try r.nat(first.first, 63) orelse return error.InvalidArgument; + var plan = try Indexed.prepare(engine.arena, first.second); + defer plan.deinit(); + if (plan.encodedLength() > limit) return error.EncodedLimit; + const hash = try plan.hash(); + return engine.list(&.{ try C.ofNumber(engine.arena, plan.encodedLength()), try engine.text(&hash) }); + } + const token = try engine.token(first.first, "io.v2.resource") orelse return error.InvalidArgument; + const second = try r.pair(first.second) orelse return error.InvalidArgument; + const limit = try r.nat(second.first, 63) orelse return error.InvalidArgument; + const third = try r.pair(second.second) orelse return error.InvalidArgument; + const tree_field = if (op == .write_indexed) try r.pair(third.second) orelse return error.InvalidArgument else third; + if (try r.node(tree_field.second) != .leaf) return error.InvalidArgument; + var expected: [64]u8 = undefined; + if (op == .write_indexed) { + var rest = third.first; + for (&expected) |*byte| { + const cell = try r.pair(rest) orelse return error.InvalidArgument; + const scalar = try r.nat(cell.first, 7) orelse return error.InvalidArgument; + if (!((scalar >= '0' and scalar <= '9') or (scalar >= 'a' and scalar <= 'f'))) return error.InvalidArgument; + byte.* = @intCast(scalar); + rest = cell.second; + } + if (try r.node(rest) != .leaf) return error.InvalidArgument; + } + const registry = engine.scheduler.registry; + const backend = (try registry.lookup(scope, token)).backend; + if (op == .verify_indexed) { + if (backend != .file or (backend.file.mode != .reader and backend.file.mode != .update)) return error.WrongAccess; + } else switch (backend) { + .file => |file| if (file.mode == .reader) return error.WrongAccess, + .stage => {}, + else => return error.WrongAccess, + } + const lease = try registry.beginLease(scope, token, .exclusive, false); + defer registry.endLease(lease.token) catch unreachable; + var plan = try Indexed.prepare(engine.arena, tree_field.first); + defer plan.deinit(); + if (plan.encodedLength() > limit) return error.EncodedLimit; + const hash = try plan.hash(); + if (op == .write_indexed and !std.mem.eql(u8, &hash, &expected)) return error.DigestMismatch; + var sink = IndexedSink{ .host = self, .backend = backend, .status = status, .verify = op == .verify_indexed }; + try plan.emit(&sink); + return engine.list(&.{ try C.ofNumber(engine.arena, plan.encodedLength()), try engine.text(&hash) }); + } + const IndexedSink = struct { + host: *Host, + backend: Backend, + status: *F.Status, + verify: bool, + pub fn writeAll(self: *@This(), bytes: []const u8) !void { + var offset: usize = 0; + while (offset < bytes.len) { + const count = if (self.verify) blk: { + var actual: [4096]u8 = undefined; + const n = try self.backend.file.read(self.status, actual[0 .. bytes.len - offset]); + if (n == 0 or !std.mem.eql(u8, actual[0..n], bytes[offset..][0..n])) return error.FrameMismatch; + break :blk n; + } else switch (self.backend) { + .file => |file| try file.write(self.status, bytes[offset..]), + .stage => |stage| blk: { + self.host.stage_operation = true; + break :blk try stage.write(&self.host.stage_status, bytes[offset..]); + }, + else => unreachable, + }; + std.debug.assert(count > 0 and count <= bytes.len - offset); + offset += count; + } + } + }; + const core_names = [_][]const u8{ "pure", "bind", "fail", "scope", "current-scope", "spawn", "join", "discard", "cancel", "cancelled" }; + fn strings(engine: anytype, values: []const []const u8) !u32 { + var ids: std.ArrayList(u32) = .empty; + defer ids.deinit(engine.arena.allocator); + for (values) |value| try ids.append(engine.arena.allocator, try engine.text(value)); + return engine.list(ids.items); + } + fn row(engine: anytype, key: []const u8, value: u32) !u32 { + return engine.list(&.{ try engine.text(key), value }); + } + fn capabilities(engine: anytype) !u32 { + var groups: std.ArrayList(u32) = .empty; + defer groups.deinit(engine.arena.allocator); + inline for (.{ "runtime", "resource", "stream", "file", "path", "directory", "stage", "digest", "indexed-tree", "utf8" }) |group| { + var operations: std.ArrayList(u32) = .empty; + defer operations.deinit(engine.arena.allocator); + inline for (entries) |entry| { + if (comptime std.mem.eql(u8, group, entry[0])) try operations.append(engine.arena.allocator, try engine.text(entry[1])); + } + try groups.append(engine.arena.allocator, try engine.list(&.{ try engine.text(group), try C.ofNumber(engine.arena, 1), try engine.list(operations.items) })); + } + const limits = try engine.list(&.{ + try row(engine, "chunk-bytes", try C.ofNumber(engine.arena, 4096)), + try row(engine, "transfer-handles", try C.ofNumber(engine.arena, 256)), + try row(engine, "file-offset-max", try C.ofNumber(engine.arena, F.limit)), + try row(engine, "indexed-sections", try C.ofNumber(engine.arena, 64)), + try row(engine, "indexed-manifest-bytes", try C.ofNumber(engine.arena, 65536)), + try row(engine, "utf8-encode-scalars", try C.ofNumber(engine.arena, 1024)), + }); + return engine.list(&.{ + try row(engine, "schema", try C.ofNumber(engine.arena, 1)), + try row(engine, "io-version", try C.ofNumber(engine.arena, 2)), + try row(engine, "backend", try engine.text("zig-native")), + try row(engine, "authority", try engine.text("os-process")), + try row(engine, "core", try strings(engine, &core_names)), + try row(engine, "groups", try engine.list(groups.items)), + try row(engine, "limits", limits), + try row(engine, "digest-algorithms", try strings(engine, &.{"sha256"})), + try row(engine, "restrictions", try strings(engine, &.{ + "action-boundary-cancellation", "no-hard-deadlines", "synchronous-host-calls", + "path-files-regular-only", "standard-streams-unsupported", "directory-cursors-unsupported", + "file-single-cursor-lease", "append-no-position-or-resize", "stage-same-filesystem", + "stage-no-copy-fallback", "stage-private-namespace", "publish-new-requires-hard-links", + "no-crash-recovery", "indexed-authenticated-snapshot", "indexed-caller-frame-and-disk-limit", + "indexed-dag-resident", "indexed-writer-preflight", "indexed-budget-not-heap-quota", + "sync-host-filesystem-guarantees", "utf8-strict-scalars", + })), + }); + } + fn dispatch(self: *Host, engine: anytype, scope: u64, op: Op, arguments: u32, status: *F.Status) !u32 { + const r = engine.reader(); + const registry = engine.scheduler.registry; + if (op == .capabilities) { + if (try r.node(arguments) != .leaf) return error.InvalidArgument; + return capabilities(engine); + } + if (op == .inspect) { + const action = Protocol.decode(engine.arena, arguments, r.fuel) catch |err| switch (err) { + error.InvalidComputationShape, error.UnknownComputationConstructor, error.InvalidFacilityHeader => return error.InvalidArgument, + else => return err, + }; + return switch (action) { + .core => |value| engine.list(&.{ try engine.text("core"), try engine.text(core_names[@intFromEnum(value.code)]) }), + .request => |value| engine.list(&.{ try engine.text("facility"), try engine.text(value.group.slice()), try C.ofNumber(engine.arena, value.revision), try engine.text(value.operation.slice()), try engine.text(if (value.revision == 1 and resolve(value.group.slice(), value.operation.slice()) != null) "available" else "unsupported") }), + }; + } + if (op == .read_indexed) return self.readIndexed(engine, scope, arguments, status); + if (op == .hash_indexed or op == .write_indexed or op == .verify_indexed) return self.indexed(engine, scope, op, arguments, status); + if (op == .encode_utf8) { + var bytes: [4096]u8 = undefined; + var scalars: usize = 0; + var used: usize = 0; + var rest = arguments; + while (try r.node(rest) != .leaf) { + if (scalars == 1024) return error.InvalidArgument; + const cell = try r.pair(rest) orelse return error.InvalidArgument; + const scalar = try r.nat(cell.first, 21) orelse return error.InvalidArgument; + if (scalar > 0x10ffff or (scalar >= 0xd800 and scalar <= 0xdfff)) return error.InvalidArgument; + used += try std.unicode.utf8Encode(@intCast(scalar), bytes[used..][0..4]); + scalars += 1; + rest = cell.second; + } + return C.ofBytes(engine.arena, bytes[0..used]); + } + if (op == .decode_utf8) { + const final = try r.pair(arguments) orelse return error.InvalidArgument; + const input = try r.pair(final.second) orelse return error.InvalidArgument; + if (try r.node(input.second) != .leaf) return error.InvalidArgument; + const ended = try r.nat(final.first, 1) orelse return error.InvalidArgument; + var bytes: [4096]u8 = undefined; + const count = try decodeChunk(engine, input.first, &bytes); + return switch (UTF8.scan(bytes[0..count], ended == 1)) { + .too_large => unreachable, // argument decoder already bounds it + .fault => |fault| blk: { + const why: []const u8 = switch (fault.kind) { + .leading => "invalid UTF-8 leading byte", + .sequence => "invalid UTF-8 sequence", + .incomplete => "incomplete UTF-8 sequence", + }; + const detail = try std.fmt.bufPrint(&self.utf8_detail, "{s} at byte {d}", .{ why, fault.offset }); + self.utf8_detail_len = detail.len; + break :blk error.InvalidData; + }, + .prefix => |prefix| try engine.list(&.{ try Text.fromUtf8(engine.arena, bytes[0..prefix]), try C.ofBytes(engine.arena, bytes[prefix..count]) }), + }; + } + switch (op) { + .stat, .lstat, .create_directory, .remove_directory, .remove_file, .rename_path, .sync_directory => { + const paths = if (op == .rename_path) try r.pair(arguments) orelse return error.InvalidArgument else null; + const raw = try decodeText(engine, if (paths) |p| p.first else arguments); + defer engine.arena.allocator.free(raw); + const path = try engine.arena.allocator.dupeZ(u8, raw); + defer engine.arena.allocator.free(path); + if (op == .stat or op == .lstat) { + const info = try Paths.stat(status, path, op == .stat); + return switch (info) { + .regular => |size| try engine.list(&.{ try engine.text("file"), try C.ofNumber(engine.arena, size) }), + .directory => try engine.list(&.{try engine.text("directory")}), + .symlink => try engine.list(&.{try engine.text("symlink")}), + .other => try engine.list(&.{try engine.text("other")}), + }; + } + if (op == .sync_directory) { + Paths.syncDirectory(status, path) catch |err| { + if (status.cleanup_code != 0) self.recordCloseError(.{ .code = status.cleanup_code }); + return err; + }; + return engine.leaf; + } + if (op == .rename_path) { + const other_raw = try decodeText(engine, paths.?.second); + defer engine.arena.allocator.free(other_raw); + const other = try engine.arena.allocator.dupeZ(u8, other_raw); + defer engine.arena.allocator.free(other); + try Paths.mutate(status, .rename, path, other); + } else { + try Paths.mutate(status, switch (op) { + .create_directory => .create_directory, + .remove_directory => .remove_directory, + .remove_file => .remove_file, + else => unreachable, + }, path, null); + } + return engine.leaf; + }, + else => {}, + } + switch (op) { + .open_reader, .open_writer, .open_appender, .open_update, .open_digest, .open_stage => { + const pair = try r.pair(arguments) orelse return error.InvalidArgument; + const owner = try engine.token(pair.first, "io.v2.scope") orelse return error.InvalidArgument; + if (owner.run != registry.identity or !registry.within(scope, owner.key)) return error.InvalidArgument; + const text = try decodeText(engine, pair.second); + defer engine.arena.allocator.free(text); + const path = try engine.arena.allocator.dupeZ(u8, text); + defer engine.arena.allocator.free(path); + if (op == .open_digest) { + if (!std.mem.eql(u8, text, "sha256")) { + const view = try std.unicode.Utf8View.init(text); + var it = view.iterator(); + var scalars: usize = 0; + while (scalars < 128 and it.nextCodepoint() != null) : (scalars += 1) {} + self.algorithm_detail_len = it.i; + @memcpy(self.algorithm_detail[0..it.i], text[0..it.i]); + return error.InvalidAlgorithm; + } + // End acquisition rollback BEFORE encoding the source token. + // Once published, only scope teardown owns this state, even + // when reply allocation fails immediately afterwards. + const token = acquired: { + const admission = try registry.beginAcquire(owner.key); + errdefer registry.abortAcquire(admission) catch unreachable; + const digest = try registry.allocator.create(Digest); + errdefer registry.allocator.destroy(digest); + digest.* = .{}; + break :acquired try registry.publish(admission, .{ .digest = digest }, .{}); + }; + self.opened += 1; + return engine.marker("io.v2.resource", token.key); + } + if (op == .open_stage) { + self.stage_operation = true; + const token = acquired: { + const admission = try registry.beginAcquire(owner.key); + errdefer registry.abortAcquire(admission) catch unreachable; + const stage = try registry.allocator.create(Stage.Stage); + errdefer registry.allocator.destroy(stage); + stage.* = try Stage.Stage.init(registry.allocator, &self.stage_status, path); + errdefer { + var released: Stage.Status = .{}; + stage.release(&released) catch self.recordCloseError(.{ .code = released.code, .cleanup_code = released.secondary }); + stage.deinit(); + } + break :acquired try registry.publish(admission, .{ .stage = stage }, .{}); + }; + self.opened += 1; + return engine.marker("io.v2.resource", token.key); + } + const mode: F.Mode = switch (op) { + .open_reader => .reader, + .open_writer => .writer, + .open_appender => .appender, + .open_update => .update, + else => unreachable, + }; + const token = F.acquireWrapped(registry, owner.key, status, path, mode, fileBackend) catch |err| { + if (status.cleanup_code != 0) self.recordCloseError(.{ .code = status.cleanup_code }); + return err; + }; + self.opened += 1; + return engine.marker("io.v2.resource", token.key); + }, + else => {}, + } + const paired = op == .read or op == .write or op == .update_digest or op == .seek or op == .resize or op == .publish_stage; + const pair = if (paired) try r.pair(arguments) orelse return error.InvalidArgument else null; + const token = try engine.token(if (pair) |p| p.first else arguments, "io.v2.resource") orelse return error.InvalidArgument; + var number: u64 = 0; + var bytes: [4096]u8 = undefined; + var count: usize = 0; + switch (op) { + .read, .seek, .resize => { + number = try r.nat(pair.?.second, if (op == .read) 16 else 63) orelse return error.InvalidArgument; + if (op == .read and (number == 0 or number > 4096)) return error.InvalidBound; + }, + .write, .update_digest => { + count = try decodeChunk(engine, pair.?.second, &bytes); + if (count == 0) return error.InvalidBound; + }, + else => {}, + } + var destination: ?[:0]u8 = null; + defer if (destination) |path| engine.arena.allocator.free(path); + if (op == .publish_stage) { + const fields = try r.pair(pair.?.second) orelse return error.InvalidArgument; + number = try r.nat(fields.first, 1) orelse return error.InvalidArgument; + const raw = try decodeText(engine, fields.second); + defer engine.arena.allocator.free(raw); + destination = try engine.arena.allocator.dupeZ(u8, raw); + } + if (op == .close) { + const resource = try registry.claimClose(scope, token, false) orelse return error.Busy; + defer registry.finishClose(token) catch unreachable; + try self.closeWithStatus(registry, resource.backend, status); + return engine.leaf; + } + const backend = (try registry.lookup(scope, token)).backend; + if (op == .seal_stage or op == .publish_stage or (op == .write and backend == .stage)) { + if (backend != .stage) return error.WrongAccess; + const lease = try registry.beginLease(scope, token, .exclusive, false); + defer registry.endLease(lease.token) catch unreachable; + self.stage_operation = true; + if (op == .write) return C.ofNumber(engine.arena, try backend.stage.write(&self.stage_status, bytes[0..count])); + if (op == .seal_stage) { + try backend.stage.seal(&self.stage_status); + return engine.leaf; + } + const published = try backend.stage.publish(&self.stage_status, number == 1, destination.?); + if (published == .published) self.published += 1; + return engine.text(if (published == .published) "published" else "exists"); + } + if (op == .update_digest or op == .finish_digest) { + if (backend != .digest) return error.WrongAccess; + const lease = try registry.beginLease(scope, token, .exclusive, false); + defer registry.endLease(lease.token) catch unreachable; + if (op == .update_digest) { + try backend.digest.update(bytes[0..count]); + return engine.leaf; + } + const hex = try backend.digest.finish(); + return engine.text(&hex); + } + if (backend != .file) return error.WrongAccess; + const file = backend.file; + const lease = try registry.beginLease(scope, token, .exclusive, false); + defer registry.endLease(lease.token) catch unreachable; + return switch (op) { + .read => blk: { + const n = try file.read(status, bytes[0..@intCast(number)]); + const label = try engine.text(if (n == 0) "eof" else "data"); + break :blk try engine.arena.alloc(.{ .fork = .{ .left = label, .right = if (n == 0) engine.leaf else try C.ofBytes(engine.arena, bytes[0..n]) } }); + }, + .write => try C.ofNumber(engine.arena, try file.write(status, bytes[0..count])), + .seek => blk: { + try file.seek(number); + break :blk engine.leaf; + }, + .resize => blk: { + try file.resize(status, number); + break :blk engine.leaf; + }, + .position => try C.ofNumber(engine.arena, try file.position()), + .size => try C.ofNumber(engine.arena, try file.size(status)), + .sync => blk: { + try file.sync(status); + break :blk engine.leaf; + }, + else => unreachable, + }; + } +}; + +fn testProgram(a: *@import("arena.zig").Arena, path: []const u8, terminal: ?u64) !u32 { + return testOpenProgram(a, path, terminal, "file", "open-reader"); +} +fn testOpenProgram(a: *@import("arena.zig").Arena, path: []const u8, terminal: ?u64, group: []const u8, operation: []const u8) !u32 { + const leaf = try a.alloc(.leaf); + const scope = try C.ofList(a, &.{ try C.ofString(a, "io.v2.scope"), try C.ofNumber(a, 1), leaf }); + const args = try a.alloc(.{ .fork = .{ .left = scope, .right = try Text.fromUtf8(a, path) } }); + const named = try a.alloc(.{ .fork = .{ .left = try C.ofString(a, group), .right = try a.alloc(.{ .fork = .{ .left = try C.ofNumber(a, 1), .right = try a.alloc(.{ .fork = .{ .left = try C.ofString(a, operation), .right = args } }) } }) } }); + const request = try a.alloc(.{ .fork = .{ .left = try C.ofNumber(a, 10), .right = named } }); + if (terminal) |tag| { + const next = try a.alloc(.{ .fork = .{ .left = try C.ofNumber(a, tag), .right = leaf } }); + const constant = try a.alloc(.{ .fork = .{ .left = leaf, .right = next } }); + return a.alloc(.{ .fork = .{ .left = try C.ofNumber(a, 1), .right = try a.alloc(.{ .fork = .{ .left = request, .right = constant } }) } }); + } + return request; +} +fn failureProbe(allocator: std.mem.Allocator, path: []const u8) !void { + var a = @import("arena.zig").Arena.init(allocator); + defer a.deinit(); + const program = try testProgram(&a, path, null); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + var host: Host = .{}; + defer std.debug.assert(host.opened == host.closed); + _ = try Core.runWithHost(Host, &host, &a, program, 1, 10000); + try std.testing.expectEqual(@as(usize, 1), host.opened); +} +test "physical resources settle on success, fail, cancellation, protocol fault and allocation failure" { + const testing = std.testing; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "file", .data = "abc" }); + const path = try tmp.dir.realPathFileAlloc(testing.io, "file", testing.allocator); + defer testing.allocator.free(path); + for ([_]u64{ 0, 2, 9, 11 }) |tag| { + var a = @import("arena.zig").Arena.init(testing.allocator); + defer a.deinit(); + const program = try testProgram(&a, path, tag); + var host: Host = .{}; + const outcome = try Core.runWithHost(Host, &host, &a, program, 1, 10000); + try testing.expectEqual(@as(usize, 1), host.opened); + try testing.expectEqual(host.opened, host.closed); + try testing.expectEqual(@as(usize, 0), host.close_error_count); + const expected: @TypeOf(outcome.kind) = if (tag == 0) .completed else if (tag == 9) .cancelled else .failed; + try testing.expectEqual(expected, outcome.kind); + } + try testing.checkAllAllocationFailures(testing.allocator, failureProbe, .{path}); +} + +const CloseFailureHost = struct { + pub const Backend = Host.Backend; + pub const standard = Host.standard; + inner: Host = .{}, + pub fn request(self: *@This(), engine: anytype, scope: u64, req: anytype) !u32 { + return self.inner.request(engine, scope, req); + } + pub fn close(self: *@This(), registry: anytype, backend: Backend) !void { + // Simulate a close that has released its descriptor but reports failure. + // No external FD is closed/reused, and every real descriptor is released. + var original = backend.file.*; + var status: F.Status = .{}; + try original.close(&status); + backend.file.fd = -1; + try self.inner.close(registry, backend); + } +}; +test "physical close failures defeat success but retain primary failure and cancellation" { + const testing = std.testing; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "file", .data = "abc" }); + const path = try tmp.dir.realPathFileAlloc(testing.io, "file", testing.allocator); + defer testing.allocator.free(path); + for ([_]u64{ 0, 2, 9 }) |tag| { + var a = @import("arena.zig").Arena.init(testing.allocator); + defer a.deinit(); + const program = try testProgram(&a, path, tag); + var host: CloseFailureHost = .{}; + const outcome = try Core.runWithHost(CloseFailureHost, &host, &a, program, 1, 10000); + try testing.expectEqual(@as(usize, 1), host.inner.closed); + try testing.expectEqual(@as(usize, 1), host.inner.close_error_count); + const expected: @TypeOf(outcome.kind) = if (tag == 9) .cancelled else .failed; + try testing.expectEqual(expected, outcome.kind); + if (tag != 0) { + try testing.expect(a.get(outcome.value).* == .leaf); + try testing.expectEqual(@as(usize, 1), outcome.cleanup.len); + } + } +} + +fn digestAllocationProbe(allocator: std.mem.Allocator) !void { + var a = @import("arena.zig").Arena.init(allocator); + defer a.deinit(); + const program = try testOpenProgram(&a, "sha256", null, "digest", "open"); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + var host: Host = .{}; + defer std.debug.assert(host.opened == host.closed); + _ = try Core.runWithHost(Host, &host, &a, program, 1, 10000); + try std.testing.expectEqual(@as(usize, 1), host.opened); +} +test "digest acquisition and reply allocation failures retire every admitted state" { + try std.testing.checkAllAllocationFailures(std.testing.allocator, digestAllocationProbe, .{}); +} + +fn stageAllocationProbe(allocator: std.mem.Allocator, directory: []const u8, dir: std.Io.Dir) !void { + var a = @import("arena.zig").Arena.init(allocator); + defer a.deinit(); + const program = try testOpenProgram(&a, directory, null, "stage", "open"); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + var host: Host = .{}; + defer std.debug.assert(host.opened == host.closed); + const outcome = Core.runWithHost(Host, &host, &a, program, 1, 10000); + var iterator = dir.iterate(); + try std.testing.expect((try iterator.next(std.testing.io)) == null); + _ = try outcome; + try std.testing.expectEqual(@as(usize, 1), host.opened); +} +test "stage acquisition and reply allocation failures remove every private name" { + const testing = std.testing; + var tmp = testing.tmpDir(.{ .iterate = true }); + defer tmp.cleanup(); + const directory = try tmp.dir.realPathFileAlloc(testing.io, ".", testing.allocator); + defer testing.allocator.free(directory); + try testing.checkAllAllocationFailures(testing.allocator, stageAllocationProbe, .{ directory, tmp.dir }); +} + +fn testRequest(a: *@import("arena.zig").Arena, group: []const u8, operation: []const u8, args: u32) !u32 { + const payload = try a.alloc(.{ .fork = .{ .left = try C.ofString(a, group), .right = try a.alloc(.{ .fork = .{ .left = try C.ofNumber(a, 1), .right = try a.alloc(.{ .fork = .{ .left = try C.ofString(a, operation), .right = args } }) } }) } }); + return a.alloc(.{ .fork = .{ .left = try C.ofNumber(a, 10), .right = payload } }); +} +fn then(a: *@import("arena.zig").Arena, left: u32, right: u32) !u32 { + const constant = try a.alloc(.{ .fork = .{ .left = try a.alloc(.leaf), .right = right } }); + return a.alloc(.{ .fork = .{ .left = try C.ofNumber(a, 1), .right = try a.alloc(.{ .fork = .{ .left = left, .right = constant } }) } }); +} +fn publicationAllocationProbe(allocator: std.mem.Allocator, directory: []const u8, destination: []const u8, dir: std.Io.Dir) !void { + var a = @import("arena.zig").Arena.init(allocator); + defer a.deinit(); + const opened = try testOpenProgram(&a, directory, null, "stage", "open"); + const token = try C.ofList(&a, &.{ try C.ofString(&a, "io.v2.resource"), try C.ofNumber(&a, 1), try C.ofNumber(&a, 5) }); + const sealed = try testRequest(&a, "stage", "seal", token); + const args = try a.alloc(.{ .fork = .{ .left = token, .right = try a.alloc(.{ .fork = .{ .left = try C.ofNumber(&a, 1), .right = try Text.fromUtf8(&a, destination) } }) } }); + const publish = try testRequest(&a, "stage", "publish", args); + const program = try then(&a, opened, try then(&a, sealed, publish)); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + var host: Host = .{}; + const result = Core.runWithHost(Host, &host, &a, program, 1, 10000); + try std.testing.expectEqual(host.opened, host.closed); + var iterator = dir.iterate(); + var visible: usize = 0; + while (try iterator.next(std.testing.io)) |entry| { + try std.testing.expectEqualStrings("out", entry.name); // never a private name + visible += 1; + } + try std.testing.expectEqual(host.published, visible); // OOM cannot roll back visibility + if (visible != 0) try dir.deleteFile(std.testing.io, "out"); + _ = try result; + try std.testing.expectEqual(@as(usize, 1), host.published); +} +test "allocation failures before and after publication clean private names without rollback" { + const testing = std.testing; + var tmp = testing.tmpDir(.{ .iterate = true }); + defer tmp.cleanup(); + const directory = try tmp.dir.realPathFileAlloc(testing.io, ".", testing.allocator); + defer testing.allocator.free(directory); + const destination = try std.fmt.allocPrint(testing.allocator, "{s}/out", .{directory}); + defer testing.allocator.free(destination); + try testing.checkAllAllocationFailures(testing.allocator, publicationAllocationProbe, .{ directory, destination, tmp.dir }); +} + +fn indexedWriteAllocationProbe(allocator: std.mem.Allocator, path: []const u8, dir: std.Io.Dir) !void { + defer dir.deleteFile(std.testing.io, "out") catch {}; + var a = @import("arena.zig").Arena.init(allocator); + defer a.deinit(); + const opened = try testOpenProgram(&a, path, null, "file", "open-writer"); + const token = try C.ofList(&a, &.{ try C.ofString(&a, "io.v2.resource"), try C.ofNumber(&a, 1), try C.ofNumber(&a, 5) }); + const args = try C.ofList(&a, &.{ token, try C.ofNumber(&a, 431), try C.ofString(&a, "6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1"), try a.alloc(.leaf) }); + const program = try then(&a, opened, try testRequest(&a, "indexed-tree", "write", args)); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + var host: Host = .{}; + const outcome = Core.runWithHost(Host, &host, &a, program, 1, 10000); + try std.testing.expectEqual(host.opened, host.closed); + if (host.opened != 0) { + const bytes = try dir.readFileAlloc(std.testing.io, "out", std.testing.allocator, .limited(1024)); + defer std.testing.allocator.free(bytes); + // Preflight never writes; a failure encoding the reply cannot undo the + // completed write. There are no allocations inside native emission. + try std.testing.expect(bytes.len == 0 or bytes.len == 431); + if (bytes.len != 0) try std.testing.expectEqualStrings("6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1", &Indexed.digest(bytes)); + if (outcome) |_| try std.testing.expectEqual(@as(usize, 431), bytes.len) else |_| {} + } + _ = try outcome; + try std.testing.expectEqual(@as(usize, 1), host.opened); +} +test "indexed writer preflight and reply OOM settle leases without rolling back writes" { + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const directory = try tmp.dir.realPathFileAlloc(std.testing.io, ".", std.testing.allocator); + defer std.testing.allocator.free(directory); + const path = try std.fmt.allocPrint(std.testing.allocator, "{s}/out", .{directory}); + defer std.testing.allocator.free(path); + try std.testing.checkAllAllocationFailures(std.testing.allocator, indexedWriteAllocationProbe, .{ path, tmp.dir }); +} + +fn snapshotAllocationProbe(allocator: std.mem.Allocator, path: []const u8, directory: []const u8, dir: std.Io.Dir) !void { + var a = @import("arena.zig").Arena.init(allocator); + defer a.deinit(); + const opened = try testOpenProgram(&a, path, null, "file", "open-reader"); + const token = try C.ofList(&a, &.{ try C.ofString(&a, "io.v2.resource"), try C.ofNumber(&a, 1), try C.ofNumber(&a, 5) }); + const args = try C.ofList(&a, &.{ token, try Text.fromUtf8(&a, directory), try C.ofNumber(&a, 431), try C.ofNumber(&a, 431), try C.ofString(&a, "6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1") }); + const program = try then(&a, opened, try testRequest(&a, "indexed-tree", "read", args)); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + var host: Host = .{}; + const outcome = Core.runWithHost(Host, &host, &a, program, 1, 10000); + try std.testing.expectEqual(host.opened, host.closed); + var iterator = dir.iterate(); + while (try iterator.next(std.testing.io)) |entry| try std.testing.expectEqualStrings("in", entry.name); + _ = try outcome; + try std.testing.expectEqual(@as(usize, 2), host.opened); +} +test "snapshot acquisition decode and reply OOM close every file without private names" { + var tmp = std.testing.tmpDir(.{ .iterate = true }); + defer tmp.cleanup(); + const directory = try tmp.dir.realPathFileAlloc(std.testing.io, ".", std.testing.allocator); + defer std.testing.allocator.free(directory); + const path = try std.fmt.allocPrint(std.testing.allocator, "{s}/in", .{directory}); + defer std.testing.allocator.free(path); + var arena = @import("arena.zig").Arena.init(std.testing.allocator); + defer arena.deinit(); + const bytes = try Indexed.encode(&arena, try arena.alloc(.leaf)); + defer std.testing.allocator.free(bytes); + try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "in", .data = bytes }); + try std.testing.checkAllAllocationFailures(std.testing.allocator, snapshotAllocationProbe, .{ path, directory, tmp.dir }); +} diff --git a/ext/zig/src/io_v2_file_probe.zig b/ext/zig/src/io_v2_file_probe.zig @@ -0,0 +1,4 @@ +//! Private source integration tool. Does not enable the production IO entrypoints. +pub fn main(init: @import("std").process.Init) !void { + return @import("io_v2_core_probe.zig").mainFor(@import("io_v2_file_host.zig").Host, init); +} diff --git a/ext/zig/src/io_v2_files.zig b/ext/zig/src/io_v2_files.zig @@ -0,0 +1,397 @@ +//! Private native regular-file backend, not a v2 facility dispatcher. Single +//! owner; the embedding supplies scope admission and exclusive registry leases. +//! These synchronous bounded calls make no cancellation/deadline guarantee. +//! Never use this for standard streams, FIFOs or devices. No v1 policy/dispatch. +const std = @import("std"); +const c = @cImport({ + @cUndef("_FORTIFY_SOURCE"); + @cDefine("_FORTIFY_SOURCE", "0"); + @cInclude("uv.h"); + @cInclude("sys/stat.h"); + @cInclude("fcntl.h"); +}); +pub const limit = std.math.maxInt(i64); +pub const chunk_limit = 4096; +pub const Mode = enum { reader, writer, appender, update }; +pub const Error = error{ InvalidArgument, InvalidBound, UnsupportedFileType, WrongAccess, UnsupportedMode, Closed, HostFailure }; +// Error codes are copied before request cleanup. A failed acquisition retains its +// primary error AND any close error; cleanup does not replace the primary fault. +pub const Status = struct { code: c_int = 0, cleanup_code: c_int = 0 }; +pub const File = struct { + fd: ?c.uv_file, + mode: Mode, + cursor: u64 = 0, + fn descriptor(self: *const File) Error!c.uv_file { + return self.fd orelse error.Closed; + } + pub fn close(self: *File, status: *Status) Error!void { + status.* = .{}; + const fd = try self.descriptor(); + self.fd = null; // Never retry an uncertain close against a reused FD. + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_close(null, &req, fd, null); + c.uv_fs_req_cleanup(&req); + try check(status, rc); + } + pub fn read(self: *File, status: *Status, bytes: []u8) Error!usize { + status.* = .{}; + if (bytes.len == 0 or bytes.len > chunk_limit) return error.InvalidBound; + const fd = try self.descriptor(); + if (self.mode != .reader and self.mode != .update) return error.WrongAccess; + const count = @min(bytes.len, limit - self.cursor); + if (count == 0) { + _ = try self.size(status); + return 0; + } + var buffer = c.uv_buf_init(bytes.ptr, @intCast(count)); + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_read(null, &req, fd, &buffer, 1, @intCast(self.cursor), null); + c.uv_fs_req_cleanup(&req); + try check(status, rc); + self.cursor += @intCast(rc); + return @intCast(rc); + } + pub fn write(self: *File, status: *Status, bytes: []const u8) Error!usize { + status.* = .{}; + if (bytes.len == 0 or bytes.len > chunk_limit) return error.InvalidBound; + const fd = try self.descriptor(); + if (self.mode == .reader) return error.WrongAccess; + if (self.mode != .appender and bytes.len > limit - self.cursor) return error.InvalidBound; + var buffer = c.uv_buf_init(@constCast(bytes.ptr), @intCast(bytes.len)); + var req: c.uv_fs_t = undefined; + const offset: i64 = if (self.mode == .appender) -1 else @intCast(self.cursor); + const rc = c.uv_fs_write(null, &req, fd, &buffer, 1, offset, null); + c.uv_fs_req_cleanup(&req); + try check(status, rc); + if (rc == 0) { + status.code = c.UV_EIO; + return error.HostFailure; + } + if (self.mode != .appender) self.cursor += @intCast(rc); + return @intCast(rc); + } + pub fn seek(self: *File, offset: u64) Error!void { + if (offset > limit) return error.InvalidBound; + _ = try self.descriptor(); + if (self.mode == .appender) return error.UnsupportedMode; + self.cursor = offset; + } + pub fn position(self: *const File) Error!u64 { + _ = try self.descriptor(); + if (self.mode == .appender) return error.UnsupportedMode; + return self.cursor; + } + pub fn size(self: *const File, status: *Status) Error!u64 { + status.* = .{}; + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_fstat(null, &req, try self.descriptor(), null); + defer c.uv_fs_req_cleanup(&req); + try check(status, rc); + if (req.statbuf.st_size > limit) return error.InvalidBound; + return req.statbuf.st_size; + } + pub fn resize(self: *File, status: *Status, length: u64) Error!void { + status.* = .{}; + if (length > limit) return error.InvalidBound; + const fd = try self.descriptor(); + if (self.mode != .writer and self.mode != .update) return error.UnsupportedMode; + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_ftruncate(null, &req, fd, @intCast(length), null); + c.uv_fs_req_cleanup(&req); + try check(status, rc); // Explicit-offset cursor survives resize, even past EOF. + } + pub fn sync(self: *const File, status: *Status) Error!void { + status.* = .{}; + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_fsync(null, &req, try self.descriptor(), null); + c.uv_fs_req_cleanup(&req); + try check(status, rc); + } +}; +fn check(status: *Status, rc: c_int) Error!void { + if (rc < 0) { + status.code = rc; + return error.HostFailure; + } +} +fn regular(mode: u64) bool { + return mode & c.S_IFMT == c.S_IFREG; +} +pub fn open(status: *Status, path: [:0]const u8, mode: Mode) Error!File { + return openAfterCheck(status, path, mode, null); +} +const CheckHook = struct { context: *anyopaque, call: *const fn (*anyopaque) void }; +// Private deterministic namespace-race seam; never a source callback. +fn openAfterCheck(status: *Status, path: [:0]const u8, mode: Mode, hook: ?CheckHook) Error!File { + status.* = .{}; + if (path.len == 0 or std.mem.indexOfScalar(u8, path, 0) != null or !std.unicode.utf8ValidateSlice(path)) return error.InvalidArgument; + // Preflight avoids opening known devices. It is NOT a security check: the + // namespace can change. The descriptor check below is authoritative, and + // deliberately precedes truncation. Opening a raced device may have effects. + var req: c.uv_fs_t = undefined; + const stat_rc = c.uv_fs_stat(null, &req, path.ptr, null); + const is_regular = stat_rc >= 0 and regular(req.statbuf.st_mode); + c.uv_fs_req_cleanup(&req); + if (stat_rc >= 0 and !is_regular) return error.UnsupportedFileType; + if (stat_rc < 0 and (stat_rc != c.UV_ENOENT or mode == .reader)) try check(status, stat_rc); + if (hook) |h| h.call(h.context); + const flags: c_int = c.O_CLOEXEC | c.O_NONBLOCK | c.O_NOCTTY | switch (mode) { + .reader => c.O_RDONLY, + .writer => c.O_WRONLY | c.O_CREAT, // NO O_TRUNC until fstat accepts the FD. + .appender => c.O_WRONLY | c.O_CREAT | c.O_APPEND, + .update => c.O_RDWR | c.O_CREAT, + }; + const fd = c.uv_fs_open(null, &req, path.ptr, flags, 0o666, null); + c.uv_fs_req_cleanup(&req); + try check(status, fd); + var file: File = .{ .fd = fd, .mode = mode }; + errdefer { + var cleanup: Status = .{}; + file.close(&cleanup) catch {}; + status.cleanup_code = cleanup.code; + } + const rc = c.uv_fs_fstat(null, &req, fd, null); + const accepted = rc >= 0 and regular(req.statbuf.st_mode); + c.uv_fs_req_cleanup(&req); + try check(status, rc); + if (!accepted) return error.UnsupportedFileType; + if (mode == .writer) try file.resize(status, 0); + return file; +} + +const testing = std.testing; +fn testPath(tmp: std.Io.Dir, name: []const u8) ![:0]u8 { + const directory = try tmp.realPathFileAlloc(testing.io, ".", testing.allocator); + defer testing.allocator.free(directory); + return std.fmt.allocPrintSentinel(testing.allocator, "{s}/{s}", .{ directory, name }, 0); +} +test "regular file modes, bounded transfers and cursor-preserving resize/sync" { + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + const path = try testPath(tmp.dir, "é😀"); + defer testing.allocator.free(path); + var status: Status = .{}; + var file = try open(&status, path, .update); + defer file.close(&status) catch {}; + try testing.expectEqual(@as(usize, 3), try file.write(&status, "abc")); + try file.sync(&status); + try testing.expectEqual(@as(u64, 3), try file.position()); + try file.seek(1); + var bytes: [4097]u8 = undefined; + try testing.expectEqual(@as(usize, 2), try file.read(&status, bytes[0..3])); + try testing.expectEqualStrings("bc", bytes[0..2]); + try testing.expectEqual(@as(usize, 0), try file.read(&status, bytes[0..3])); + try file.seek(10); + try file.resize(&status, 1); + try testing.expectEqual(@as(u64, 10), try file.position()); + try testing.expectEqual(@as(u64, 1), try file.size(&status)); + try testing.expectError(error.InvalidBound, file.read(&status, &bytes)); + try testing.expectError(error.InvalidBound, file.read(&status, bytes[0..0])); + try testing.expectError(error.InvalidBound, file.write(&status, &bytes)); + try testing.expectError(error.InvalidBound, file.seek(@as(u64, limit) + 1)); + try testing.expectError(error.InvalidBound, file.resize(&status, @as(u64, limit) + 1)); + try file.close(&status); + try testing.expectError(error.Closed, file.close(&status)); + try testing.expectError(error.Closed, file.read(&status, bytes[0..1])); + var writer = try open(&status, path, .writer); + defer writer.close(&status) catch {}; + try testing.expectEqual(@as(u64, 0), try writer.size(&status)); + try testing.expectError(error.WrongAccess, writer.read(&status, bytes[0..1])); + _ = try writer.write(&status, "x"); + var appender = try open(&status, path, .appender); + defer appender.close(&status) catch {}; + _ = try appender.write(&status, "y"); + try testing.expectError(error.UnsupportedMode, appender.seek(0)); + try testing.expectError(error.UnsupportedMode, appender.position()); + try testing.expectError(error.UnsupportedMode, appender.resize(&status, 0)); + try testing.expectError(error.WrongAccess, appender.read(&status, bytes[0..1])); + var reader = try open(&status, path, .reader); + defer reader.close(&status) catch {}; + try testing.expectError(error.InvalidBound, reader.write(&status, "")); + try testing.expectError(error.UnsupportedMode, reader.resize(&status, 0)); + try testing.expectEqual(@as(usize, 2), try reader.read(&status, bytes[0..3])); + try testing.expectEqualStrings("xy", bytes[0..2]); + try reader.seek(limit); + try testing.expectEqual(@as(usize, 0), try reader.read(&status, bytes[0..1])); +} +test "reject nonregular paths, invalid text and missing readers; symlinks are not confinement" { + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + const fifo = try testPath(tmp.dir, "fifo"); + defer testing.allocator.free(fifo); + try testing.expectEqual(@as(c_int, 0), c.mkfifo(fifo.ptr, 0o600)); + const directory = try testPath(tmp.dir, "."); + defer testing.allocator.free(directory); + const missing = try testPath(tmp.dir, "missing"); + defer testing.allocator.free(missing); + var status: Status = .{}; + for (std.enums.values(Mode)) |mode| { + try testing.expectError(error.UnsupportedFileType, open(&status, fifo, mode)); + try testing.expectError(error.UnsupportedFileType, open(&status, directory, mode)); + try testing.expectError(error.UnsupportedFileType, open(&status, "/dev/null", mode)); + } + try testing.expectError(error.HostFailure, open(&status, missing, .reader)); + try testing.expectEqual(@as(c_int, c.UV_ENOENT), status.code); + try testing.expectError(error.InvalidArgument, open(&status, "", .writer)); + try testing.expectError(error.InvalidArgument, open(&status, "x\x00y", .writer)); + try testing.expectError(error.InvalidArgument, open(&status, "\xff", .writer)); + var file = try open(&status, missing, .writer); + defer file.close(&status) catch {}; + _ = try file.write(&status, "keep"); + const link = try testPath(tmp.dir, "link"); + defer testing.allocator.free(link); + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_symlink(null, &req, missing.ptr, link.ptr, 0, null); + c.uv_fs_req_cleanup(&req); + try testing.expectEqual(@as(c_int, 0), rc); + var reader = try open(&status, link, .reader); + defer reader.close(&status) catch {}; + var bytes: [4]u8 = undefined; + try testing.expectEqual(@as(usize, 4), try reader.read(&status, &bytes)); + try testing.expectEqualStrings("keep", &bytes); + try testing.expect(c.fcntl(reader.fd.?, c.F_GETFD) & c.FD_CLOEXEC != 0); +} + +// Concrete ownership bridge. Null is reserved for standard placeholders; it is +// NOT a standard-stream implementation. Real dispatch must check backend kind +// before leasing. No allocator operation follows successful host acquisition. +pub const Registry = @import("io_v2_resources.zig").Registry(?*File); +pub fn acquire(registry: *Registry, owner: u64, status: *Status, path: [:0]const u8, mode: Mode) !@import("io_v2_resources.zig").Token { + return acquireWrapped(registry, owner, status, path, mode, struct { + fn wrap(file: *File) ?*File { + return file; + } + }.wrap); +} +pub fn acquireWrapped(registry: anytype, owner: u64, status: *Status, path: [:0]const u8, mode: Mode, comptime wrap: anytype) !@import("io_v2_resources.zig").Token { + status.* = .{}; + const admission = try registry.beginAcquire(owner); + errdefer registry.abortAcquire(admission) catch unreachable; + const backend = try registry.allocator.create(File); + errdefer registry.allocator.destroy(backend); + backend.* = try open(status, path, mode); + errdefer { + var cleanup: Status = .{}; + backend.close(&cleanup) catch {}; + status.cleanup_code = cleanup.code; + } + return registry.publish(admission, wrap(backend), .{}); +} +// Call only for an acquired close claim, after all leases have settled. Registry +// entry stays present through physical close, even on error. Memory and aliases +// are retired exactly once; a close error is reported, never retried implicitly. +pub fn release(registry: *Registry, token: @import("io_v2_resources.zig").Token, backend: *File, status: *Status) Error!void { + defer registry.finishClose(token) catch unreachable; + defer registry.allocator.destroy(backend); + try backend.close(status); +} + +fn acquisitionFailureProbe(allocator: std.mem.Allocator, path: [:0]const u8) !void { + var registry = try Registry.init(allocator, 91, .{ null, null, null }); + defer registry.deinit(); + var status: Status = .{}; + const token = acquire(&registry, 0, &status, path, .reader) catch |err| { + try testing.expectEqual(@as(usize, 0), registry.admissions.count()); + try testing.expectEqual(@as(usize, 3), registry.resources.count()); + return err; + }; + const claim = (try registry.claimClose(0, token, false)).?; + try release(&registry, token, claim.backend.?, &status); + try testing.expectEqual(@as(usize, 3), registry.resources.count()); +} +test "real descriptor admission, lease, handoff, close and allocator failure" { + const R = @import("io_v2_resources.zig"); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "file", .data = "abc" }); + const path = try testPath(tmp.dir, "file"); + defer testing.allocator.free(path); + try testing.checkAllAllocationFailures(testing.allocator, acquisitionFailureProbe, .{path}); + var registry = try Registry.init(testing.allocator, 92, .{ null, null, null }); + defer registry.deinit(); + var status: Status = .{}; + const token = try acquire(&registry, 0, &status, path, .update); + const lease = try registry.beginLease(0, token, .exclusive, false); + const fd = lease.backend.?.fd.?; + try testing.expectError(error.Busy, registry.reserveChild(0, &.{token})); + try registry.endLease(lease.token); + const transfer = try registry.reserveChild(0, &.{token}); + try testing.expectError(error.StaleHandle, registry.lookup(0, token)); + try testing.expectError(error.OutOfScope, registry.lookup(0, transfer.tokens[0])); + const child_lease = try registry.beginLease(transfer.scope, transfer.tokens[0], .exclusive, false); + var bytes: [3]u8 = undefined; + _ = try child_lease.backend.?.read(&status, &bytes); + try testing.expectEqualStrings("abc", &bytes); + registry.beginCloseScope(transfer.scope); + try testing.expect((try registry.claimClose(transfer.scope, transfer.tokens[0], true)) == null); + try registry.endLease(child_lease.token); + const claim = (try registry.claimClose(transfer.scope, transfer.tokens[0], true)).?; + try release(&registry, transfer.tokens[0], claim.backend.?, &status); + try testing.expectEqual(@as(c_int, -1), c.fcntl(fd, c.F_GETFD)); + try registry.finishScope(transfer.scope); + // Publication exhaustion can occur AFTER successful open. The descriptor + // must be closed and acquisition aborted, not silently left ownerless. + registry.next = std.math.maxInt(u64) - 1; + try testing.expectError(R.Fault.IdExhausted, acquire(&registry, 0, &status, path, .reader)); + try testing.expectEqual(@as(usize, 0), registry.admissions.count()); + try testing.expectEqual(@as(usize, 3), registry.resources.count()); +} + +pub fn errorCategory(code: c_int) []const u8 { + return switch (code) { + c.UV_ENOENT => "not-found", + c.UV_EACCES, c.UV_EPERM => "os-permission-denied", + c.UV_EEXIST => "already-exists", + else => "io-error", + }; +} +pub fn errorText(code: c_int) []const u8 { + return std.mem.span(c.uv_strerror(code)); +} + +test "descriptor kind check rejects a namespace substitution after preflight without truncation" { + const Swap = struct { + from: [:0]const u8, + to: [:0]const u8, + fn run(context: *anyopaque) void { + const self: *@This() = @ptrCast(@alignCast(context)); + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_rename(null, &req, self.from.ptr, self.to.ptr, null); + c.uv_fs_req_cleanup(&req); + std.debug.assert(rc == 0); + } + }; + for ([_]Mode{ .reader, .writer, .appender, .update }) |mode| { + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "file", .data = "keep" }); + const path = try testPath(tmp.dir, "file"); + defer testing.allocator.free(path); + const fifo = try testPath(tmp.dir, "fifo"); + defer testing.allocator.free(fifo); + const saved = try testPath(tmp.dir, "saved"); + defer testing.allocator.free(saved); + try testing.expectEqual(@as(c_int, 0), c.mkfifo(fifo.ptr, 0o600)); + var req: c.uv_fs_t = undefined; + const linked = c.uv_fs_link(null, &req, path.ptr, saved.ptr, null); + c.uv_fs_req_cleanup(&req); + try testing.expectEqual(@as(c_int, 0), linked); + // Keep a reader for writer modes, but no writer: opening the reader case + // must use NONBLOCK rather than wait for a nonexistent FIFO peer. + const anchor = c.uv_fs_open(null, &req, fifo.ptr, c.O_RDONLY | c.O_NONBLOCK | c.O_CLOEXEC, 0, null); + c.uv_fs_req_cleanup(&req); + try testing.expect(anchor >= 0); + defer { + _ = c.uv_fs_close(null, &req, anchor, null); + c.uv_fs_req_cleanup(&req); + } + var swap: Swap = .{ .from = fifo, .to = path }; + var status: Status = .{}; + try testing.expectError(error.UnsupportedFileType, openAfterCheck(&status, path, mode, .{ .context = &swap, .call = Swap.run })); + var original = try open(&status, saved, .reader); + defer original.close(&status) catch {}; + var bytes: [4]u8 = undefined; + try testing.expectEqual(@as(usize, 4), try original.read(&status, &bytes)); + try testing.expectEqualStrings("keep", &bytes); + } +} diff --git a/ext/zig/src/io_v2_native.zig b/ext/zig/src/io_v2_native.zig @@ -0,0 +1,81 @@ +//! Native v2 entry boundary. Never probes by executing or retries another driver. +const std = @import("std"); +const Arena = @import("arena.zig").Arena; +const P = @import("io_v2_protocol.zig"); +const Core = @import("io_v2_core.zig"); +const Host = @import("io_v2_file_host.zig").Host; +const C = @import("codecs.zig"); +const Identities = struct { + next: std.atomic.Value(u64) = .init(1), + fn issue(self: *@This()) !u64 { + var value = self.next.load(.monotonic); + while (true) { + if (value == std.math.maxInt(u64)) return error.RunIdentityExhausted; + if (self.next.cmpxchgWeak(value, value + 1, .monotonic, .monotonic)) |changed| value = changed else return value; + } + } +}; +var identities: Identities = .{}; +pub const Envelope = struct { version: u64, action: u32 }; +pub fn envelope(arena: *Arena, program: u32, fuel: u64) !Envelope { + const base = arena.roots.items.len; + defer arena.roots.shrinkRetainingCapacity(base); + try arena.roots.append(arena.allocator, program); + const r = P.Reader{ .arena = arena, .fuel = fuel }; + const outer = try r.pair(program) orelse return error.InvalidIOEnvelope; + var rest = outer.first; + for ("tricuIO") |ch| { + const cell = try r.pair(rest) orelse return error.InvalidIOEnvelope; + if (try r.nat(cell.first, 7) != @as(?u64, ch)) return error.InvalidIOEnvelope; + rest = cell.second; + } + if (try r.node(rest) != .leaf) return error.InvalidIOEnvelope; + const inner = try r.pair(outer.second) orelse return error.InvalidIOEnvelope; + return .{ .version = try r.nat(inner.first, 16) orelse return error.InvalidIOEnvelope, .action = inner.second }; +} +pub fn run(arena: *Arena, program: u32, fuel: u64) !u32 { + const decoded = try envelope(arena, program, fuel); + if (decoded.version != 2) return error.UnsupportedIOVersion; + var host: Host = .{}; + const outcome = try Core.runWithHost(Host, &host, arena, decoded.action, try identities.issue(), fuel); + return switch (outcome.kind) { + .completed => outcome.value, + .failed => error.IOV2Failed, + .cancelled => error.IOV2Cancelled, + }; +} +test "native run identities never repeat or wrap" { + var ids: Identities = .{}; + try std.testing.expectEqual(@as(u64, 1), try ids.issue()); + try std.testing.expectEqual(@as(u64, 2), try ids.issue()); + ids.next.store(std.math.maxInt(u64) - 1, .monotonic); + _ = try ids.issue(); + try std.testing.expectError(error.RunIdentityExhausted, ids.issue()); + try std.testing.expectError(error.RunIdentityExhausted, ids.issue()); +} +test "retired versions rejected without demanding action" { + var a = Arena.init(std.testing.allocator); + defer a.deinit(); + const bad = try a.allocApp(try a.alloc(.leaf), try a.alloc(.leaf)); + for ([_]u64{ 0, 1, 3, 65535 }) |version| { + const program = try a.alloc(.{ .fork = .{ .left = try C.ofString(&a, "tricuIO"), .right = try a.alloc(.{ .fork = .{ .left = try C.ofNumber(&a, version), .right = bad } }) } }); + try std.testing.expectError(error.UnsupportedIOVersion, run(&a, program, 0)); + } +} + +test "successive native runs produce distinct scope identities" { + var arena = Arena.init(std.testing.allocator); + defer arena.deinit(); + const leaf = try arena.alloc(.leaf); + const action = try arena.alloc(.{ .fork = .{ .left = try C.ofNumber(&arena, 4), .right = leaf } }); + const program = try arena.alloc(.{ .fork = .{ .left = try C.ofString(&arena, "tricuIO"), .right = try arena.alloc(.{ .fork = .{ .left = try C.ofNumber(&arena, 2), .right = action } }) } }); + const first = try run(&arena, program, 10000); + try arena.roots.append(arena.allocator, first); + const second = try run(&arena, program, 10000); + const reader = P.Reader{ .arena = &arena, .fuel = 10000 }; + const first_fields = (try reader.pair(first)).?; + const second_fields = (try reader.pair(second)).?; + const first_id = try reader.nat((try reader.pair(first_fields.second)).?.first, 64); + const second_id = try reader.nat((try reader.pair(second_fields.second)).?.first, 64); + try std.testing.expect(first_id != null and second_id != null and first_id != second_id); +} diff --git a/ext/zig/src/io_v2_paths.zig b/ext/zig/src/io_v2_paths.zig @@ -0,0 +1,120 @@ +//! Private synchronous path operations. Observations do not pin subsequent path +//! operations. OS process authority, no grants, sandbox or implicit rollback. +const std = @import("std"); +const F = @import("io_v2_files.zig"); +const c = @cImport({ + @cUndef("_FORTIFY_SOURCE"); + @cDefine("_FORTIFY_SOURCE", "0"); + @cInclude("uv.h"); + @cInclude("sys/stat.h"); + @cInclude("fcntl.h"); +}); +pub const Error = F.Error || error{NotDirectory}; +pub const Info = union(enum) { regular: u64, directory, symlink, other }; +pub const Mutation = enum { create_directory, remove_directory, remove_file, rename }; +fn valid(path: [:0]const u8) Error!void { + if (path.len == 0 or std.mem.indexOfScalar(u8, path, 0) != null or !std.unicode.utf8ValidateSlice(path)) return error.InvalidArgument; +} +fn checked(status: *F.Status, code: c_int) Error!void { + if (code < 0) { + status.code = code; + return error.HostFailure; + } +} +pub fn stat(status: *F.Status, path: [:0]const u8, follow: bool) Error!Info { + status.* = .{}; + try valid(path); + var req: c.uv_fs_t = undefined; + const rc = if (follow) c.uv_fs_stat(null, &req, path.ptr, null) else c.uv_fs_lstat(null, &req, path.ptr, null); + defer c.uv_fs_req_cleanup(&req); + try checked(status, rc); + return switch (req.statbuf.st_mode & c.S_IFMT) { + c.S_IFREG => if (req.statbuf.st_size <= F.limit) .{ .regular = req.statbuf.st_size } else error.InvalidBound, + c.S_IFDIR => .directory, + c.S_IFLNK => .symlink, + else => .other, + }; +} +pub fn mutate(status: *F.Status, op: Mutation, path: [:0]const u8, destination: ?[:0]const u8) Error!void { + status.* = .{}; + try valid(path); + if (op == .rename) try valid(destination orelse return error.InvalidArgument); + var req: c.uv_fs_t = undefined; + const rc = switch (op) { + .create_directory => c.uv_fs_mkdir(null, &req, path.ptr, 0o777, null), + .remove_directory => c.uv_fs_rmdir(null, &req, path.ptr, null), + .remove_file => c.uv_fs_unlink(null, &req, path.ptr, null), + .rename => c.uv_fs_rename(null, &req, path.ptr, destination.?.ptr, null), + }; + defer c.uv_fs_req_cleanup(&req); + try checked(status, rc); +} +pub fn syncDirectory(status: *F.Status, path: [:0]const u8) Error!void { + status.* = .{}; + try valid(path); + var req: c.uv_fs_t = undefined; + // NONBLOCK plus O_DIRECTORY avoids opening a FIFO while waiting for peers. + const fd = c.uv_fs_open(null, &req, path.ptr, c.O_RDONLY | c.O_NONBLOCK | c.O_DIRECTORY | c.O_CLOEXEC | c.O_NOCTTY, 0, null); + c.uv_fs_req_cleanup(&req); + if (fd == c.UV_ENOTDIR) return error.NotDirectory; + try checked(status, fd); + // Close exactly once on every path; retain a primary sync/fstat error. + const rc = c.uv_fs_fstat(null, &req, fd, null); + const is_directory = rc >= 0 and req.statbuf.st_mode & c.S_IFMT == c.S_IFDIR; + c.uv_fs_req_cleanup(&req); + const synced = if (is_directory) blk: { + const result = c.uv_fs_fsync(null, &req, fd, null); + c.uv_fs_req_cleanup(&req); + break :blk result; + } else 0; + const closed = c.uv_fs_close(null, &req, fd, null); + c.uv_fs_req_cleanup(&req); + if (closed < 0) status.cleanup_code = closed; + try checked(status, rc); + if (!is_directory) return error.NotDirectory; + try checked(status, synced); + try checked(status, closed); +} + +test "path observations, explicit symlink following, rename/unlink and directory durability" { + const t = std.testing; + var tmp = t.tmpDir(.{}); + defer tmp.cleanup(); + const base = try tmp.dir.realPathFileAlloc(t.io, ".", t.allocator); + defer t.allocator.free(base); + const dir = try std.fmt.allocPrintSentinel(t.allocator, "{s}/dir", .{base}, 0); + defer t.allocator.free(dir); + const source = try std.fmt.allocPrintSentinel(t.allocator, "{s}/file", .{base}, 0); + defer t.allocator.free(source); + const target = try std.fmt.allocPrintSentinel(t.allocator, "{s}/dir/file", .{base}, 0); + defer t.allocator.free(target); + const link = try std.fmt.allocPrintSentinel(t.allocator, "{s}/link", .{base}, 0); + defer t.allocator.free(link); + var status: F.Status = .{}; + try mutate(&status, .create_directory, dir, null); + try t.expect(try stat(&status, dir, true) == .directory); + try t.expectError(error.HostFailure, mutate(&status, .create_directory, dir, null)); + try t.expectEqualStrings("already-exists", F.errorCategory(status.code)); + var file = try F.open(&status, source, .writer); + defer file.close(&status) catch {}; + _ = try file.write(&status, "abc"); + var req: c.uv_fs_t = undefined; + const linked = c.uv_fs_symlink(null, &req, source.ptr, link.ptr, 0, null); + c.uv_fs_req_cleanup(&req); + try t.expectEqual(@as(c_int, 0), linked); + try t.expect(try stat(&status, link, false) == .symlink); + try t.expectEqual(@as(u64, 3), (try stat(&status, link, true)).regular); + try mutate(&status, .rename, source, target); + try t.expectError(error.HostFailure, stat(&status, source, true)); + try t.expectEqualStrings("not-found", F.errorCategory(status.code)); + try t.expectEqual(@as(u64, 3), (try stat(&status, target, true)).regular); + try syncDirectory(&status, dir); + try t.expectError(error.NotDirectory, syncDirectory(&status, target)); + try t.expectError(error.HostFailure, mutate(&status, .remove_directory, dir, null)); + try mutate(&status, .remove_file, target, null); + try mutate(&status, .remove_directory, dir, null); + try mutate(&status, .remove_file, link, null); + try t.expectError(error.InvalidArgument, mutate(&status, .rename, source, null)); + try t.expectError(error.InvalidArgument, stat(&status, "x\x00y", true)); + try t.expectError(error.InvalidArgument, syncDirectory(&status, "")); +} diff --git a/ext/zig/src/io_v2_protocol.zig b/ext/zig/src/io_v2_protocol.zig @@ -0,0 +1,253 @@ +//! IO v2 wire decoding only. No v1 dispatch, facilities, scheduler or host effects. +//! Returned node IDs are borrowed: the caller must retain the action's root while +//! using them. Header decoding temporarily roots its input across reducer GC. +const std = @import("std"); +const Arena = @import("arena.zig").Arena; +const Node = @import("tree.zig").Node; +const reduce = @import("reduce.zig"); + +pub const Core = enum(u4) { pure, bind, fail, scope, current_scope, spawn, join, discard, cancel, cancelled }; +pub const Name = struct { + bytes: [64]u8 = undefined, + len: u7 = 0, + pub fn slice(self: *const Name) []const u8 { + return self.bytes[0..self.len]; + } +}; +pub const Action = union(enum) { + core: struct { code: Core, payload: u32 }, + request: struct { group: Name, revision: u16, operation: Name, arguments: u32 }, +}; +pub const DecodeError = reduce.ReduceError || error{ + InvalidComputationShape, + UnknownComputationConstructor, + InvalidFacilityHeader, +}; + +pub const Reader = struct { + arena: *Arena, + // Per-demand reducer fuel, not an aggregate request budget or deadline. + fuel: u64, + pub fn node(self: Reader, id: u32) reduce.ReduceError!Node { + // Copy, never hold an arena pointer across another reduction/allocation. + return self.arena.get(try reduce.reduce(id, self.arena, self.fuel)).*; + } + pub const Pair = struct { first: u32, second: u32 }; + pub fn pair(self: Reader, id: u32) reduce.ReduceError!?Pair { + return switch (try self.node(id)) { + .fork => |p| .{ .first = p.left, .second = p.right }, + else => null, + }; + } + pub fn nat(self: Reader, id: u32, bits: u7) reduce.ReduceError!?u64 { + std.debug.assert(bits <= 64); + var rest = id; + var value: u64 = 0; + var count: u7 = 0; + var last: u1 = 0; + while (true) { + const n = try self.node(rest); + if (n == .leaf) return if (count == 0 or last == 1) value else null; + if (count == bits or n != .fork) return null; + last = switch (try self.node(n.fork.left)) { + .leaf => 0, + .stem => |s| if (try self.node(s.child) == .leaf) 1 else return null, + else => return null, + }; + value |= @as(u64, last) << @as(u6, @intCast(count)); + count += 1; + rest = n.fork.right; + } + } + fn name(self: Reader, id: u32) reduce.ReduceError!?Name { + var out: Name = .{}; + var rest = id; + while (true) { + const n = try self.node(rest); + if (n == .leaf) return if (out.len == 0) null else out; + // At the limit observe the spine only, never an excess head/tail. + if (out.len == 64 or n != .fork) return null; + const c = try self.nat(n.fork.left, 7) orelse return null; + const letter = c >= 'a' and c <= 'z'; + if (!letter and (out.len == 0 or !((c >= '0' and c <= '9') or c == '-'))) return null; + out.bytes[out.len] = @intCast(c); + out.len += 1; + rest = n.fork.right; + } + } +}; + +pub fn decode(arena: *Arena, action: u32, fuel: u64) DecodeError!Action { + const base = arena.roots.items.len; + defer arena.roots.shrinkRetainingCapacity(base); + try arena.roots.append(arena.allocator, action); + const r: Reader = .{ .arena = arena, .fuel = fuel }; + const outer = try r.pair(action) orelse return error.InvalidComputationShape; + const tag = try r.nat(outer.first, 8) orelse return error.UnknownComputationConstructor; + if (tag <= 9) return .{ .core = .{ .code = @enumFromInt(tag), .payload = outer.second } }; + if (tag != 10) return error.UnknownComputationConstructor; + const group = try r.pair(outer.second) orelse return error.InvalidFacilityHeader; + const revision = try r.pair(group.second) orelse return error.InvalidFacilityHeader; + const operation = try r.pair(revision.second) orelse return error.InvalidFacilityHeader; + const g = try r.name(group.first) orelse return error.InvalidFacilityHeader; + const v = try r.nat(revision.first, 16) orelse return error.InvalidFacilityHeader; + if (v == 0) return error.InvalidFacilityHeader; + const op = try r.name(operation.first) orelse return error.InvalidFacilityHeader; + return .{ .request = .{ .group = g, .revision = @intCast(v), .operation = op, .arguments = operation.second } }; +} + +const testing = std.testing; +const codecs = @import("codecs.zig"); +fn pair(a: *Arena, left: u32, right: u32) !u32 { + return a.alloc(.{ .fork = .{ .left = left, .right = right } }); +} +fn request(a: *Arena, group: u32, revision: u32, operation: u32, arguments: u32) !u32 { + return pair(a, try codecs.ofNumber(a, 10), try pair(a, group, try pair(a, revision, try pair(a, operation, arguments)))); +} +fn poison(a: *Arena) !u32 { + const leaf = try a.alloc(.leaf); + const p = try a.alloc(.leaf); + a.get(p).* = .{ .app = .{ .func = p, .arg = leaf } }; + return p; +} + +test "core catalogue and unknown tags never demand payloads" { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + for (0..10) |tag| { + const act = try pair(&a, try codecs.ofNumber(&a, tag), p); + const decoded = try decode(&a, act, 0); + try testing.expectEqual(tag, @intFromEnum(decoded.core.code)); + try testing.expectEqual(p, decoded.core.payload); + } + for ([_]u64{ 11, 20, 255, 256 }) |tag| { + try testing.expectError(error.UnknownComputationConstructor, decode(&a, try pair(&a, try codecs.ofNumber(&a, tag), p), 0)); + } + try testing.expectError(error.InvalidComputationShape, decode(&a, try a.alloc(.leaf), 0)); + try testing.expectEqual(@as(usize, 0), a.roots.items.len); + try testing.expect(a.get(p).* == .app); +} + +test "valid future headers retain arguments without demanding or resolving them" { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + const act = try request(&a, try codecs.ofString(&a, "future-9"), try codecs.ofNumber(&a, 65535), try codecs.ofString(&a, "unknown"), p); + const decoded = (try decode(&a, act, 0)).request; + try testing.expectEqualStrings("future-9", decoded.group.slice()); + try testing.expectEqualStrings("unknown", decoded.operation.slice()); + try testing.expectEqual(@as(u16, 65535), decoded.revision); + try testing.expectEqual(p, decoded.arguments); + try testing.expect(a.get(p).* == .app); +} + +test "strict canonical header numerals and identifier grammar" { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + const leaf = try a.alloc(.leaf); + const zero = try pair(&a, leaf, leaf); // forbidden redundant zero bit + const good = try codecs.ofString(&a, "file"); + const one = try codecs.ofNumber(&a, 1); + try testing.expectError(error.UnknownComputationConstructor, decode(&a, try pair(&a, zero, p), 0)); + for ([_]u32{ leaf, zero, try codecs.ofNumber(&a, 65536) }) |revision| { + try testing.expectError(error.InvalidFacilityHeader, decode(&a, try request(&a, good, revision, good, p), 0)); + } + for ([_][]const u8{ "", "-file", "1file", "File", "a_b", "a b", "a\x7f", "a" ** 65 }) |name| { + const invalid = try codecs.ofString(&a, name); + try testing.expectError(error.InvalidFacilityHeader, decode(&a, try request(&a, invalid, one, good, p), 0)); + try testing.expectError(error.InvalidFacilityHeader, decode(&a, try request(&a, good, one, invalid, p), 0)); + } + const longest = try codecs.ofString(&a, "a" ** 64); + try testing.expectEqual(@as(u7, 64), (try decode(&a, try request(&a, longest, one, longest, p), 0)).request.group.len); + // Character 97 with a redundant high zero must not be accepted as ASCII a. + var character = zero; + for ([_]u1{ 1, 1, 0, 0, 0, 0, 1 }) |bit| { + character = try pair(&a, if (bit == 0) leaf else try a.alloc(.{ .stem = .{ .child = leaf } }), character); + } + const bad_name = try pair(&a, character, leaf); + try testing.expectError(error.InvalidFacilityHeader, decode(&a, try request(&a, bad_name, one, good, p), 0)); +} + +test "oversize names do not demand excess head or tail and evaluation errors stay distinct" { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + var name = try pair(&a, p, p); + const ch = try codecs.ofNumber(&a, 'a'); + for (0..64) |_| name = try pair(&a, ch, name); + const one = try codecs.ofNumber(&a, 1); + const good = try codecs.ofString(&a, "read"); + try testing.expectError(error.InvalidFacilityHeader, decode(&a, try request(&a, name, one, good, p), 0)); + try testing.expectError(error.FuelExhausted, decode(&a, try pair(&a, p, p), 0)); + try testing.expectEqual(@as(usize, 0), a.roots.items.len); +} + +fn allocationProbe(allocator: std.mem.Allocator) !void { + var a = Arena.init(allocator); + defer a.deinit(); + const arg = try poison(&a); + const action = try request(&a, try codecs.ofString(&a, "file"), try codecs.ofNumber(&a, 1), try codecs.ofString(&a, "open-reader"), arg); + // An unrelated retained root must survive decoding and all failure unwinds. + const keep = try a.alloc(.leaf); + try a.roots.append(allocator, keep); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + const decoded = decode(&a, action, 0) catch |err| { + try testing.expectEqual(@as(usize, 1), a.roots.items.len); + return err; + }; + try testing.expect(a.collections > 0); + try testing.expectEqualStrings("open-reader", decoded.request.operation.slice()); + try testing.expect(a.get(decoded.request.arguments).* == .app); + try testing.expectEqualSlices(u32, &.{keep}, a.roots.items); + // Decoder roots do not turn into a hidden completion/history archive. + try a.collect(&.{}); + try testing.expectEqual(@as(usize, 1), a.last_live); +} +test "GC roots survive header demands and every allocator failure unwinds" { + try allocationProbe(testing.allocator); + try testing.checkAllAllocationFailures(testing.allocator, allocationProbe, .{}); +} + +test "malformed facility framing is terminal without examining unrelated arguments" { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + const leaf = try a.alloc(.leaf); + const ten = try codecs.ofNumber(&a, 10); + const group = try codecs.ofString(&a, "file"); + for ([_]u32{ leaf, try pair(&a, group, leaf), try pair(&a, group, try pair(&a, leaf, leaf)) }) |body| { + try testing.expectError(error.InvalidFacilityHeader, decode(&a, try pair(&a, ten, body), 0)); + } + // A bad header must not be confused with a valid unsupported operation. + const operation = try codecs.ofString(&a, "not-implemented"); + const one = try codecs.ofNumber(&a, 1); + const act = try request(&a, group, one, operation, p); + try testing.expectEqualStrings("not-implemented", (try decode(&a, act, 0)).request.operation.slice()); + try testing.expect(a.get(p).* == .app); +} + +fn delayed(a: *Arena, value: u32, ignored: u32) !u32 { + return a.allocApp(try pair(a, try a.alloc(.leaf), value), ignored); +} +test "computed headers reduce under the caller budget and preserve borrowed roots through GC" { + var a = Arena.init(testing.allocator); + defer a.deinit(); + const p = try poison(&a); + const group = try delayed(&a, try codecs.ofString(&a, "file"), p); + const revision = try delayed(&a, try codecs.ofNumber(&a, 1), p); + const operation = try delayed(&a, try codecs.ofString(&a, "open-reader"), p); + const act = try delayed(&a, try request(&a, group, revision, operation, p), p); + try testing.expectError(error.FuelExhausted, decode(&a, act, 0)); + a.gc_enabled = true; + try a.setGcPolicy(1, 1); + const decoded = (try decode(&a, act, 1)).request; + try testing.expectEqualStrings("file", decoded.group.slice()); + try testing.expectEqualStrings("open-reader", decoded.operation.slice()); + try testing.expectEqual(p, decoded.arguments); + try testing.expect(a.get(p).* == .app); + try testing.expect(a.collections > 0); + try testing.expectEqual(@as(usize, 0), a.roots.items.len); +} diff --git a/ext/zig/src/io_v2_resources.zig b/ext/zig/src/io_v2_resources.zig @@ -0,0 +1,375 @@ +//! Single-owner-event-loop registry. This module performs no OS operations and +//! never invokes user callbacks. Scheduler publication must reserve its own memory +//! before reserveChild; no fallible work may separate that commit from admission. +//! Backend values are owned by the embedding. deinit frees registry memory only; +//! the embedding must drain admitted opens/leases/releases and settle scopes first. +const std = @import("std"); +pub const Scope = u64; +pub const Token = struct { run: u64, key: u64 }; +pub const Access = enum { exclusive, read, write }; +pub const Flags = packed struct { borrowed: bool = false, internal: bool = false, duplex: bool = false }; +pub const Fault = error{ ClosedScope, StaleHandle, OutOfScope, DuplicateHandle, NotOwner, BorrowedResource, InternalResource, ClosedHandle, Busy, InvalidAdmission, InvalidLease, InvalidClose, IdExhausted, TransferLimit, OutOfMemory }; +pub const transfer_limit = 256; + +pub fn Registry(comptime Backend: type) type { + return struct { + const Self = @This(); + const ScopeState = struct { parent: ?Scope, admitting: bool = true, pending: usize = 0, operations: usize = 0, children: usize = 0 }; + pub const Resource = struct { owner: Scope, backend: Backend, flags: Flags, busy: u2 = 0, closing: bool = false, releasing: bool = false }; + const LeaseState = struct { scope: Scope, resource: u64, mask: u2 }; + pub const Lease = struct { token: Token, backend: Backend }; + pub const Transfer = struct { scope: Scope, tokens: [transfer_limit]Token = undefined, len: usize }; + allocator: std.mem.Allocator, + identity: u64, + next: u64 = 4, // root=0; standard references are reserved at 1..3 + scopes: std.AutoHashMapUnmanaged(Scope, ScopeState) = .empty, + resources: std.AutoHashMapUnmanaged(u64, Resource) = .empty, + admissions: std.AutoHashMapUnmanaged(u64, Scope) = .empty, + leases: std.AutoHashMapUnmanaged(u64, LeaseState) = .empty, + + pub fn init(allocator: std.mem.Allocator, identity: u64, standard: [3]Backend) !Self { + var self: Self = .{ .allocator = allocator, .identity = identity }; + errdefer self.deinit(); + try self.scopes.put(allocator, 0, .{ .parent = null }); + for (standard, 1..) |backend, standard_key| try self.resources.put(allocator, standard_key, .{ .owner = 0, .backend = backend, .flags = .{ .borrowed = true } }); + return self; + } + pub fn deinit(self: *Self) void { + self.leases.deinit(self.allocator); + self.admissions.deinit(self.allocator); + self.resources.deinit(self.allocator); + self.scopes.deinit(self.allocator); + } + fn room(self: *const Self, count: u64) Fault!void { + _ = std.math.add(u64, self.next, count) catch return error.IdExhausted; + } + pub fn within(self: *const Self, current: Scope, owner: Scope) bool { + var cursor = current; + while (self.scopes.get(cursor)) |scope| { + if (cursor == owner) return true; + cursor = scope.parent orelse return false; + } + return false; + } + fn openScope(self: *Self, scope: Scope) Fault!*ScopeState { + const state = self.scopes.getPtr(scope) orelse return error.ClosedScope; + if (!state.admitting) return error.ClosedScope; + return state; + } + fn key(self: *const Self, token: Token) Fault!u64 { + if (token.run != self.identity) return error.StaleHandle; + return token.key; + } + pub fn lookup(self: *const Self, current: Scope, token: Token) Fault!Resource { + const resource = self.resources.get(try self.key(token)) orelse return error.StaleHandle; + if (!self.within(current, resource.owner)) return error.OutOfScope; + return resource; + } + pub fn beginCloseScope(self: *Self, scope: Scope) void { + if (self.scopes.getPtr(scope)) |state| state.admitting = false; + } + // Admission is counted through host acquisition and publication/abort. + // Allocate all map capacity BEFORE the host can create a resource. + pub fn beginAcquire(self: *Self, owner: Scope) Fault!Token { + _ = try self.openScope(owner); + try self.room(1); + try self.admissions.ensureUnusedCapacity(self.allocator, 1); + try self.resources.ensureTotalCapacity(self.allocator, std.math.add(u32, self.resources.count(), std.math.add(u32, self.admissions.count(), 1) catch return error.OutOfMemory) catch return error.OutOfMemory); + const id = self.next; + self.next += 1; + self.admissions.putAssumeCapacityNoClobber(id, owner); + self.scopes.getPtr(owner).?.pending += 1; + return .{ .run = self.identity, .key = id }; + } + pub fn abortAcquire(self: *Self, admission: Token) Fault!void { + const removed = self.admissions.fetchRemove(try self.key(admission)) orelse return error.InvalidAdmission; + self.scopes.getPtr(removed.value).?.pending -= 1; + } + // No allocation. A counter-exhaustion failure leaves admission live; + // the embedding must release its raw backend and abort that admission. + // IDs reflect publication order, not asynchronous open-start order. + pub fn publish(self: *Self, admission: Token, backend: Backend, flags: Flags) Fault!Token { + const id = try self.key(admission); + const owner = self.admissions.get(id) orelse return error.InvalidAdmission; + try self.room(1); + const key_value = self.next; + self.next += 1; + self.resources.putAssumeCapacityNoClobber(key_value, .{ .owner = owner, .backend = backend, .flags = flags }); + _ = self.admissions.remove(id); + self.scopes.getPtr(owner).?.pending -= 1; + return .{ .run = self.identity, .key = key_value }; + } + pub fn reserveChild(self: *Self, parent: Scope, tokens: []const Token) Fault!Transfer { + _ = try self.openScope(parent); + if (tokens.len > transfer_limit) return error.TransferLimit; + var sorted: [transfer_limit]u64 = undefined; + // Validate every token before any mutation. No backend copies escape + // on failure and no old token is invalidated on partial validation. + for (tokens, 0..) |token, i| { + for (tokens[0..i]) |previous| if (std.meta.eql(previous, token)) return error.DuplicateHandle; + const resource = try self.lookup(parent, token); + if (resource.owner != parent) return error.NotOwner; + if (resource.flags.borrowed) return error.BorrowedResource; + if (resource.flags.internal) return error.InternalResource; + if (resource.closing) return error.ClosedHandle; + if (resource.busy != 0) return error.Busy; + sorted[i] = token.key; + } + std.mem.sort(u64, sorted[0..tokens.len], {}, std.sort.asc(u64)); + try self.room(1 + tokens.len); + try self.scopes.ensureUnusedCapacity(self.allocator, 1); + var result: Transfer = .{ .scope = self.next, .len = tokens.len }; + self.next += 1 + tokens.len; + self.scopes.putAssumeCapacityNoClobber(result.scope, .{ .parent = parent }); + self.scopes.getPtr(parent).?.children += 1; + // Remove/insert each entry without allocation; capacity does not grow. + // Sorted original IDs preserve acquisition order, not argument order. + for (sorted[0..tokens.len], 0..) |old, index| { + var resource = self.resources.fetchRemove(old).?.value; + resource.owner = result.scope; + const fresh = result.scope + 1 + index; + self.resources.putAssumeCapacityNoClobber(fresh, resource); + for (tokens, 0..) |token, i| if (token.key == old) { + result.tokens[i] = .{ .run = self.identity, .key = fresh }; + }; + } + return result; + } + pub fn beginLease(self: *Self, current: Scope, token: Token, access: Access, internal: bool) Fault!Lease { + const resource = try self.lookup(current, token); + if (resource.closing) return error.ClosedHandle; + if (resource.flags.internal and !internal) return error.InternalResource; + const mask: u2 = if (!resource.flags.duplex or access == .exclusive) 3 else if (access == .read) 1 else 2; + if (resource.busy & mask != 0) return error.Busy; + try self.room(1); + try self.leases.ensureUnusedCapacity(self.allocator, 1); + const id = self.next; + self.next += 1; + self.leases.putAssumeCapacityNoClobber(id, .{ .scope = current, .resource = token.key, .mask = mask }); + self.resources.getPtr(token.key).?.busy |= mask; + self.scopes.getPtr(current).?.operations += 1; + return .{ .token = .{ .run = self.identity, .key = id }, .backend = resource.backend }; + } + pub fn endLease(self: *Self, lease: Token) Fault!void { + const entry = self.leases.fetchRemove(try self.key(lease)) orelse return error.InvalidLease; + self.resources.getPtr(entry.value.resource).?.busy &= ~entry.value.mask; + self.scopes.getPtr(entry.value.scope).?.operations -= 1; + } + // Mark closing before waiting, blocking handoff and new leases. null + // means a lease or another close is in flight, not permission to retry IO. + pub fn claimClose(self: *Self, current: Scope, token: Token, settling: bool) Fault!?Resource { + const resource = try self.lookup(current, token); + if (!settling) { + if (resource.flags.internal) return error.InternalResource; + if (resource.flags.borrowed) return error.BorrowedResource; + } + const entry = self.resources.getPtr(token.key).?; + entry.closing = true; + if (entry.busy != 0 or entry.releasing) return null; + entry.releasing = true; + return entry.*; + } + // Exactly once, including after a host release error. Keep the entry until + // the callback settles so finishScope cannot outrun asynchronous close. + pub fn finishClose(self: *Self, token: Token) Fault!void { + const id = try self.key(token); + const entry = self.resources.get(id) orelse return error.StaleHandle; + if (!entry.releasing) return error.InvalidClose; + _ = self.resources.remove(id); + } + pub fn newestOwned(self: *const Self, scope: Scope) ?Token { + var newest: ?u64 = null; + var iterator = self.resources.iterator(); + while (iterator.next()) |entry| { + if (entry.value_ptr.owner == scope and (newest == null or entry.key_ptr.* > newest.?)) newest = entry.key_ptr.*; + } + return if (newest) |id| .{ .run = self.identity, .key = id } else null; + } + // Scheduler must first settle descendants. No reachability-based lifetime + // extension, recursive implicit cancellation, or silent orphan removal here. + pub fn readyForRelease(self: *const Self, scope: Scope) bool { + const state = self.scopes.get(scope) orelse return false; + return !state.admitting and state.children == 0 and state.pending == 0 and state.operations == 0; + } + pub fn finishScope(self: *Self, scope: Scope) Fault!void { + const state = self.scopes.get(scope) orelse return error.ClosedScope; + if (state.admitting or state.children != 0 or state.pending != 0 or state.operations != 0 or self.newestOwned(scope) != null) return error.Busy; + _ = self.scopes.remove(scope); + if (state.parent) |parent| self.scopes.getPtr(parent).?.children -= 1; + } + }; +} + +const testing = std.testing; +const TestRegistry = Registry(u32); +fn create(r: *TestRegistry, scope: Scope, value: u32, flags: Flags) !Token { + return r.publish(try r.beginAcquire(scope), value, flags); +} +fn closeScope(r: *TestRegistry, scope: Scope) !void { + r.beginCloseScope(scope); + while (r.newestOwned(scope)) |token| { + _ = (try r.claimClose(scope, token, true)) orelse return error.Busy; + try r.finishClose(token); + } + try r.finishScope(scope); +} +test "closing admission preserves existing descendant borrowing and admitted opens" { + var r = try TestRegistry.init(testing.allocator, 7, .{ 10, 11, 12 }); + defer r.deinit(); + const child = (try r.reserveChild(0, &.{})).scope; + const admission = try r.beginAcquire(0); + r.beginCloseScope(0); + try testing.expectError(error.ClosedScope, r.beginAcquire(0)); + try testing.expectError(error.ClosedScope, r.reserveChild(0, &.{})); + try testing.expectError(error.Busy, r.finishScope(0)); + const token = try r.publish(admission, 99, .{}); + const lease = try r.beginLease(child, token, .read, false); + try testing.expectEqual(@as(u32, 99), lease.backend); + r.beginCloseScope(child); + try testing.expectError(error.Busy, r.finishScope(child)); + try r.endLease(lease.token); + try closeScope(&r, child); + try closeScope(&r, 0); + try testing.expectEqual(@as(u32, 0), r.scopes.count()); + try testing.expectEqual(@as(u32, 0), r.resources.count()); +} +test "handoff validates atomically, rekeys aliases, and preserves acquisition order" { + var r = try TestRegistry.init(testing.allocator, 8, .{ 0, 0, 0 }); + defer r.deinit(); + const a = try create(&r, 0, 101, .{}); + const b = try create(&r, 0, 102, .{}); + const next = r.next; + try testing.expectError(error.DuplicateHandle, r.reserveChild(0, &.{ a, a })); + try testing.expectError(error.StaleHandle, r.reserveChild(0, &.{ a, .{ .run = 9, .key = b.key } })); + try testing.expectError(error.BorrowedResource, r.reserveChild(0, &.{ a, .{ .run = 8, .key = 1 } })); + try testing.expectEqual(next, r.next); + const transferred = try r.reserveChild(0, &.{ b, a }); + try testing.expectError(error.StaleHandle, r.lookup(0, a)); + try testing.expectError(error.StaleHandle, r.lookup(transferred.scope, b)); + try testing.expectError(error.OutOfScope, r.lookup(0, transferred.tokens[0])); + try testing.expectEqual(@as(u32, 102), (try r.lookup(transferred.scope, transferred.tokens[0])).backend); + try testing.expectEqual(transferred.tokens[0], r.newestOwned(transferred.scope).?); + const inner = (try r.reserveChild(transferred.scope, &.{})).scope; + try testing.expectError(error.NotOwner, r.reserveChild(inner, transferred.tokens[1..2])); + try closeScope(&r, inner); + try closeScope(&r, transferred.scope); + try closeScope(&r, 0); +} +test "direction leases, close claims and stale completions cannot cross ownership" { + var r = try TestRegistry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer r.deinit(); + const token = try create(&r, 0, 5, .{ .duplex = true }); + const read = try r.beginLease(0, token, .read, false); + const write = try r.beginLease(0, token, .write, false); + try testing.expectError(error.Busy, r.beginLease(0, token, .read, false)); + try testing.expectError(error.Busy, r.reserveChild(0, &.{token})); + try r.endLease(read.token); + const read2 = try r.beginLease(0, token, .read, false); + try testing.expectError(error.InvalidLease, r.endLease(read.token)); + try testing.expectEqual(@as(?TestRegistry.Resource, null), try r.claimClose(0, token, false)); + try testing.expectError(error.ClosedHandle, r.beginLease(0, token, .read, false)); + try testing.expectError(error.ClosedHandle, r.reserveChild(0, &.{token})); + try r.endLease(write.token); + try r.endLease(read2.token); + _ = (try r.claimClose(0, token, false)).?; + try testing.expectEqual(@as(?TestRegistry.Resource, null), try r.claimClose(0, token, false)); + r.beginCloseScope(0); + try testing.expectError(error.Busy, r.finishScope(0)); + try r.finishClose(token); // also required after failed host close + try testing.expectError(error.StaleHandle, r.finishClose(token)); + try closeScope(&r, 0); +} +test "single file cursor, internal resources and borrowed standards stay distinct" { + var r = try TestRegistry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer r.deinit(); + const file = try create(&r, 0, 0, .{}); + const reader = try r.beginLease(0, file, .read, false); + try testing.expectError(error.Busy, r.beginLease(0, file, .write, false)); + try r.endLease(reader.token); + const hidden = try create(&r, 0, 0, .{ .internal = true }); + try testing.expectError(error.InternalResource, r.beginLease(0, hidden, .read, false)); + try testing.expectError(error.InternalResource, r.claimClose(0, hidden, false)); + try testing.expectError(error.InternalResource, r.reserveChild(0, &.{hidden})); + const internal = try r.beginLease(0, hidden, .exclusive, true); + try r.endLease(internal.token); + try testing.expectError(error.BorrowedResource, r.claimClose(0, .{ .run = 1, .key = 2 }, false)); + try closeScope(&r, 0); +} +test "publication order, multiple admitted opens, and explicit abort" { + var r = try TestRegistry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer r.deinit(); + var admissions: [64]Token = undefined; + for (&admissions) |*a| a.* = try r.beginAcquire(0); + r.beginCloseScope(0); + var index: usize = admissions.len; + while (index > 1) { + index -= 1; + const token = try r.publish(admissions[index], @intCast(index), .{}); + try testing.expectEqual(token, r.newestOwned(0).?); + } + try testing.expectError(error.Busy, r.finishScope(0)); + try r.abortAcquire(admissions[0]); + try testing.expectError(error.InvalidAdmission, r.abortAcquire(admissions[0])); + try closeScope(&r, 0); +} +test "ID exhaustion is fail closed without mutation or handle reuse" { + var r = try TestRegistry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer r.deinit(); + const a = try r.beginAcquire(0); + r.next = std.math.maxInt(u64); + try testing.expectError(error.IdExhausted, r.publish(a, 0, .{})); + try testing.expectError(error.IdExhausted, r.reserveChild(0, &.{})); + try testing.expectError(error.IdExhausted, r.beginAcquire(0)); + try testing.expectError(error.IdExhausted, r.beginLease(0, .{ .run = 1, .key = 1 }, .read, false)); + try testing.expectEqual(@as(u32, 1), r.admissions.count()); + try r.abortAcquire(a); + try closeScope(&r, 0); +} +fn allocationProbe(allocator: std.mem.Allocator) !void { + var r = try TestRegistry.init(allocator, 1, .{ 0, 0, 0 }); + defer r.deinit(); + const token = try create(&r, 0, 5, .{}); + // Fill the scope map to its load limit so the tested transfer must grow it. + while (r.scopes.count() < r.scopes.capacity() * 80 / 100) _ = try r.reserveChild(0, &.{}); + const scope_count = r.scopes.count(); + const count = r.resources.count(); + const next = r.next; + const child = r.reserveChild(0, &.{token}) catch |err| { + try testing.expectEqual(next, r.next); + try testing.expectEqual(scope_count, r.scopes.count()); + try testing.expectEqual(count, r.resources.count()); + try testing.expectEqual(@as(u32, 5), (try r.lookup(0, token)).backend); + return err; + }; + const lease = try r.beginLease(child.scope, child.tokens[0], .read, false); + try r.endLease(lease.token); + try closeScope(&r, child.scope); + while (r.scopes.count() > 1) { + var it = r.scopes.keyIterator(); + while (it.next()) |scope| { + if (scope.* == 0) continue; + try closeScope(&r, scope.*); + break; + } + } + try closeScope(&r, 0); +} +test "allocator failure never partially transfers resources" { + try testing.checkAllAllocationFailures(testing.allocator, allocationProbe, .{}); +} +test "repeated scopes, acquisitions and leases retain no historical entries" { + var r = try TestRegistry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer r.deinit(); + for (0..4096) |_| { + const child = (try r.reserveChild(0, &.{})).scope; + const token = try create(&r, child, 0, .{}); + const lease = try r.beginLease(child, token, .exclusive, false); + try r.endLease(lease.token); + try closeScope(&r, child); + try testing.expectEqual(@as(u32, 1), r.scopes.count()); + try testing.expectEqual(@as(u32, 3), r.resources.count()); + try testing.expectEqual(@as(u32, 0), r.admissions.count()); + try testing.expectEqual(@as(u32, 0), r.leases.count()); + } + try closeScope(&r, 0); +} diff --git a/ext/zig/src/io_v2_snapshot.zig b/ext/zig/src/io_v2_snapshot.zig @@ -0,0 +1,68 @@ +//! Synchronous private anonymous snapshot. The host registers the descriptor +//! before copy; authentication completes before any format decoding. Not durable +//! publication, confinement, a deadline, or an aggregate disk/heap quota. +const std = @import("std"); +const F = @import("io_v2_files.zig"); +const Indexed = @import("indexed.zig"); +const c = @cImport({ + @cUndef("_FORTIFY_SOURCE"); + @cDefine("_FORTIFY_SOURCE", "0"); + @cInclude("uv.h"); +}); +pub fn create(allocator: std.mem.Allocator, status: *F.Status, directory: [:0]const u8) !F.File { + if (directory.len == 0 or std.mem.indexOfScalar(u8, directory, 0) != null) return error.InvalidArgument; + const template = try std.fmt.allocPrintSentinel(allocator, "{s}/.tricu-snapshot-XXXXXX", .{directory}, 0); + defer allocator.free(template); + var req: c.uv_fs_t = undefined; + const fd = c.uv_fs_mkstemp(null, &req, template.ptr, null); + defer c.uv_fs_req_cleanup(&req); + if (fd < 0) { + status.code = fd; + return error.HostFailure; + } + var file = F.File{ .fd = fd, .mode = .update }; + var unlink_req: c.uv_fs_t = undefined; + const removed = c.uv_fs_unlink(null, &unlink_req, req.path, null); + c.uv_fs_req_cleanup(&unlink_req); + if (removed < 0) { + var cleanup: F.Status = .{}; + file.close(&cleanup) catch {}; + status.* = .{ .code = removed, .cleanup_code = cleanup.code }; + return error.HostFailure; + } + return file; +} +pub const Snapshot = struct { + file: *F.File, + status: *F.Status, + length: u64, + pub fn readAt(self: *@This(), offset: u64, bytes: []u8) !void { + if (bytes.len > 4096 or offset > self.length or bytes.len > self.length - offset) return error.InvalidData; + try self.file.seek(offset); + var used: usize = 0; + while (used < bytes.len) { + const n = try self.file.read(self.status, bytes[used..]); + if (n == 0) return error.InvalidData; + used += n; + } + } +}; +pub fn authenticate(file: *F.File, source: *F.File, status: *F.Status, size: u64, expected: *const [64]u8) !void { + var hash = std.crypto.hash.sha2.Sha256.init(.{}); + hash.update(Indexed.domain ++ "\x00"); + var remaining = size; + var bytes: [4096]u8 = undefined; + while (remaining != 0) { + const wanted: usize = @intCast(@min(remaining, bytes.len)); + const count = try source.read(status, bytes[0..wanted]); + if (count == 0) return error.SnapshotTruncated; + var sent: usize = 0; + while (sent < count) sent += try file.write(status, bytes[sent..count]); + hash.update(bytes[0..count]); + remaining -= count; + } + var raw: [32]u8 = undefined; + hash.final(&raw); + const actual = std.fmt.bytesToHex(raw, .lower); + if (!std.mem.eql(u8, &actual, expected)) return error.SnapshotDigestMismatch; +} diff --git a/ext/zig/src/io_v2_stage.zig b/ext/zig/src/io_v2_stage.zig @@ -0,0 +1,208 @@ +//! Private native staged files. Scope registry owns the backend; this layer owns +//! only its descriptor/private name, never the publication destination. +const std = @import("std"); +const State = @import("io_v2_stage_state.zig"); +const c = @cImport({ + @cUndef("_FORTIFY_SOURCE"); + @cDefine("_FORTIFY_SOURCE", "0"); + @cInclude("uv.h"); + @cInclude("fcntl.h"); + @cInclude("sys/stat.h"); +}); +pub const Status = State.Status; +pub const Error = State.Error || error{ OutOfMemory, InvalidArgument, InvalidDestination }; +const marker = ".tricu-stage-"; +const Native = struct { + not_found: i32 = c.UV_ENOENT, + already_exists: i32 = c.UV_EEXIST, + io_error: i32 = c.UV_EIO, + pub fn write(_: *Native, fd: i32, bytes: []const u8) i32 { + var req: c.uv_fs_t = undefined; + var buffer = c.uv_buf_init(@constCast(bytes.ptr), @intCast(bytes.len)); + const rc = c.uv_fs_write(null, &req, fd, &buffer, 1, -1, null); + c.uv_fs_req_cleanup(&req); + return rc; + } + pub fn sync(_: *Native, fd: i32) i32 { + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_fsync(null, &req, fd, null); + c.uv_fs_req_cleanup(&req); + return rc; + } + pub fn close(_: *Native, fd: i32) i32 { + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_close(null, &req, fd, null); + c.uv_fs_req_cleanup(&req); + return rc; + } + pub fn unlink(_: *Native, path: [:0]const u8) i32 { + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_unlink(null, &req, path.ptr, null); + c.uv_fs_req_cleanup(&req); + return rc; + } + pub fn rename(_: *Native, source: [:0]const u8, target: [:0]const u8) i32 { + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_rename(null, &req, source.ptr, target.ptr, null); + c.uv_fs_req_cleanup(&req); + return rc; + } + pub fn link(_: *Native, source: [:0]const u8, target: [:0]const u8) i32 { + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_link(null, &req, source.ptr, target.ptr, null); + c.uv_fs_req_cleanup(&req); + return rc; + } + pub fn openDirectory(_: *Native, path: [:0]const u8) i32 { + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_open(null, &req, path.ptr, c.O_RDONLY | c.O_DIRECTORY | c.O_NONBLOCK | c.O_CLOEXEC | c.O_NOCTTY, 0, null); + c.uv_fs_req_cleanup(&req); + return rc; + } +}; +fn check(status: *Status, rc: i32) Error!void { + if (rc < 0) { + status.code = rc; + return error.HostFailure; + } +} +fn valid(path: [:0]const u8) Error!void { + if (path.len == 0 or std.mem.indexOfScalar(u8, path, 0) != null or !std.unicode.utf8ValidateSlice(path)) return error.InvalidArgument; +} +fn canonical(allocator: std.mem.Allocator, status: *Status, path: [:0]const u8) Error![:0]u8 { + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_realpath(null, &req, path.ptr, null); + defer c.uv_fs_req_cleanup(&req); + try check(status, rc); + return allocator.dupeZ(u8, std.mem.span(@as([*:0]const u8, @ptrCast(req.ptr)))); +} +pub const Stage = struct { + allocator: std.mem.Allocator, + state: State.Stage, + directory: [:0]u8, + path: [:0]u8, + pub fn init(allocator: std.mem.Allocator, status: *Status, directory: [:0]const u8) Error!Stage { + status.* = .{}; + try valid(directory); + const dir = try canonical(allocator, status, directory); + errdefer allocator.free(dir); + const template = try std.fmt.allocPrintSentinel(allocator, "{s}/{s}XXXXXX", .{ dir, marker }, 0); + defer allocator.free(template); + const path = try allocator.allocSentinel(u8, template.len, 0); + errdefer allocator.free(path); + var req: c.uv_fs_t = undefined; + const fd = c.uv_fs_mkstemp(null, &req, template.ptr, null); + defer c.uv_fs_req_cleanup(&req); + try check(status, fd); + // mkstemp replaces exactly six characters. Storage was reserved BEFORE + // file creation; no fallible allocation separates acquisition/publication. + @memcpy(path, req.path[0..path.len]); + return .{ .allocator = allocator, .directory = dir, .path = path, .state = .{ .directory = dir, .path = path, .fd = fd } }; + } + pub fn deinit(self: *Stage) void { + self.allocator.free(self.path); + self.allocator.free(self.directory); + } + pub fn write(self: *Stage, status: *Status, bytes: []const u8) Error!usize { + var ops: Native = .{}; + return self.state.write(&ops, status, bytes); + } + pub fn seal(self: *Stage, status: *Status) Error!void { + var ops: Native = .{}; + try self.state.seal(&ops, status); + } + pub fn release(self: *Stage, status: *Status) Error!void { + var ops: Native = .{}; + try self.state.release(&ops, status); + } + pub fn publish(self: *Stage, status: *Status, replace: bool, destination: [:0]const u8) Error!State.Publication { + status.* = .{}; + try self.state.checkSealed(); + status.progress = .not_published; + errdefer self.state.preparationFailed(); + if (std.mem.indexOfScalar(u8, destination, 0) != null or !std.unicode.utf8ValidateSlice(destination)) return error.InvalidArgument; + const slash = std.mem.lastIndexOfScalar(u8, destination, '/'); + const parent = if (slash) |i| (if (i == 0) "/" else destination[0..i]) else "."; + const name = if (slash) |i| destination[i + 1 ..] else destination; + const parent_z = try self.allocator.dupeZ(u8, parent); + defer self.allocator.free(parent_z); + const target_dir = try canonical(self.allocator, status, parent_z); + defer self.allocator.free(target_dir); + if (name.len == 0 or std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..") or std.mem.indexOf(u8, name, marker) != null) return error.InvalidDestination; + const target = try std.fmt.allocPrintSentinel(self.allocator, "{s}/{s}", .{ target_dir, name }, 0); + defer self.allocator.free(target); + var ops: Native = .{}; + return self.state.publish(&ops, status, replace, target_dir, target); + } +}; + +const t = std.testing; +fn testPath(tmp: std.Io.Dir, name: []const u8) ![:0]u8 { + const dir = try tmp.realPathFileAlloc(t.io, ".", t.allocator); + defer t.allocator.free(dir); + return std.fmt.allocPrintSentinel(t.allocator, "{s}/{s}", .{ dir, name }, 0); +} +test "native staged cross-directory no-replace, replacement and private cleanup" { + var tmp = t.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.createDir(t.io, "a", .default_dir); + try tmp.dir.createDir(t.io, "b", .default_dir); + const source = try testPath(tmp.dir, "a"); + defer t.allocator.free(source); + const target = try testPath(tmp.dir, "b/out"); + defer t.allocator.free(target); + var status: Status = .{}; + var stage = try Stage.init(t.allocator, &status, source); + defer stage.deinit(); + defer stage.release(&status) catch {}; + _ = try stage.write(&status, "abc"); + try stage.seal(&status); + try t.expectEqual(State.Publication.published, try stage.publish(&status, false, target)); + var second = try Stage.init(t.allocator, &status, source); + defer second.deinit(); + defer second.release(&status) catch {}; + _ = try second.write(&status, "new"); + try second.seal(&status); + try t.expectEqual(State.Publication.exists, try second.publish(&status, false, target)); + var ops: Native = .{}; + // Existing is neither verified identity nor permission to delete that name. + var req: c.uv_fs_t = undefined; + const fd = c.uv_fs_open(null, &req, target.ptr, c.O_RDONLY, 0, null); + c.uv_fs_req_cleanup(&req); + try t.expect(fd >= 0); + var bytes: [3]u8 = undefined; + var buffer = c.uv_buf_init(&bytes, 3); + const n = c.uv_fs_read(null, &req, fd, &buffer, 1, 0, null); + c.uv_fs_req_cleanup(&req); + _ = ops.close(fd); + try t.expectEqual(@as(c_int, 3), n); + try t.expectEqualStrings("abc", &bytes); + try t.expectEqual(State.Publication.published, try second.publish(&status, true, target)); + try second.release(&status); + try stage.release(&status); + const output = try tmp.dir.readFileAlloc(t.io, "b/out", t.allocator, .limited(4)); + defer t.allocator.free(output); + try t.expectEqualStrings("new", output); +} +test "invalid destination poisons sealed stage; early release closes and removes private name" { + var tmp = t.tmpDir(.{}); + defer tmp.cleanup(); + const directory = try testPath(tmp.dir, "."); + defer t.allocator.free(directory); + const bad = try testPath(tmp.dir, ".tricu-stage-stolen"); + defer t.allocator.free(bad); + var status: Status = .{}; + var stage = try Stage.init(t.allocator, &status, directory); + defer stage.deinit(); + const private = try t.allocator.dupeZ(u8, stage.path); + defer t.allocator.free(private); + try stage.seal(&status); + try t.expectError(error.InvalidDestination, stage.publish(&status, true, bad)); + try t.expectEqual(State.Progress.not_published, status.progress); + try t.expectError(error.InvalidState, stage.publish(&status, true, bad)); + try stage.release(&status); + var req: c.uv_fs_t = undefined; + const rc = c.uv_fs_stat(null, &req, private.ptr, null); + c.uv_fs_req_cleanup(&req); + try t.expectEqual(@as(c_int, c.UV_ENOENT), rc); +} diff --git a/ext/zig/src/io_v2_stage_state.zig b/ext/zig/src/io_v2_stage_state.zig @@ -0,0 +1,229 @@ +//! Publication state machine. Ops are synchronous, allocation-free OS hooks; +//! paths are prepared/owned by the embedding. No source/artifact/digest policy. +const std = @import("std"); +pub const Error = error{ InvalidState, InvalidBound, HostFailure }; +pub const Phase = enum { writing, sealed, failed, published }; +pub const Progress = enum { none, not_published, unknown, published }; +pub const Status = struct { code: i32 = 0, secondary: i32 = 0, progress: Progress = .none }; +pub const Publication = enum { exists, published }; +pub const Stage = struct { + directory: [:0]const u8, + path: [:0]const u8, + fd: ?i32, + phase: Phase = .writing, + private_name: bool = true, + fn require(self: *Stage, phase: Phase) Error!void { + if (self.phase != phase) return error.InvalidState; + } + fn check(status: *Status, code: i32) Error!void { + if (code < 0) { + status.code = code; + return error.HostFailure; + } + } + fn closeOwned(self: *Stage, ops: anytype) i32 { + const fd = self.fd orelse return 0; + self.fd = null; // an uncertain close is never retried against a reused FD + return ops.close(fd); + } + fn removePrivate(self: *Stage, ops: anytype) i32 { + if (!self.private_name) return 0; + const rc = ops.unlink(self.path); + if (rc >= 0 or rc == ops.not_found) { + self.private_name = false; + return 0; + } + return rc; + } + pub fn write(self: *Stage, ops: anytype, status: *Status, bytes: []const u8) Error!usize { + status.* = .{}; + if (bytes.len == 0 or bytes.len > 4096) return error.InvalidBound; + try self.require(.writing); + const rc = ops.write(self.fd.?, bytes); + if (rc <= 0 or rc > bytes.len) { + self.phase = .failed; + status.progress = .unknown; + status.code = if (rc < 0) rc else ops.io_error; + return error.HostFailure; + } + return @intCast(rc); + } + pub fn seal(self: *Stage, ops: anytype, status: *Status) Error!void { + status.* = .{}; + try self.require(.writing); + errdefer self.phase = .failed; + status.progress = .unknown; + try check(status, ops.sync(self.fd.?)); + try check(status, self.closeOwned(ops)); + self.phase = .sealed; + } + pub fn checkSealed(self: *Stage) Error!void { + try self.require(.sealed); + } + // Preparation errors after a valid publish request poison the stage too; + // argument decoding errors are handled before invoking the backend. + pub fn preparationFailed(self: *Stage) void { + self.phase = .failed; + } + pub fn publish(self: *Stage, ops: anytype, status: *Status, replace: bool, target_directory: [:0]const u8, target: [:0]const u8) Error!Publication { + status.* = .{}; + try self.require(.sealed); + errdefer self.phase = .failed; + status.progress = .unknown; + const rc = if (replace) ops.rename(self.path, target) else ops.link(self.path, target); + if (!replace and rc == ops.already_exists) return .exists; + try check(status, rc); + self.phase = .published; + status.progress = .published; + if (replace) self.private_name = false else try check(status, self.removePrivate(ops)); + try self.syncParent(ops, status, target_directory); + if (!std.mem.eql(u8, self.directory, target_directory)) try self.syncParent(ops, status, self.directory); + return .published; + } + fn syncParent(self: *Stage, ops: anytype, status: *Status, path: [:0]const u8) Error!void { + const fd = ops.openDirectory(path); + try check(status, fd); + self.fd = fd; + try check(status, ops.sync(fd)); + try check(status, self.closeOwned(ops)); + } + pub fn release(self: *Stage, ops: anytype, status: *Status) Error!void { + status.* = .{ .progress = .unknown }; + const closed = self.closeOwned(ops); + const removed = self.removePrivate(ops); // attempted even if close failed + if (closed < 0) { + status.code = closed; + if (removed < 0) status.secondary = removed; + return error.HostFailure; + } + try check(status, removed); + } +}; + +const Mock = struct { + const Event = enum { write, sync, close, unlink, rename, link, open_source, open_target }; + not_found: i32 = -2, + already_exists: i32 = -17, + io_error: i32 = -5, + events: [32]Event = undefined, + len: usize = 0, + fail_at: ?usize = null, + failure: i32 = -5, + fn event(self: *Mock, value: Event) i32 { + self.events[self.len] = value; + self.len += 1; + return if (self.fail_at == self.len) self.failure else 0; + } + fn write(self: *Mock, _: i32, bytes: []const u8) i32 { + const rc = self.event(.write); + return if (rc < 0) rc else @intCast(bytes.len); + } + fn sync(self: *Mock, _: i32) i32 { + return self.event(.sync); + } + fn close(self: *Mock, _: i32) i32 { + return self.event(.close); + } + fn unlink(self: *Mock, _: [:0]const u8) i32 { + return self.event(.unlink); + } + fn rename(self: *Mock, _: [:0]const u8, _: [:0]const u8) i32 { + return self.event(.rename); + } + fn link(self: *Mock, _: [:0]const u8, _: [:0]const u8) i32 { + return self.event(.link); + } + fn openDirectory(self: *Mock, path: [:0]const u8) i32 { + const rc = self.event(if (std.mem.eql(u8, path, "source")) .open_source else .open_target); + return if (rc < 0) rc else 20; + } +}; +fn fresh() Stage { + return .{ .directory = "source", .path = "source/.tricu-stage-private", .fd = 10 }; +} +test "stages seal once, publish across directories, sync destination first and retain no destination ownership" { + const t = std.testing; + for ([_]bool{ false, true }) |replace| { + var stage = fresh(); + var ops: Mock = .{}; + var status: Status = .{}; + try t.expectError(error.InvalidState, stage.publish(&ops, &status, replace, "target", "target/file")); + try t.expectEqual(@as(usize, 0), ops.len); + _ = try stage.write(&ops, &status, "abc"); + try stage.seal(&ops, &status); + try t.expect(stage.fd == null); + try t.expectError(error.InvalidState, stage.write(&ops, &status, "x")); + try t.expectEqual(Publication.published, try stage.publish(&ops, &status, replace, "target", "target/file")); + try t.expectEqual(Progress.published, status.progress); + const expected_link = [_]Mock.Event{ .write, .sync, .close, .link, .unlink, .open_target, .sync, .close, .open_source, .sync, .close }; + const expected_rename = [_]Mock.Event{ .write, .sync, .close, .rename, .open_target, .sync, .close, .open_source, .sync, .close }; + try t.expectEqualSlices(Mock.Event, if (replace) &expected_rename else &expected_link, ops.events[0..ops.len]); + const before = ops.len; + try stage.release(&ops, &status); + try stage.release(&ops, &status); + try t.expectEqual(before, ops.len); + } +} +test "all publication failures are poisoned, preserve visibility and attempt only private cleanup" { + const t = std.testing; + // link, private unlink, target open/sync/close, source open/sync/close. + for (1..9) |step| { + var stage = fresh(); + stage.phase = .sealed; + stage.fd = null; + var ops: Mock = .{ .fail_at = step }; + var status: Status = .{}; + try t.expectError(error.HostFailure, stage.publish(&ops, &status, false, "target", "target/file")); + try t.expectEqual(Phase.failed, stage.phase); + try t.expectEqual(if (step == 1) Progress.unknown else .published, status.progress); + const before = ops.len; + try t.expectError(error.InvalidState, stage.publish(&ops, &status, true, "target", "target/file")); + try t.expectEqual(before, ops.len); + ops.fail_at = null; + try stage.release(&ops, &status); + try t.expect(stage.fd == null and !stage.private_name); + for (ops.events[before..ops.len]) |event| try t.expect(event == .close or event == .unlink); + } +} +test "exists is not identity or durability; ambiguous EXDEV is not copy or retry permission" { + const t = std.testing; + var stage = fresh(); + stage.phase = .sealed; + stage.fd = null; + var ops: Mock = .{ .fail_at = 1, .failure = -17 }; + var status: Status = .{}; + try t.expectEqual(Publication.exists, try stage.publish(&ops, &status, false, "target", "target/file")); + try t.expectEqual(Phase.sealed, stage.phase); + try t.expectEqual(@as(usize, 1), ops.len); + ops.fail_at = 2; + ops.failure = -18; + try t.expectError(error.HostFailure, stage.publish(&ops, &status, false, "target", "target/file")); + try t.expectEqual(Progress.unknown, status.progress); + try t.expectEqual(@as(usize, 2), ops.len); + ops.fail_at = null; + try stage.release(&ops, &status); +} +test "seal failures retain or retire descriptors exactly once and cleanup still unlinks" { + const t = std.testing; + for ([_]usize{ 1, 2 }) |step| { + var stage = fresh(); + var ops: Mock = .{ .fail_at = step }; + var status: Status = .{}; + try t.expectError(error.HostFailure, stage.seal(&ops, &status)); + try t.expectEqual(Phase.failed, stage.phase); + try t.expect((stage.fd != null) == (step == 1)); + ops.fail_at = null; + try stage.release(&ops, &status); + var closes: usize = 0; + for (ops.events[0..ops.len]) |event| if (event == .close) { + closes += 1; + }; + try t.expectEqual(@as(usize, 1), closes); + try t.expectEqual(Mock.Event.unlink, ops.events[ops.len - 1]); + } + var stage = fresh(); + var ops: Mock = .{ .fail_at = 1 }; + var status: Status = .{}; + try t.expectError(error.HostFailure, stage.release(&ops, &status)); + try t.expectEqualSlices(Mock.Event, &.{ .close, .unlink }, ops.events[0..ops.len]); +} diff --git a/ext/zig/src/io_v2_tasks.zig b/ext/zig/src/io_v2_tasks.zig @@ -0,0 +1,582 @@ +//! Structured task state machine, owned by one event loop. No user code, host IO, +//! allocation during stop/settlement, or v1 scheduler reuse. The driver owns body +//! continuations, prepares their storage before admission, and visits every live +//! Payload here when registering evaluator GC roots. +const std = @import("std"); +const R = @import("io_v2_resources.zig"); +pub const Phase = enum { open, joining, releasing }; +pub const Kind = enum { root, lexical, spawned }; +pub const Error = R.Fault || error{ Stopped, StaleTask, JoinCycle, InvalidTransition }; + +pub fn Scheduler(comptime Backend: type, comptime Payload: type) type { + return struct { + const Self = @This(); + pub const Registry = R.Registry(Backend); + pub const Diagnostics = struct { + items: [16]Payload = undefined, + len: usize = 0, + omitted: u64 = 0, + pub fn append(self: *Diagnostics, other: Diagnostics) void { + for (other.items[0..other.len]) |item| { + if (self.len < 16) { + self.items[self.len] = item; + self.len += 1; + } else self.omitted += 1; + } + self.omitted += other.omitted; + } + pub fn one(value: Payload) Diagnostics { + var result: Diagnostics = .{}; + result.items[0] = value; + result.len = 1; + return result; + } + }; + pub const Outcome = struct { + kind: enum { completed, failed, cancelled }, + value: Payload, + cleanup: Diagnostics = .{}, + fn primary(self: Outcome) Outcome { + return .{ .kind = self.kind, .value = self.value }; + } + }; + pub const Group = struct { + parent: ?R.Scope, + task: u64, + kind: Kind, + propagate: bool, + phase: Phase = .open, + reason: ?Outcome = null, + body: ?Outcome = null, + cleanup: Diagnostics = .{}, + lexical: ?R.Scope = null, + wait: ?u64 = null, + host_calls: u32 = 0, + visited: bool = false, + walked: bool = false, + }; + const Task = struct { + owner: R.Scope, + supervised: bool, + ticket: bool, + waiter: ?R.Scope = null, + outcome: ?Outcome = null, + }; + allocator: std.mem.Allocator, + registry: *Registry, + owner_stopped: Payload, + cycle: Payload, + groups: std.AutoHashMapUnmanaged(R.Scope, Group) = .empty, + tasks: std.AutoHashMapUnmanaged(u64, Task) = .empty, + + pub fn init(allocator: std.mem.Allocator, registry: *Registry, owner_stopped: Payload, cycle: Payload) !Self { + var self: Self = .{ .allocator = allocator, .registry = registry, .owner_stopped = owner_stopped, .cycle = cycle }; + try self.groups.put(allocator, 0, .{ .parent = null, .task = 0, .kind = .root, .propagate = false }); + return self; + } + pub fn deinit(self: *Self) void { + self.tasks.deinit(self.allocator); + self.groups.deinit(self.allocator); + } + pub fn checkpoint(self: *Self, scope: R.Scope) Error!void { + const group = self.groups.get(scope) orelse return error.ClosedScope; + if (group.reason != null) return error.Stopped; + if (group.phase != .open or group.lexical != null or group.wait != null) return error.InvalidTransition; + } + pub fn nested(self: *Self, parent: R.Scope) Error!R.Scope { + try self.checkpoint(parent); + const task = self.groups.get(parent).?.task; + try self.groups.ensureUnusedCapacity(self.allocator, 1); + const child = (try self.registry.reserveChild(parent, &.{})).scope; + self.groups.putAssumeCapacityNoClobber(child, .{ .parent = parent, .task = task, .kind = .lexical, .propagate = true }); + self.groups.getPtr(parent).?.lexical = child; + return child; + } + pub fn spawn(self: *Self, owner: R.Scope, supervised: bool, keep: bool, tokens: []const R.Token) Error!Registry.Transfer { + try self.checkpoint(owner); + try self.groups.ensureUnusedCapacity(self.allocator, 1); + try self.tasks.ensureUnusedCapacity(self.allocator, 1); + const child = try self.registry.reserveChild(owner, tokens); + // Everything following resource commitment is allocation-free. + self.groups.putAssumeCapacityNoClobber(child.scope, .{ .parent = owner, .task = child.scope, .kind = .spawned, .propagate = !supervised }); + self.tasks.putAssumeCapacityNoClobber(child.scope, .{ .owner = owner, .supervised = supervised, .ticket = keep }); + return child; + } + fn recordStop(self: *Self, scope: R.Scope, reason: Outcome) bool { + const group = self.groups.getPtr(scope) orelse return false; + if (group.reason != null) return false; + group.reason = reason.primary(); + self.registry.beginCloseScope(scope); + return true; + } + // No allocation or host interruption may prevent requests reaching the + // entire active subtree. First recorded reason wins, including while joining. + pub fn stop(self: *Self, scope: R.Scope, reason: Outcome) void { + std.debug.assert(reason.kind != .completed); + if (!self.recordStop(scope, reason)) return; + var changed = true; + while (changed) { + changed = false; + var it = self.groups.iterator(); + while (it.next()) |entry| { + const g = entry.value_ptr.*; + if (g.parent) |parent| { + if (g.reason) |why| { + if (why.kind == .failed and g.propagate) changed = self.recordStop(parent, why) or changed; + } + if (self.groups.get(parent).?.reason != null) { + changed = self.recordStop(entry.key_ptr.*, .{ .kind = .cancelled, .value = self.owner_stopped }) or changed; + } + } + } + } + } + fn checked(self: *Self, current: R.Scope, token: R.Token) Error!*Task { + if (token.run != self.registry.identity) return error.StaleTask; + const task = self.tasks.getPtr(token.key) orelse return error.StaleTask; + if (!self.registry.within(current, task.owner)) return error.OutOfScope; + return task; + } + fn markTask(self: *Self, task: u64) bool { + var changed = false; + var it = self.groups.valueIterator(); + while (it.next()) |g| if (g.task == task and !g.visited) { + g.visited = true; + changed = true; + }; + return changed; + } + // Allocation-free reachability over task identities, not lexical scope + // IDs. Active implicit joins contribute edges just like explicit tickets. + fn reaches(self: *Self, start: u64, wanted: u64) bool { + if (start == wanted) return true; + var reset = self.groups.valueIterator(); + while (reset.next()) |g| { + g.visited = false; + g.walked = false; + } + _ = self.markTask(start); + var changed = true; + while (changed) { + changed = false; + var it = self.groups.iterator(); + while (it.next()) |entry| { + if (!entry.value_ptr.visited or entry.value_ptr.walked) continue; + entry.value_ptr.walked = true; + const g = entry.value_ptr.*; + if (g.wait) |target| { + if (target == wanted) return true; + changed = self.markTask(target) or changed; + } + if (g.phase == .joining) { + var children = self.tasks.iterator(); + while (children.next()) |child| { + if (child.value_ptr.owner != entry.key_ptr.* or child.value_ptr.outcome != null) continue; + if (child.key_ptr.* == wanted) return true; + changed = self.markTask(child.key_ptr.*) or changed; + } + } + } + } + return false; + } + pub fn claimJoin(self: *Self, current: R.Scope, token: R.Token) Error!void { + try self.checkpoint(current); + const task = try self.checked(current, token); + if (!task.ticket) return error.StaleTask; + const identity = self.groups.get(current).?.task; + if (self.registry.within(current, token.key) or self.reaches(token.key, identity)) return error.JoinCycle; + // Consume before waiting. Cancellation must not restore this ticket. + task.ticket = false; + task.waiter = current; + self.groups.getPtr(current).?.wait = token.key; + } + pub fn pollJoin(self: *Self, current: R.Scope) Error!?Outcome { + const g = self.groups.get(current) orelse return error.ClosedScope; + const target = g.wait orelse return error.InvalidTransition; + const task = self.tasks.get(target) orelse return error.StaleTask; + const outcome = task.outcome orelse return null; + self.groups.getPtr(current).?.wait = null; + _ = self.tasks.remove(target); + return outcome; + } + pub fn abandonJoin(self: *Self, current: R.Scope) void { + const group = self.groups.getPtr(current) orelse return; + const target = group.wait orelse return; + group.wait = null; + const task = self.tasks.getPtr(target).?; + task.waiter = null; + if (task.outcome != null) _ = self.tasks.remove(target); + } + pub fn discard(self: *Self, current: R.Scope, token: R.Token) Error!void { + try self.checkpoint(current); + const task = try self.checked(current, token); + if (!task.ticket) return error.StaleTask; + task.ticket = false; + if (task.outcome != null) _ = self.tasks.remove(token.key); + } + pub fn cancel(self: *Self, current: R.Scope, token: R.Token, reason: Payload) Error!void { + try self.checkpoint(current); + const task = try self.checked(current, token); + if (task.outcome != null) return error.StaleTask; + self.stop(token.key, .{ .kind = .cancelled, .value = reason }); + } + pub fn bodyDone(self: *Self, scope: R.Scope, outcome: Outcome) Error!void { + const g = self.groups.get(scope) orelse return error.ClosedScope; + if (g.phase != .open or g.lexical != null) return error.InvalidTransition; + self.abandonJoin(scope); + if (outcome.kind != .completed) self.stop(scope, outcome); + self.groups.getPtr(scope).?.body = outcome; + self.registry.beginCloseScope(scope); + // Detect before adding implicit edges. A cycle is terminal; explicit + // join cycles instead leave the ticket untouched and return an error. + var it = self.tasks.iterator(); + while (it.next()) |entry| { + if (entry.value_ptr.owner == scope and entry.value_ptr.outcome == null and self.reaches(entry.key_ptr.*, g.task)) { + self.stop(scope, .{ .kind = .failed, .value = self.cycle }); + break; + } + } + self.groups.getPtr(scope).?.phase = .joining; + } + // The task owns host calls even when a newly acquired resource belongs to + // an ancestor. Cancellation never detaches an unfinished callback. + pub fn beginHost(self: *Self, scope: R.Scope) Error!void { + try self.checkpoint(scope); + const group = self.groups.getPtr(scope).?; + group.host_calls = std.math.add(u32, group.host_calls, 1) catch return error.OutOfMemory; + } + pub fn endHost(self: *Self, scope: R.Scope) Error!void { + const group = self.groups.getPtr(scope) orelse return error.ClosedScope; + if (group.host_calls == 0) return error.InvalidTransition; + group.host_calls -= 1; + } + pub fn beginRelease(self: *Self, scope: R.Scope) Error!bool { + const g = self.groups.get(scope) orelse return error.ClosedScope; + if (g.phase != .joining) return error.InvalidTransition; + var children = self.tasks.valueIterator(); + while (children.next()) |task| if (task.owner == scope and task.outcome == null) return false; + if (g.host_calls != 0 or !self.registry.readyForRelease(scope)) return false; + self.groups.getPtr(scope).?.phase = .releasing; + return true; + } + pub fn settle(self: *Self, scope: R.Scope, own_cleanup: Diagnostics) Error!Outcome { + const g = self.groups.get(scope) orelse return error.ClosedScope; + if (g.phase != .releasing) return error.InvalidTransition; + try self.registry.finishScope(scope); + var final = (g.reason orelse g.body.?).primary(); + var errors = g.body.?.cleanup; + errors.append(g.cleanup); + errors.append(own_cleanup); + if (final.kind == .completed and errors.len != 0) { + final = .{ .kind = .failed, .value = errors.items[0] }; + std.mem.copyForwards(Payload, errors.items[0 .. errors.len - 1], errors.items[1..errors.len]); + errors.len -= 1; + } + final.cleanup = errors; + if (final.kind == .failed) self.stop(scope, final); + // Drop all unclaimed tickets owned by this scope, including completed + // results. No iteration pointer survives a removal/restart. + while (true) { + var owned: ?u64 = null; + var it = self.tasks.iterator(); + while (it.next()) |entry| if (entry.value_ptr.owner == scope) { + owned = entry.key_ptr.*; + break; + }; + if (owned) |key| { + _ = self.tasks.remove(key); + } else break; + } + if (g.parent) |parent| { + if (g.kind == .lexical) { + self.groups.getPtr(parent).?.cleanup.append(final.cleanup); + self.groups.getPtr(parent).?.lexical = null; + } else { + const task = self.tasks.getPtr(scope).?; + if (!task.supervised) self.groups.getPtr(parent).?.cleanup.append(final.cleanup); + task.outcome = final; + if (!task.ticket and task.waiter == null) _ = self.tasks.remove(scope); + } + } + _ = self.groups.remove(scope); + return if (g.kind == .lexical) final.primary() else final; + } + // Embeddings must root these plus their body/continuation/host-reply roots. + pub fn visitPayloads(self: *const Self, context: anytype, visit: anytype) !void { + try visit(context, self.owner_stopped); + try visit(context, self.cycle); + var groups = self.groups.valueIterator(); + while (groups.next()) |g| { + if (g.reason) |reason| try visit(context, reason.value); + if (g.body) |body| { + try visit(context, body.value); + for (body.cleanup.items[0..body.cleanup.len]) |v| try visit(context, v); + } + for (g.cleanup.items[0..g.cleanup.len]) |v| try visit(context, v); + } + var tasks = self.tasks.valueIterator(); + while (tasks.next()) |task| if (task.outcome) |outcome| { + try visit(context, outcome.value); + for (outcome.cleanup.items[0..outcome.cleanup.len]) |v| try visit(context, v); + }; + } + }; +} + +const testing = std.testing; +const S = Scheduler(u32, u32); +fn taskToken(s: *S, id: u64) R.Token { + return .{ .run = s.registry.identity, .key = id }; +} +fn finish(s: *S, scope: R.Scope, body: S.Outcome, cleanup: S.Diagnostics) !S.Outcome { + try s.bodyDone(scope, body); + try testing.expect(try s.beginRelease(scope)); + return release(s, scope, cleanup); +} +fn release(s: *S, scope: R.Scope, cleanup: S.Diagnostics) !S.Outcome { + while (s.registry.newestOwned(scope)) |resource| { + _ = (try s.registry.claimClose(scope, resource, true)).?; + try s.registry.finishClose(resource); + } + return s.settle(scope, cleanup); +} +fn ok(value: u32) S.Outcome { + return .{ .kind = .completed, .value = value }; +} +fn fail(value: u32) S.Outcome { + return .{ .kind = .failed, .value = value }; +} +fn cancelled(value: u32) S.Outcome { + return .{ .kind = .cancelled, .value = value }; +} + +test "normal exit closes admission, joins, remains cancellable and retains no task history" { + var registry = try S.Registry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(testing.allocator, &registry, 90, 91); + defer s.deinit(); + const child = try s.spawn(0, false, false, &.{}); + try s.bodyDone(0, ok(4)); + try testing.expect(!try s.beginRelease(0)); + try testing.expectError(error.ClosedScope, registry.beginAcquire(0)); + s.stop(0, cancelled(77)); + try testing.expectEqual(@as(u32, 90), s.groups.get(child.scope).?.reason.?.value); + _ = try finish(&s, child.scope, ok(5), .{}); + try testing.expect(try s.beginRelease(0)); + const outcome = try release(&s, 0, .{}); + try testing.expectEqual(.cancelled, outcome.kind); + try testing.expectEqual(@as(u32, 77), outcome.value); + try testing.expectEqual(@as(u32, 0), s.tasks.count()); + try testing.expectEqual(@as(u32, 0), s.groups.count()); +} +test "unsupervised first failure stops siblings before release; supervision contains failure" { + var registry = try S.Registry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(testing.allocator, &registry, 90, 91); + defer s.deinit(); + const contained = (try s.spawn(0, true, true, &.{})).scope; + _ = try finish(&s, contained, fail(5), .{}); + try testing.expectEqual(@as(?S.Outcome, null), s.groups.get(0).?.reason); + const first = (try s.spawn(0, false, true, &.{})).scope; + const second = (try s.spawn(0, false, true, &.{})).scope; + s.stop(first, fail(7)); + s.stop(second, fail(8)); + try testing.expectEqual(@as(u32, 7), s.groups.get(0).?.reason.?.value); + try testing.expectEqual(.cancelled, s.groups.get(second).?.reason.?.kind); + try testing.expectError(error.ClosedScope, registry.beginAcquire(second)); + _ = try finish(&s, first, ok(0), .{}); + _ = try finish(&s, second, ok(0), .{}); + const outcome = try finish(&s, 0, ok(0), .{}); + try testing.expectEqual(@as(u32, 7), outcome.value); + try testing.expectEqual(@as(u32, 0), s.tasks.count()); +} +test "consumable join tickets, cancellation and abandoned waits" { + var registry = try S.Registry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(testing.allocator, &registry, 90, 91); + defer s.deinit(); + const child = (try s.spawn(0, true, true, &.{})).scope; + const other = (try s.spawn(0, true, true, &.{})).scope; + try s.claimJoin(other, taskToken(&s, child)); + try testing.expectEqual(@as(?S.Outcome, null), try s.pollJoin(other)); + try testing.expectError(error.StaleTask, s.discard(0, taskToken(&s, child))); + try s.cancel(0, taskToken(&s, other), 13); + _ = try finish(&s, other, ok(0), .{}); + try testing.expectEqual(@as(?R.Scope, null), s.tasks.get(child).?.waiter); + _ = try finish(&s, child, ok(123), .{}); + try testing.expect(!s.tasks.contains(child)); + try s.claimJoin(0, taskToken(&s, other)); + const result = (try s.pollJoin(0)).?; + try testing.expectEqual(.cancelled, result.kind); + try testing.expectEqual(@as(u32, 13), result.value); + try testing.expectError(error.StaleTask, s.claimJoin(0, taskToken(&s, other))); + _ = try finish(&s, 0, ok(0), .{}); +} +test "explicit and implicit join cycles include lexical task identities" { + var registry = try S.Registry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(testing.allocator, &registry, 90, 91); + defer s.deinit(); + const a = (try s.spawn(0, true, true, &.{})).scope; + var b = (try s.spawn(0, true, true, &.{})).scope; + const nested = try s.nested(a); + try testing.expectError(error.JoinCycle, s.claimJoin(nested, taskToken(&s, a))); + try s.claimJoin(nested, taskToken(&s, b)); + try testing.expectError(error.JoinCycle, s.claimJoin(b, taskToken(&s, a))); + try testing.expect(s.tasks.get(a).?.ticket); + s.abandonJoin(nested); + _ = try finish(&s, b, ok(0), .{}); + // The abandoned ticket was consumed, so use a fresh task for this cycle. + b = (try s.spawn(0, true, true, &.{})).scope; + // b waits for a; a then implicitly joins a nested-scope child waiting on b. + const c = (try s.spawn(nested, true, true, &.{})).scope; + try s.claimJoin(b, taskToken(&s, a)); + try s.claimJoin(c, taskToken(&s, b)); + try s.bodyDone(nested, ok(0)); + try testing.expectEqual(@as(u32, 91), s.groups.get(nested).?.reason.?.value); + try testing.expectEqual(@as(u32, 91), s.groups.get(a).?.reason.?.value); + _ = try finish(&s, c, ok(0), .{}); + try testing.expect(try s.beginRelease(nested)); + _ = try release(&s, nested, .{}); + _ = try finish(&s, a, ok(0), .{}); + const result = (try s.pollJoin(b)).?; + try testing.expectEqual(.failed, result.kind); + try testing.expectEqual(@as(u32, 91), result.value); + _ = try finish(&s, b, ok(0), .{}); + _ = try finish(&s, 0, ok(0), .{}); +} +test "cleanup fails success, preserves primary and propagates once with explicit omissions" { + var registry = try S.Registry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(testing.allocator, &registry, 90, 91); + defer s.deinit(); + const inner = try s.nested(0); + const child = (try s.spawn(inner, false, false, &.{})).scope; + const child_result = try finish(&s, child, ok(0), S.Diagnostics.one(17)); + try testing.expectEqual(.failed, child_result.kind); + try testing.expectEqual(@as(u32, 17), child_result.value); + var cleanup: S.Diagnostics = .{}; + for (0..25) |i| cleanup.append(S.Diagnostics.one(@intCast(100 + i))); + const inner_result = try finish(&s, inner, ok(0), cleanup); + try testing.expectEqual(@as(usize, 0), inner_result.cleanup.len); // lexical return omits cleanup + const result = try finish(&s, 0, fail(999), S.Diagnostics.one(200)); + try testing.expectEqual(@as(u32, 17), result.value); // first stop, not late body fault + try testing.expectEqual(@as(usize, 16), result.cleanup.len); + try testing.expectEqual(@as(u64, 10), result.cleanup.omitted); + try testing.expectEqual(@as(u32, 100), result.cleanup.items[0]); +} +test "host callbacks and ancestor-owned acquisition settle before cancelled task retirement" { + var registry = try S.Registry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(testing.allocator, &registry, 90, 91); + defer s.deinit(); + const child = (try s.spawn(0, true, false, &.{})).scope; + try s.beginHost(child); + const admission = try registry.beginAcquire(0); + try s.cancel(0, taskToken(&s, child), 77); + try s.bodyDone(child, ok(0)); + try testing.expect(!try s.beginRelease(child)); + const resource = try registry.publish(admission, 5, .{}); + try s.endHost(child); + try testing.expectError(error.InvalidTransition, s.endHost(child)); + try testing.expect(try s.beginRelease(child)); + _ = try release(&s, child, .{}); + try testing.expectEqual(@as(u32, 5), (try registry.lookup(0, resource)).backend); + _ = try finish(&s, 0, ok(0), .{}); +} +fn allocationProbe(allocator: std.mem.Allocator) !void { + var registry = try S.Registry.init(allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(allocator, &registry, 90, 91); + defer s.deinit(); + for (0..40) |_| { + const resource = try registry.publish(try registry.beginAcquire(0), 22, .{}); + const before = registry.next; + const count = s.groups.count(); + const task_count = s.tasks.count(); + _ = s.spawn(0, false, false, &.{resource}) catch |err| { + try testing.expectEqual(before, registry.next); + try testing.expectEqual(count, s.groups.count()); + try testing.expectEqual(task_count, s.tasks.count()); + try testing.expectEqual(@as(u32, 22), (try registry.lookup(0, resource)).backend); + return err; + }; + } + s.stop(0, cancelled(8)); + while (s.tasks.count() != 0) { + var it = s.tasks.keyIterator(); + const child = it.next().?.*; + _ = try finish(&s, child, ok(0), .{}); + } + _ = try finish(&s, 0, ok(0), .{}); +} +test "allocation failures never expose partly published tasks or moved handles" { + try testing.checkAllAllocationFailures(testing.allocator, allocationProbe, .{}); +} +fn capture(list: *std.ArrayList(u32), value: u32) !void { + try list.append(testing.allocator, value); +} +test "GC root visitation drops consumed results and repeated tasks retain no history" { + var registry = try S.Registry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(testing.allocator, &registry, 90, 91); + defer s.deinit(); + var roots: std.ArrayList(u32) = .empty; + defer roots.deinit(testing.allocator); + for (0..2048) |_| { + const child = (try s.spawn(0, true, true, &.{})).scope; + _ = try finish(&s, child, ok(12345), .{}); + roots.clearRetainingCapacity(); + try s.visitPayloads(&roots, capture); + try testing.expect(std.mem.indexOfScalar(u32, roots.items, 12345) != null); + try s.claimJoin(0, taskToken(&s, child)); + try testing.expectEqual(@as(u32, 12345), (try s.pollJoin(0)).?.value); + roots.clearRetainingCapacity(); + try s.visitPayloads(&roots, capture); + try testing.expectEqualSlices(u32, &.{ 90, 91 }, roots.items); + try testing.expectEqual(@as(u32, 0), s.tasks.count()); + try testing.expectEqual(@as(u32, 1), s.groups.count()); + } + _ = try finish(&s, 0, ok(0), .{}); +} + +test "cancelled primaries survive cleanup and supervised discarded cleanup stays contained" { + var registry = try S.Registry.init(testing.allocator, 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(testing.allocator, &registry, 90, 91); + defer s.deinit(); + const discarded = (try s.spawn(0, true, false, &.{})).scope; + _ = try finish(&s, discarded, ok(3), S.Diagnostics.one(8)); + try testing.expect(s.groups.get(0).?.reason == null); + try testing.expectEqual(@as(usize, 0), s.groups.get(0).?.cleanup.len); + const outcome = try finish(&s, 0, cancelled(19), S.Diagnostics.one(20)); + try testing.expectEqual(.cancelled, outcome.kind); + try testing.expectEqual(@as(u32, 19), outcome.value); + try testing.expectEqualSlices(u32, &.{20}, outcome.cleanup.items[0..outcome.cleanup.len]); +} +test "stop, host settlement, and teardown need no allocation" { + var allocator = std.testing.FailingAllocator.init(testing.allocator, .{}); + var registry = try S.Registry.init(allocator.allocator(), 1, .{ 0, 0, 0 }); + defer registry.deinit(); + var s = try S.init(allocator.allocator(), &registry, 90, 91); + defer s.deinit(); + const first = (try s.spawn(0, false, true, &.{})).scope; + const lexical = try s.nested(first); + const child = (try s.spawn(lexical, false, true, &.{})).scope; + try s.beginHost(child); + allocator.fail_index = allocator.alloc_index; + s.stop(0, cancelled(4)); + try s.bodyDone(child, ok(1)); + try testing.expect(!try s.beginRelease(child)); + try s.endHost(child); + try testing.expect(try s.beginRelease(child)); + _ = try release(&s, child, S.Diagnostics.one(7)); + _ = try finish(&s, lexical, ok(1), .{}); + _ = try finish(&s, first, ok(1), .{}); + const outcome = try finish(&s, 0, ok(1), .{}); + try testing.expectEqual(.cancelled, outcome.kind); + try testing.expectEqualSlices(u32, &.{7}, outcome.cleanup.items[0..outcome.cleanup.len]); + try testing.expectEqual(@as(u32, 0), registry.resources.count()); + try testing.expectEqual(@as(u32, 0), registry.scopes.count()); +} diff --git a/ext/zig/src/io_v2_utf8.zig b/ext/zig/src/io_v2_utf8.zig @@ -0,0 +1,63 @@ +//! Stateless strict UTF-8 framing. A successful nonfinal scan returns the complete +//! prefix length; the remaining <=3 bytes are the caller-owned carry. Invalid +//! available continuation bytes fail immediately, not after an eventual EOF. +const std = @import("std"); +pub const Fault = struct { offset: usize, kind: enum { leading, sequence, incomplete } }; +pub const Scan = union(enum) { prefix: usize, fault: Fault, too_large }; +pub fn scan(bytes: []const u8, final: bool) Scan { + if (bytes.len > 4096) return .too_large; + var offset: usize = 0; + while (offset < bytes.len) { + const lead = bytes[offset]; + if (lead <= 0x7f) { + offset += 1; + continue; + } + var length: usize = undefined; + var low: u8 = 0x80; + var high: u8 = 0xbf; + if (lead >= 0xc2 and lead <= 0xdf) length = 2 else if (lead >= 0xe0 and lead <= 0xef) { + length = 3; + if (lead == 0xe0) low = 0xa0; + if (lead == 0xed) high = 0x9f; + } else if (lead >= 0xf0 and lead <= 0xf4) { + length = 4; + if (lead == 0xf0) low = 0x90; + if (lead == 0xf4) high = 0x8f; + } else return .{ .fault = .{ .offset = offset, .kind = .leading } }; + const available = @min(length - 1, bytes.len - offset - 1); + for (1..available + 1) |i| { + const byte = bytes[offset + i]; + if (byte < (if (i == 1) low else 0x80) or byte > (if (i == 1) high else 0xbf)) + return .{ .fault = .{ .offset = offset, .kind = .sequence } }; + } + if (bytes.len - offset < length) return if (final) .{ .fault = .{ .offset = offset, .kind = .incomplete } } else .{ .prefix = offset }; + offset += length; + } + return .{ .prefix = offset }; +} +test "every Unicode scalar and every valid incomplete prefix" { + for (0..0x110000) |number| { + if (number >= 0xd800 and number <= 0xdfff) continue; + var bytes: [4]u8 = undefined; + const n = try std.unicode.utf8Encode(@intCast(number), &bytes); + try std.testing.expectEqual(@as(usize, n), scan(bytes[0..n], true).prefix); + for (1..n) |prefix| { + try std.testing.expectEqual(@as(usize, 0), scan(bytes[0..prefix], false).prefix); + try std.testing.expectEqual(Fault{ .offset = 0, .kind = .incomplete }, scan(bytes[0..prefix], true).fault); + } + } +} +test "strict malformed prefix classification, byte offsets and bounded scan" { + const t = std.testing; + for ([_][]const u8{ "\xc0", "\xc1", "\x80", "\xf5", "\xff" }) |bytes| { + try t.expectEqual(Fault{ .offset = 0, .kind = .leading }, scan(bytes, false).fault); + } + for ([_][]const u8{ "\xe0\x80", "\xed\xa0", "\xf0\x80", "\xf4\x90", "\xc2A", "\xe1\x80A" }) |bytes| { + try t.expectEqual(Fault{ .offset = 0, .kind = .sequence }, scan(bytes, false).fault); + } + try t.expectEqual(Fault{ .offset = 3, .kind = .leading }, scan("abc\xff", true).fault); + try t.expectEqual(@as(usize, 4), scan("abc\x00\xf0\x90", false).prefix); + try t.expectEqual(@as(usize, 0), scan("", true).prefix); + try t.expect(scan(&([_]u8{0} ** 4097), false) == .too_large); +} diff --git a/ext/zig/src/main.zig b/ext/zig/src/main.zig @@ -5,7 +5,7 @@ const reduce = @import("reduce.zig"); const codecs = @import("codecs.zig"); const kernel = @import("kernel.zig"); const bundle = @import("bundle.zig"); -const io_driver = @import("io_driver.zig"); +const io_v2 = @import("io_v2_native.zig"); fn writeStdout(bytes: []const u8) !void { var offset: usize = 0; @@ -75,7 +75,7 @@ fn runNative(arena: *Arena, tag: u64, bundle_bytes: []const u8, args_raw: []cons try printNode(arena, tag, result, io); } -fn runIO(arena: *Arena, tag: u64, bundle_bytes: []const u8, args_raw: []const []const u8, fuel: u64, perms: io_driver.IOPerms, io: std.Io) !void { +fn runIO(arena: *Arena, tag: u64, bundle_bytes: []const u8, args_raw: []const []const u8, fuel: u64, io: std.Io) !void { const term = try bundle.loadBundleDefaultRoot(arena, bundle_bytes); var current = term; @@ -91,14 +91,7 @@ fn runIO(arena: *Arena, tag: u64, bundle_bytes: []const u8, args_raw: []const [] defer arena.gc_enabled = false; const reduced = try reduce.reduce(current, arena, fuel); - if (try io_driver.isIOSentinel(arena, reduced) == null) { - var stderr = std.Io.File.stderr().writer(io, &[_]u8{}); - try stderr.interface.writeAll("Error: reduced term is not a valid IO program\n"); - try stderr.flush(); - std.process.exit(1); - } - - const result = try io_driver.runIO(arena.allocator, arena, reduced, perms); + const result = try io_v2.run(arena, reduced, fuel); try arena.roots.append(arena.allocator, result); try printNode(arena, tag, result, io); } @@ -154,6 +147,7 @@ fn runBundle(arena: *Arena, tag: u64, bundle_bytes: []const u8, args_raw: []cons } fn parseArg(arena: *Arena, io: std.Io, s: []const u8) !u32 { + if (std.mem.startsWith(u8, s, "text:")) return @import("source_text.zig").fromUtf8(arena, s[5..]); if (std.mem.endsWith(u8, s, ".arboricx")) { const bundle_bytes = try std.Io.Dir.cwd().readFileAlloc(io, s, arena.allocator, .limited(10 * 1024 * 1024)); defer arena.allocator.free(bundle_bytes); @@ -178,7 +172,7 @@ pub fn main(init: std.process.Init) !void { const args = try init.minimal.args.toSlice(init.arena.allocator()); if (args.len < 2) { var stderr = std.Io.File.stderr().writer(io, &[_]u8{}); - try stderr.interface.writeAll("Usage: tricu-zig [--type TYPE] [--kernel] [--io] [--unsafe-io] [--fuel N] <bundle.arboricx> [arg1 arg2 ...]\n"); + try stderr.interface.writeAll("Usage: tricu-zig [--type TYPE] [--kernel] [--io] [--fuel N] <bundle.arboricx> [arg1 arg2 ...]\n"); try stderr.flush(); std.process.exit(1); } @@ -190,7 +184,6 @@ pub fn main(init: std.process.Init) !void { var use_kernel = false; var use_io = false; - var io_perms = io_driver.IOPerms{}; var fuel: u64 = std.math.maxInt(u64); var i: usize = 1; @@ -198,7 +191,7 @@ pub fn main(init: std.process.Init) !void { if (std.mem.eql(u8, args[i], "--type")) { if (i + 1 >= args.len) { var stderr = std.Io.File.stderr().writer(io, &[_]u8{}); - try stderr.interface.writeAll("Usage: tricu-zig --type <tree|number|bool|string|list|bytes> [--io] [--unsafe-io] [--fuel N] <bundle> [args...]\n"); + try stderr.interface.writeAll("Usage: tricu-zig --type <tree|number|bool|string|list|bytes> [--io] [--fuel N] <bundle> [args...]\n"); try stderr.flush(); std.process.exit(1); } @@ -216,12 +209,11 @@ pub fn main(init: std.process.Init) !void { } else if (std.mem.eql(u8, args[i], "--io")) { use_io = true; } else if (std.mem.eql(u8, args[i], "--unsafe-io")) { - io_perms.allow_read_all = true; - io_perms.allow_write_all = true; + return error.RetiredIOGrants; } else if (std.mem.eql(u8, args[i], "--fuel")) { if (i + 1 >= args.len) { var stderr = std.Io.File.stderr().writer(io, &[_]u8{}); - try stderr.interface.writeAll("Usage: tricu-zig --fuel <N> [--io] [--unsafe-io] <bundle> [args...]\n"); + try stderr.interface.writeAll("Usage: tricu-zig --fuel <N> [--io] <bundle> [args...]\n"); try stderr.flush(); std.process.exit(1); } @@ -242,7 +234,7 @@ pub fn main(init: std.process.Init) !void { if (bundle_idx >= args.len) { var stderr = std.Io.File.stderr().writer(io, &[_]u8{}); - try stderr.interface.writeAll("Usage: tricu-zig [--type TYPE] [--kernel] [--io] [--unsafe-io] [--fuel N] <bundle.arboricx> [arg1 arg2 ...]\n"); + try stderr.interface.writeAll("Usage: tricu-zig [--type TYPE] [--kernel] [--io] [--fuel N] <bundle.arboricx> [arg1 arg2 ...]\n"); try stderr.flush(); std.process.exit(1); } @@ -257,7 +249,7 @@ pub fn main(init: std.process.Init) !void { const call_args = if (arg_start < args.len) args[arg_start..] else &[_][]const u8{}; if (use_io) { - runIO(&arena, tag, bundle_bytes, call_args, fuel, io_perms, io) catch |err| { + runIO(&arena, tag, bundle_bytes, call_args, fuel, io) catch |err| { var stderr = std.Io.File.stderr().writer(io, &[_]u8{}); try stderr.interface.print("Execution failed: {s}\n", .{@errorName(err)}); try stderr.flush(); diff --git a/ext/zig/tests/handles_test.c b/ext/zig/tests/handles_test.c @@ -6,7 +6,7 @@ #define OK(call) assert((call) == ARB_OK) int main(void) { - assert(arb_abi_version() == 2); + assert(arb_abi_version() == 3); arb_ctx_t *ctx = arboricx_init(), *other = arboricx_init(); assert(ctx && other); assert(arb_set_gc_policy(ctx, 0, 1) == ARB_INVALID_DATA); diff --git a/ext/zig/tests/helpers.h b/ext/zig/tests/helpers.h @@ -66,12 +66,6 @@ static inline arb_handle_t checked_arb_get_stem_child(arb_ctx_t* ctx, arb_handle if (status != ARB_OK) abort(); return out; } -static inline arb_handle_t checked_arb_run_io(arb_ctx_t* ctx, arb_handle_t program, const arb_io_perms_t* perms) { - arb_handle_t out; - arb_status_t status = arb_run_io(ctx, program, perms, &out); - if (status != ARB_OK) abort(); - return out; -} static inline arb_handle_t checked_arb_kernel_root(arb_ctx_t* ctx) { arb_handle_t out; arb_status_t status = arb_kernel_root(ctx, &out); diff --git a/ext/zig/tests/io_run_test.c b/ext/zig/tests/io_run_test.c @@ -21,199 +21,40 @@ int main(void) { return 1; } - arb_io_perms_t perms = { 0, 0 }; - - /* Test 1: pure "hello" wrapped in IO sentinel */ - { - arb_handle_t hello = checked_arb_of_string(ctx, "hello"); - arb_handle_t pure_hello = make_pure(ctx, hello); - arb_handle_t program = make_io_sentinel(ctx, pure_hello); - - arb_handle_t result = checked_arb_run_io(ctx, program, &perms); - if (result == 0) { - fprintf(stderr, "FAIL: pure hello returned 0\n"); - arboricx_free(ctx); - return 1; - } - - uint8_t* decoded; - size_t decoded_len; - if (!(arb_to_string(ctx, result, &decoded, &decoded_len) == ARB_OK) || - decoded_len != 5 || memcmp(decoded, "hello", 5) != 0) { - fprintf(stderr, "FAIL: pure hello result mismatch\n"); - arboricx_free(ctx); - return 1; - } - arboricx_free_buf(ctx, decoded, decoded_len); - printf("PASS: pure hello\n"); - } - - /* Test 2: bind (pure "a") (\_ : pure "done") */ - { - arb_handle_t a = checked_arb_of_string(ctx, "a"); - arb_handle_t done = checked_arb_of_string(ctx, "done"); - arb_handle_t pure_a = make_pure(ctx, a); - arb_handle_t pure_done = make_pure(ctx, done); - - /* K pure_done = Fork Leaf pure_done */ - arb_handle_t k = checked_arb_fork(ctx, checked_arb_leaf(ctx), pure_done); - arb_handle_t bind_pair = checked_arb_fork(ctx, pure_a, k); - arb_handle_t one = checked_arb_of_number(ctx, 1); - arb_handle_t bind_action = checked_arb_fork(ctx, one, bind_pair); - arb_handle_t program = make_io_sentinel(ctx, bind_action); - - arb_handle_t result = checked_arb_run_io(ctx, program, &perms); - if (result == 0) { - fprintf(stderr, "FAIL: bind returned 0\n"); - arboricx_free(ctx); - return 1; - } - - uint8_t* decoded; - size_t decoded_len; - if (!(arb_to_string(ctx, result, &decoded, &decoded_len) == ARB_OK) || - decoded_len != 4 || memcmp(decoded, "done", 4) != 0) { - fprintf(stderr, "FAIL: bind result mismatch\n"); - arboricx_free(ctx); - return 1; - } - arboricx_free_buf(ctx, decoded, decoded_len); - printf("PASS: bind pure\n"); - } - - /* Test 3: putStr "test" (no permissions needed) */ - { - arb_handle_t test = checked_arb_of_string(ctx, "test"); - arb_handle_t ten = checked_arb_of_number(ctx, 10); - arb_handle_t putStr_action = checked_arb_fork(ctx, ten, test); - arb_handle_t program = make_io_sentinel(ctx, putStr_action); - - printf("EXPECT: test\n"); - arb_handle_t result = checked_arb_run_io(ctx, program, &perms); - if (result == 0) { - fprintf(stderr, "FAIL: putStr returned 0\n"); - arboricx_free(ctx); - return 1; - } - if (!checked_arb_is_leaf(ctx, result)) { - fprintf(stderr, "FAIL: putStr should return Leaf\n"); - arboricx_free(ctx); - return 1; - } - printf("PASS: putStr\n"); - } - - /* Test 4: readFile without permission returns err */ + /* Version-specific v2 execution; output handles remain untouched on errors. */ { - arb_handle_t path = checked_arb_of_string(ctx, "/etc/passwd"); - arb_handle_t twenty = checked_arb_of_number(ctx, 20); - arb_handle_t readFile_action = checked_arb_fork(ctx, twenty, path); - arb_handle_t program = make_io_sentinel(ctx, readFile_action); - - arb_handle_t result = checked_arb_run_io(ctx, program, &perms); - if (result == 0) { - fprintf(stderr, "FAIL: readFile denied returned 0\n"); - arboricx_free(ctx); - return 1; - } - - /* Should be an err result: Fork Leaf (Fork code Leaf) */ - arb_handle_t left, right; - if (!(arb_get_fork_children(ctx, result, &left, &right) == ARB_OK) || - !checked_arb_is_leaf(ctx, left)) { - fprintf(stderr, "FAIL: readFile denied should be err result\n"); - arboricx_free(ctx); - return 1; - } - - arb_handle_t code, rest; - if (!(arb_get_fork_children(ctx, right, &code, &rest) == ARB_OK) || - !checked_arb_is_leaf(ctx, rest)) { - fprintf(stderr, "FAIL: readFile denied err shape mismatch\n"); - arboricx_free(ctx); - return 1; - } - - uint8_t* message; - size_t message_len; - const char* expected = "permission denied"; - if (!(arb_to_string(ctx, code, &message, &message_len) == ARB_OK) || - message_len != strlen(expected) || - memcmp(message, expected, message_len) != 0) { - fprintf(stderr, "FAIL: readFile denied should carry a text error\n"); - arboricx_free(ctx); - return 1; - } - arboricx_free_buf(ctx, message, message_len); - printf("PASS: readFile denied\n"); - } - - /* Test 5: readFile with permission succeeds */ - { - /* Create a temp file first */ - const char* tmp = "/tmp/tricu_io_test.txt"; - FILE* f = fopen(tmp, "w"); - if (!f) { - fprintf(stderr, "FAIL: could not create temp file\n"); - arboricx_free(ctx); - return 1; - } - fprintf(f, "hi"); - fclose(f); - - arb_io_perms_t unsafe_perms = { 1, 0 }; - arb_handle_t path = checked_arb_of_string(ctx, tmp); - arb_handle_t twenty = checked_arb_of_number(ctx, 20); - arb_handle_t readFile_action = checked_arb_fork(ctx, twenty, path); - arb_handle_t program = make_io_sentinel(ctx, readFile_action); - - arb_handle_t result = checked_arb_run_io(ctx, program, &unsafe_perms); - if (result == 0) { - fprintf(stderr, "FAIL: readFile allowed returned 0\n"); - arboricx_free(ctx); - return 1; - } - - /* Should be ok result: Fork (Stem Leaf) (Fork val Leaf) */ - arb_handle_t ok_tag, ok_rest; - if (!(arb_get_fork_children(ctx, result, &ok_tag, &ok_rest) == ARB_OK) || - !checked_arb_is_stem(ctx, ok_tag)) { - fprintf(stderr, "FAIL: readFile allowed should be ok result\n"); - arboricx_free(ctx); - return 1; - } - - arb_handle_t val, leaf; - if (!(arb_get_fork_children(ctx, ok_rest, &val, &leaf) == ARB_OK) || - !checked_arb_is_leaf(ctx, leaf)) { - fprintf(stderr, "FAIL: readFile allowed ok shape mismatch\n"); - arboricx_free(ctx); - return 1; - } - - uint8_t* decoded; - size_t decoded_len; - if (!(arb_to_string(ctx, val, &decoded, &decoded_len) == ARB_OK) || - decoded_len != 2 || memcmp(decoded, "hi", 2) != 0) { - fprintf(stderr, "FAIL: readFile allowed contents mismatch\n"); - arboricx_free(ctx); - return 1; - } - arboricx_free_buf(ctx, decoded, decoded_len); - printf("PASS: readFile allowed\n"); - } - - /* Test 6: invalid sentinel returns 0 */ - { - arb_handle_t bad = checked_arb_fork(ctx, checked_arb_leaf(ctx), checked_arb_leaf(ctx)); - arb_handle_t result = 123; - arb_status_t failure = arb_run_io(ctx, bad, &perms, &result); - if (failure != ARB_IO_ERROR || result != 123) { - fprintf(stderr, "FAIL: invalid sentinel should return 0\n"); - arboricx_free(ctx); - return 1; - } - printf("PASS: invalid sentinel\n"); + arb_handle_t payload = checked_arb_of_number(ctx, 42); + arb_handle_t action = make_pure(ctx, payload); + arb_handle_t v1 = make_io_sentinel(ctx, action); + arb_handle_t v2 = checked_arb_fork(ctx, checked_arb_of_string(ctx, "tricuIO"), + checked_arb_fork(ctx, checked_arb_of_number(ctx, 2), action)); + arb_handle_t out = 123; + uint64_t value = 0; + if (arb_run_io_v2(ctx,v2,&out) != ARB_OK || arb_to_number(ctx,out,&value) != ARB_OK || value != 42) return 1; + if (arb_release(ctx,out) != ARB_OK) return 1; + out = 123; + if (arb_run_io_v2(ctx,v1,&out) != ARB_IO_ERROR || out != 123) return 1; + if (arb_run_io_v2(ctx,0,&out) != ARB_INVALID_HANDLE || out != 123) return 1; + arb_handle_t malformed = checked_arb_fork(ctx, checked_arb_of_string(ctx, "notIO"), + checked_arb_fork(ctx, checked_arb_of_number(ctx, 2), action)); + if (arb_run_io_v2(ctx,malformed,&out) != ARB_IO_ERROR || out != 123) return 1; + for (uint64_t tag = 0; tag <= 100; tag++) { + arb_handle_t retired = make_io_sentinel(ctx, + checked_arb_fork(ctx, checked_arb_of_number(ctx, tag), payload)); + if (arb_run_io_v2(ctx,retired,&out) != ARB_IO_ERROR || out != 123) return 1; + } + /* Non-normal terminal payloads must not become successful Results. */ + arb_handle_t inert = checked_arb_app(ctx, checked_arb_leaf(ctx), checked_arb_leaf(ctx)); + for (uint64_t tag = 2; tag <= 9; tag += 7) { + arb_handle_t terminal = checked_arb_fork(ctx, checked_arb_of_string(ctx, "tricuIO"), + checked_arb_fork(ctx, checked_arb_of_number(ctx, 2), + checked_arb_fork(ctx, checked_arb_of_number(ctx, tag), inert))); + if (arb_run_io_v2(ctx,terminal,&out) != ARB_IO_ERROR || out != 123) return 1; + } + /* A failed run does not poison subsequent valid runs in this context. */ + if (arb_run_io_v2(ctx,v2,&out) != ARB_OK || arb_to_number(ctx,out,&value) != ARB_OK || value != 42) return 1; + if (arb_release(ctx,out) != ARB_OK) return 1; + printf("PASS: explicit v2 ABI and no fallback\n"); } arboricx_free(ctx); diff --git a/ext/zig/tests/performance_smoke.c b/ext/zig/tests/performance_smoke.c @@ -1,4 +1,4 @@ -/* Correctness-checked ABI v2 performance smoke. No compiler execution. +/* Correctness-checked ABI v3 performance smoke. No compiler execution. * Times exclude context/kernel loading; process peak RSS includes everything. * The library's optimization mode is specified by the Nix package, not gcc. */ #define _POSIX_C_SOURCE 200809L @@ -47,7 +47,7 @@ static arb_handle_t load(arb_ctx_t *ctx, const char *path) { free(buf); return h; } int main(int argc, char **argv) { - CHECK(argc == 5); CHECK(arb_abi_version() == 2); + CHECK(argc == 5); CHECK(arb_abi_version() == 3); // Apply limits before runtime initialization without a Python preexec hook. struct rlimit memory = {512 * 1024 * 1024, 512 * 1024 * 1024}; struct rlimit stack = {8 * 1024 * 1024, 8 * 1024 * 1024}; diff --git a/ext/zig/tests/python_ffi_test.py b/ext/zig/tests/python_ffi_test.py @@ -85,7 +85,8 @@ lib.arb_release.restype = ctypes.c_int lib.arb_collect.argtypes = [ctypes.c_void_p] lib.arb_collect.restype = ctypes.c_int lib.arb_abi_version.restype = ctypes.c_uint32 -assert lib.arb_abi_version() == 2 +assert lib.arb_abi_version() == 3 +assert not hasattr(lib, "arb_run_io"), "retired v1 entrypoint remains exported" def owned(name, *args): out = ctypes.c_uint64() @@ -278,24 +279,50 @@ from pathlib import Path sys.path.insert(0, os.path.join(ZIG_DIR, "..", "..", "test")) from executable_object import indexed_manifest, verify_payload, INDEXED -class Perms(ctypes.Structure): - _fields_ = [("read", ctypes.c_int), ("write", ctypes.c_int)] -lib.arb_run_io.argtypes = [ctypes.c_void_p, ctypes.c_uint64, ctypes.POINTER(Perms), ctypes.POINTER(ctypes.c_uint64)] -lib.arb_run_io.restype = ctypes.c_int +lib.arb_run_io_v2.argtypes = [ctypes.c_void_p, ctypes.c_uint64, ctypes.POINTER(ctypes.c_uint64)] +lib.arb_run_io_v2.restype = ctypes.c_int +lib.arb_get_fork_children.argtypes = [ctypes.c_void_p, ctypes.c_uint64, + ctypes.POINTER(ctypes.c_uint64), ctypes.POINTER(ctypes.c_uint64)] +lib.arb_get_fork_children.restype = ctypes.c_int def pair(a, b): return owned("arb_fork", ctx, a, b) -def action(tag, payload): +def fork_children(value): + a, b = ctypes.c_uint64(), ctypes.c_uint64() + assert lib.arb_get_fork_children(ctx, value, ctypes.byref(a), ctypes.byref(b)) == 0 + return a.value, b.value + +def execute(action): program = pair(owned("arb_of_string", ctx, b"tricuIO"), - pair(owned("arb_of_number", ctx, 1), - pair(owned("arb_of_number", ctx, tag), payload))) - result = owned("arb_run_io", ctx, program, ctypes.byref(Perms(1, 1))) + pair(owned("arb_of_number", ctx, 2), action)) + result = owned("arb_run_io_v2", ctx, program) ok, value, rest = ctypes.c_int(), ctypes.c_uint64(), ctypes.c_uint64() assert lib.arb_unwrap_result(ctx, result, ctypes.byref(ok), ctypes.byref(value), ctypes.byref(rest)) == 0 - assert ok.value, to_string(ctx, value.value) + assert ok.value, "v2 operation failed" return value.value +# Only the scoped storage composition comes from the authenticated compiler +# library. The canonical DAG and expected bytes/digest below remain independent. +fixture = (Path(ZIG_DIR).parent.parent / 'test/fixtures/compiler-library.arboricx').read_bytes() +def export(name): + return owned('arb_load_bundle', ctx, c_bytes(fixture), len(fixture), name.encode()) +publish_tree = export('io_v2_storage.publishTree') +read_tree = export('io_v2_storage.readTree') +def call(function, *arguments): + for argument in arguments: + function = owned('arb_app', ctx, function, argument) + return execute(function) +limit = owned('arb_of_number', ctx, 1048576) +def hash_tree(value): + # Independently encode indexed-tree revision 1 hash request. + request = pair(owned('arb_of_string', ctx, b'indexed-tree'), + pair(owned('arb_of_number', ctx, 1), pair(owned('arb_of_string', ctx, b'hash'), pair(limit, value)))) + info = execute(pair(owned('arb_of_number', ctx, 10), request)) + size, tail = fork_children(info) + digest, _ = fork_children(tail) + return to_number(ctx, size), to_string(ctx, digest) + rng = random.Random(510052) with tempfile.TemporaryDirectory() as directory: for case in range(24): @@ -329,16 +356,17 @@ with tempfile.TemporaryDirectory() as directory: expected += manifest + nodes verify_payload(INDEXED, expected) digest = hashlib.sha256(INDEXED.encode() + b'\0' + expected).hexdigest() - check(f"indexed independent hash {case}", to_string(ctx, action(51, handles[-1])), digest) + check(f"indexed independent hash {case}", hash_tree(handles[-1])[1], digest) path = Path(directory) / str(case) path_tree = owned("arb_of_string", ctx, str(path).encode()) - hash_tree = owned("arb_of_string", ctx, digest.encode()) - write = pair(path_tree, pair(hash_tree, handles[-1])) - assert to_bool(ctx, action(52, write)) is True + hash_tree_ = owned("arb_of_string", ctx, digest.encode()) + directory_tree = owned('arb_of_string', ctx, directory.encode()) + assert hash_tree(handles[-1])[0] == len(expected) + assert to_bool(ctx, call(publish_tree, directory_tree, path_tree, limit, hash_tree_, handles[-1])) is True assert path.read_bytes() == expected - assert to_bool(ctx, action(52, write)) is False - decoded = action(50, pair(path_tree, hash_tree)) - assert to_string(ctx, action(51, decoded)) == digest + assert to_bool(ctx, call(publish_tree, directory_tree, path_tree, limit, hash_tree_, handles[-1])) is False + decoded = call(read_tree, directory_tree, path_tree, limit, hash_tree_) + assert hash_tree(decoded) == (len(expected), digest) # Malformed bundle failure must not publish or clobber the output handle. bad = expected[:31] out = ctypes.c_uint64(123456) diff --git a/flake.nix b/flake.nix @@ -71,11 +71,11 @@ # Compile once with the packaged stable compiler. Runtime needs neither # source lookup nor a writable compiler/application cache. - arboricxServerBundle = pkgs.runCommand "arboricx-server-bundle" { + mkRegistryBundle = name: source: pkgs.runCommand "${name}-bundle" { nativeBuildInputs = [ tricuPackage ]; } '' mkdir -p $out - tricu arboricx compile --file ${./.}/tricu-apps/arboricxServer.tri --store build-store \ + tricu arboricx compile --file ${source} --store build-store \ --output $out/server.arboricx --compile-timeout 900 \ +RTS -N1 -M8192m -RTS tricu arboricx import --file $out/server.arboricx --store $out/runtime \ @@ -85,13 +85,34 @@ grep -Eq '^[0-9a-f]{64}$' $out/manifest ''; - arboricxServerRunner = pkgs.writeShellScriptBin "arboricx-server" '' + arboricxServerBundle = mkRegistryBundle "arboricx-server" "${./.}/tricu-apps/arboricxServer.tri"; + arboricxServerRunner = mkRegistryV2Runner "arboricx-server" arboricxServerBundle; + arboricxStreamingV2Bundle = mkRegistryBundle "arboricx-streaming-v2" "${./.}/tricu-apps/arboricxStreamingV2.tri"; + mkRegistryV2Runner = name: bundle: pkgs.writeShellScriptBin name '' exec ${tricuPackage}/bin/tricu arboricx run \ - --module "$(${pkgs.coreutils}/bin/cat ${arboricxServerBundle}/manifest)" --name main \ - --store ${arboricxServerBundle}/runtime \ - --io --no-result --allow-read ./store --allow-write ./store \ + --module "$(${pkgs.coreutils}/bin/cat ${bundle}/manifest)" --name main \ + --store ${bundle}/runtime --io --no-result \ +RTS -N1 -M2048m -RTS ''; + arboricxStreamingV2Runner = mkRegistryV2Runner "arboricx-streaming-v2" arboricxStreamingV2Bundle; + arboricxStreamingV2FaultBundle = mkRegistryBundle "arboricx-streaming-v2-fault" "${./.}/test/io-v2/registry-log-failure.tri"; + arboricxStreamingV2FaultRunner = mkRegistryV2Runner "arboricx-streaming-v2-fault" arboricxStreamingV2FaultBundle; + mkRegistryV2Test = name: runner: pkgs.runCommand "${name}-test" { + nativeBuildInputs = [ pkgs.python3 ]; + } '' + mkdir -p $out + python3 ${./test/arboricx_deployment.py} ${runner}/bin/${name} \ + --io-version 2 --handler-policy \ + --fault-runner ${arboricxStreamingV2FaultRunner}/bin/arboricx-streaming-v2-fault \ + --report $out/report.json + python3 ${./test/streaming_protocol.py} ${runner}/bin/${name} \ + --io-version 2 > $out/protocol.txt + PYTHONDONTWRITEBYTECODE=1 python3 ${./.}/test/body_retention.py \ + --runner ${runner}/bin/${name} \ + --phases streaming-publish streaming-download --bounded-streaming \ + --sizes 65536 1048576 --repeats 3 \ + --directory $out/retention > $out/retention-summary.json + ''; tricuBench = (hsLib.overrideCabal @@ -248,6 +269,10 @@ test "$(tricu-zig --type string ../../test/fixtures/id.arboricx hello)" = hello test "$(tricu-zig --type string --kernel ../../test/fixtures/append.arboricx 'Hello, ' 'world!')" = 'Hello, world!' + # Retired grants fail before even trying to open a bundle. + if tricu-zig --unsafe-io /definitely-missing >grant.log 2>&1; then exit 1; fi + grep -q RetiredIOGrants grant.log + # C ABI smoke test gcc -o /tmp/c_abi_test tests/c_abi_test.c \ -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \ @@ -531,6 +556,15 @@ --seeder ${./ext/demo-site/seed.py} mkdir -p $out ''; + checks.rtsMetrics = pkgs.runCommand "tricu-rts-metrics-tests" { + nativeBuildInputs = [ pkgs.python3 ]; + } '' + export PYTHONDONTWRITEBYTECODE=1 + cd ${tricuSource} + timeout 30s python3 test/rts_metrics_test.py + timeout 30s python3 test/process_rss_test.py + mkdir -p $out + ''; checks.bootstrapRetention = pkgs.runCommand "tricu-bootstrap-retention-tests" { nativeBuildInputs = [ pkgs.python3 ]; } '' @@ -597,13 +631,10 @@ }; }; - # Opt-in: compiles and exercises the actual deployment entrypoint. - packages.arboricxServerTest = pkgs.runCommand "arboricx-server-test" { - nativeBuildInputs = [ pkgs.python3 ]; - } '' - python3 ${./test/arboricx_deployment.py} ${arboricxServerRunner}/bin/arboricx-server - mkdir -p $out - ''; + # Opt-in acceptance runs the actual default entrypoint and common v2 gates. + packages.arboricxServerTest = mkRegistryV2Test "arboricx-server" arboricxServerRunner; + packages.arboricxStreamingV2Runner = arboricxStreamingV2Runner; + packages.arboricxStreamingV2Test = mkRegistryV2Test "arboricx-streaming-v2" arboricxStreamingV2Runner; packages.arboricxServerBundle = arboricxServerBundle; packages.arboricxServerRunner = arboricxServerRunner; packages.arboricxServer = pkgs.dockerTools.buildImage { diff --git a/lib/arboricx/handlers.tri b/lib/arboricx/handlers.tri @@ -0,0 +1,108 @@ +!import base as B +!import http as HTTP +!import conversions as Convert +!import arboricx_server as Store +!import handlers as H +!import io_v2 as V2 +!import io_v2_storage as Storage +!import handler_log as Log +!import handler_artifacts as Artifacts +!export request +!export packageLookupV2 +!export stdoutLogV2 +!export writeLogV2 +!export routerV2 + +-- HTTP policy is separate from storage, logging, and the socket lifecycle. +-- Outcomes carry a status beside the complete response so logging does not +-- parse response bytes. Only completed application routes emit access events; +-- parser/protocol rejections remain owned by the scoped HTTP transport. +reply status bytes = H.pure [status bytes] +error status message = reply status (HTTP.errorResponse status message) + +load digest = do H.bind + result <- Artifacts.load digest + case result of + ((t t), (bytes, t)) = reply 200 (HTTP.response 200 Store.bundleContentType bytes) + (t, (["invalid-digest" message], t)) = error 400 message + _ = error 404 "Artifact unavailable\n" + +publish bytes = do H.bind + result <- Artifacts.publish bytes + case result of + ((t t), (digest, t)) = reply 201 (HTTP.createdResponse digest) + _ = error 400 "Upload failed\n" + +-- Package storage is an explicit captured service: its identity is stable for +-- this server. Contextual artifact overrides need no package-path authority. +-- The HTTP parser supplies canonical target bytes, as in the original server. + +package lookup target = do H.bind + result <- lookup target + case result of + ((t t), (bytes, t)) = reply 200 (HTTP.response 200 "application/vnd.arboricx.package-release" bytes) + (t, (["invalid-package" message], t)) = error 400 message + _ = error 404 "Package release not found" + +get lookup target = case Store.stripPrefix Store.packagePrefix target of + (t suffix) = package lookup suffix + _ = case Store.stripPrefix Store.bundleHashPrefix target of + (t digest) = load digest + _ = reply 404 HTTP.notFoundResponse + +route _ "GET" "/" _ _ = reply 200 (HTTP.htmlResponse Store.htmlLandingPage) +route _ "GET" "/_arboricx/health" _ _ = reply 200 (HTTP.okResponse "OK\n") +route lookup "GET" target _ _ = get lookup target +route _ "POST" "/_arboricx/bundle" _ body = publish body +route _ _ _ _ _ = reply 404 HTTP.notFoundResponse + +request lookup method target headers body = H.label "registry-request" <| do H.bind + outcome <- route lookup method target headers body + case outcome of + [status response] = do H.bind + logged <- Log.info status + case logged of + ((t t), (t, t)) = H.pure response + (t, (message, t)) = H.fail "application-failure" "logging" "emit" message + _ = H.fail "application-failure" "logging" "emit" "Invalid logger reply" + _ = H.fail "application-failure" "registry" "route" "Invalid route outcome" + +-- The captured package service remains independent of contextual artifact routes. +packageLookupV2 root limit target = case Store.packageTarget target of + (t (name, version)) = H.liftIO (Storage.readBytes + (Store.pathJoin (Store.pathJoin (Store.pathJoin root "packages") name) version) limit) + _ = H.pure (B.err ["invalid-package" "Invalid package name/version"] t) + +-- Status-only ASCII logging. Ordinary write failures remain Results; publication +-- precedes logging and remains visible if the logger fails. +writeLogV2 bytes = do V2.onOk + writer <- V2.stdout + V2.writeAll writer bytes + V2.pure (B.ok t t) +stdoutLogV2 ["info" fields status] = case B.checkContract B.nat? status of + ((t t), _) = H.liftIO (writeLogV2 + (B.append "registry status=" (B.append (Convert.showNumber status) "\n"))) + _ = H.pure (B.err "Invalid access-log status" t) +stdoutLogV2 _ = H.pure (B.err "Invalid access-log event" t) + +-- A supervised child contains request interpretation/logger failure without +-- weakening native fail-fast semantics or retaining completed task tickets. +-- Cancellation remains cancellation; a failure becomes a redacted HTTP response. +-- The listener owns sockets and framing. +routerV2 root limit sink method target headers body = V2.scope (_ : do V2.require + spawned <- V2.spawn 1 1 [] (_ : H.runIOV2 + (request (packageLookupV2 root limit) method target headers body + |> Log.withContext [["method" method] ["target" target]] + |> Artifacts.withFilesystemV2 root limit + |> Log.withSink sink)) + case spawned of + [task ticket] = do V2.require + outcome <- V2.join ticket + case outcome of + ["completed" response] = V2.pure response + ["failed" _ _] = do V2.bind + writeLogV2 "registry request failed\n" + V2.pure (HTTP.errorResponse 500 "Internal Server Error\n") + ["cancelled" reason _] = V2.cancelled reason + _ = V2.fail "Invalid registry task outcome" + _ = V2.fail "Invalid registry spawn reply") diff --git a/lib/arboricx/inspect.tri b/lib/arboricx/inspect.tri @@ -13,7 +13,7 @@ -- Each export record is (UTF-8 name bytes, (u32 root bytes, (kind bytes, ABI bytes))). -- The decoder's remainder is an internal section cursor, NOT unconsumed input; -- success deliberately returns an empty remainder. Failure remainders retain --- opaque diagnostic context, not an incremental parsing continuation. +-- opaque diagnostic context. inspectBundle bytes = (bindResult (A.readArboricxDecoded bytes) (decoded _ : ok (pair (C.u64BEBytesToNat (Nodes.nodesSectionCount (snd decoded))) @@ -39,8 +39,8 @@ renderExports [(name, (root, (kind, abi))) | rest] = (append "export\t" (append "\n" (renderExports rest)))))))))) renderExports _ = "" --- Accepts a successful inspectBundle summary, not an arbitrary unvalidated tree. --- Count stored records, never unfold logical occurrences of shared branches. +-- Requires a successful inspectBundle summary. Counts represent stored records; +-- shared branches contribute once per stored record. renderSummary summary = (append "stored-nodes\t" (append (showNumber (fst summary)) (append "\n" (renderExports (snd summary))))) diff --git a/lib/arboricx/manifest.tri b/lib/arboricx/manifest.tri @@ -250,8 +250,8 @@ validateManifestCore = (core rest : (and? (manifestRootsValid? (manifestRoots core)) (manifestExportsValid? (manifestExports core))))))))))))))))) --- Export names are UTF-8 byte strings, never filesystem paths. --- Validate every export before selection; never silently replace kind or ABI. +-- Export names are UTF-8 byte strings interpreted as symbols. +-- Validate every export before selection and preserve its declared kind and ABI. manifestExportsValid? exports = case exports of [] = true [(name, (root, (kind, abi))) | rest] = (and? (not? (equal? name [])) diff --git a/lib/arboricx/server.tri b/lib/arboricx/server.tri @@ -1,12 +1,10 @@ !import prelude * -!import io * !import http * -!import socket * !import native_math as Nat !export * --- Store root is trusted configuration. These pure path helpers are not guards; --- every read/write entrypoint must validate a hash before using it as a path. +-- Store root is trusted configuration. Every read/write entrypoint must validate +-- a hash before passing it to these pure path constructors. pathJoin parent child = append parent (append "/" child) objectDir root shard = pathJoin (pathJoin root "objects") shard @@ -19,21 +17,16 @@ bundleObjectPath root hash = pathJoin (objectDir root (hashShard hash)) (append -- Exact canonical lowercase SHA-256 spelling. Literal patterns, unlike numeric -- range predicates, reject malformed numeral trees without normalizing them. -bundleHexByte? 48 = true -bundleHexByte? 49 = true -bundleHexByte? 50 = true -bundleHexByte? 51 = true -bundleHexByte? 52 = true -bundleHexByte? 53 = true -bundleHexByte? 54 = true -bundleHexByte? 55 = true -bundleHexByte? 56 = true -bundleHexByte? 57 = true +-- Disjoint compact-bit ranges: 48..55, 56..57, 97, 98..99, +-- 100..101, 102. Every captured bit is checked as an exact Boolean. +bundleBit? t = true +bundleBit? (t t) = true +bundleBit? _ = false +bundleHexByte? (a, (b, (c, 6))) = and? (bundleBit? a) (and? (bundleBit? b) (bundleBit? c)) +bundleHexByte? (a, 28) = bundleBit? a bundleHexByte? 97 = true -bundleHexByte? 98 = true -bundleHexByte? 99 = true -bundleHexByte? 100 = true -bundleHexByte? 101 = true +bundleHexByte? (a, 49) = bundleBit? a +bundleHexByte? (a, 50) = bundleBit? a bundleHexByte? 102 = true bundleHexByte? _ = false @@ -47,48 +40,21 @@ bundleHashBytes [byte | rest] remaining = case (bundleHexByte? byte) of bundleHashBytes _ _ = false -bundleHashValid? hash = bundleHashBytes hash 64 +-- The fixed SHA-256 boundary uses structural fuel instead of subtracting a +-- compact-natural counter per character. Retain bundleHashBytes for callers +-- that explicitly supply a length. Both stop before inspecting a 65th element. +bundleHashFuel8 tail = t (t (t (t (t (t (t (t tail))))))) +bundleHashFuel64 = bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 + (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 t))))))) +bundleHashScan [] t = true +bundleHashScan _ t = false +bundleHashScan [byte | rest] (t fuel) = case bundleHexByte? byte of + ^true = bundleHashScan rest fuel + _ = false +bundleHashScan _ _ = false +bundleHashValid? hash = bundleHashScan hash bundleHashFuel64 bundleHashC = guardC "expected 64 lowercase hexadecimal hash characters" bundleHashValid? --- Directory creation already tolerates existing directories in the Haskell --- driver. Propagate real failures and stop; void would discard those failures. -ensureDir path = mapErrIO "createDirectory: " (createDirectory path) -ensureStore root = (do onOk_ - ensureDir root - ensureDir (pathJoin root "tmp") - ensureDir (pathJoin root "objects") - ensureDir (pathJoin root "aliases") - ensureDir (pathJoin (pathJoin root "aliases") "names") - ensureDir (pathJoin (pathJoin root "aliases") "packages") - ensureDir (pathJoin root "manifests") - pure (ok t t)) - --- Hash opaque bytes ourselves: no public unchecked hash-taking writer. Validate --- even the digest reply before constructing filesystem actions. Atomic writes --- use host-managed unique staging in the destination directory, not hash.tmp. --- Existing-object replacement remains supported. A post-publication sync error --- can leave the new object visible; this is not rollback or bundle validation. -putBundle root bytes = (do onOk_ - hash <- mapErrIO "sha256Hex: " (sha256Hex bytes) - case (checkContract bundleHashC hash) of - ((t t), (value, _)) = (do onOk_ - ensureDir (objectDir root (hashShard value)) - mapErrIO "atomicWriteBytes: " (atomicWriteBytes (bundleObjectPath root value) bytes) - pure (ok value t)) - (t, (diagnostic, _)) = pure (err (append "sha256Hex: " diagnostic) t) - _ = pure (err "sha256Hex: Invalid hash contract result" t)) - --- Translate contract failures before IO, preserving HTTP status/message roles. -getBundleByHash root hash = case (checkContract bundleHashC hash) of - ((t t), (value, _)) = (do bind - reply <- readFile (bundleObjectPath root value) - case reply of - ((t t), (bytes, _)) = pure (ok bytes t) - (t, (message, _)) = pure (err 404 message) - _ = pure (err 404 "Invalid read result")) - (t, (diagnostic, _)) = pure (err 400 diagnostic) - _ = pure (err 400 "Invalid hash contract result") - -- A proper list prefix; return its exact suffix, without normalizing the tail. stripPrefix [] input = just input stripPrefix [wanted | prefix] [byte | rest] = case byte of @@ -106,31 +72,6 @@ htmlLandingPage = "<!DOCTYPE html><html><head><meta name='viewport' content='wid bundleResponse bytes = response 200 bundleContentType bytes -serveBundleHash root hash = (do bind - reply <- getBundleByHash root hash - case reply of - ((t t), (bytes, _)) = pure (bundleResponse bytes) - (t, (status, message)) = pure (errorResponse status message) - _ = pure (errorResponse 500 "Invalid bundle result")) - -healthRoute "GET" ^healthPath = pure (okResponse "OK\n") -healthRoute _ _ = pure notFoundResponse - -putBundleRoute root "POST" ^bundlePath body = (do bind - reply <- putBundle root body - case reply of - ((t t), (hash, _)) = pure (createdResponse hash) - (t, (message, _)) = pure (badRequestResponse (append "Upload failed: " message)) - _ = pure (errorResponse 500 "Invalid upload result")) - -putBundleRoute _ _ _ _ = pure notFoundResponse - -getBundleRoute root "GET" target = case (stripPrefix bundleHashPrefix target) of - (t hash) = serveBundleHash root hash - _ = pure notFoundResponse - -getBundleRoute _ _ _ = pure notFoundResponse - -- Read-only exact-version index. Administrative publication happens locally; -- this route never resolves dependencies or executes fetched code. HTTP input -- bytes are canonical naturals. Scan at most 128 bytes per path component. @@ -163,37 +104,3 @@ packageTarget [first | rest] = case (packageAlphaNum? first) of _ = nothing packageTarget _ = nothing - -servePackage root target = case (packageTarget target) of - (t (name, version)) = (do bind - reply <- readFile (pathJoin (pathJoin (pathJoin root "packages") name) version) - case reply of - ((t t), (bytes, _)) = pure (response 200 "application/vnd.arboricx.package-release" bytes) - _ = pure (errorResponse 404 "Package release not found")) - _ = pure (badRequestResponse "Invalid package name/version") - -getRegistryRoute root target = case (stripPrefix packagePrefix target) of - (t suffix) = servePackage root suffix - _ = getBundleRoute root "GET" target - -arboricxRouter _ "GET" "/" _ _ = pure (htmlResponse htmlLandingPage) -arboricxRouter _ "GET" ^healthPath _ _ = healthRoute "GET" healthPath -arboricxRouter root "GET" target _ _ = getRegistryRoute root target -arboricxRouter root "POST" target _ body = putBundleRoute root "POST" target body -arboricxRouter _ _ _ _ _ = pure notFoundResponse - -arboricxHandler root client peer = httpHandlerIO (arboricxRouter root) client peer - --- Close a newly acquired socket on ordinary bind/listen failures. As elsewhere, --- finally does not catch terminal abortIO or host exceptions; successful serving --- delegates scheduling/accept policy to serveForever, without a shutdown hook. -arboricxServer root addr port = (let - failed message = pure (err message t) - opened server = (let - rejected message = finally (failed message) (closeSocket_ server) - ready _ = serveForever server (arboricxHandler root) - in onResult_ (do onOk_ - bindSocket server addr port - listen server 128) - rejected ready) - in onResult_ socket failed opened) diff --git a/lib/arboricx/streaming-v2.tri b/lib/arboricx/streaming-v2.tri @@ -0,0 +1,111 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!import io_v2_http as Transport +!import io_v2_artifacts as Stream +!import http as HTTP +!import handlers as H +!import handler_artifacts as Artifacts +!import handler_log as Log +!import arboricx_handlers as Registry +!export pump +!export connection +!export serve +!export serveCount + +pump _ 0 _ = H.pure (B.ok t t) +pump client remaining initial = let + limit = N.minimum remaining 4096 + consume (unused, (reversed, rest)) = case reversed of + [] = H.bind (H.liftIO (IO.readSome client limit)) (reply : case reply of + ((t t), (("data", chunk), t)) = pump client remaining chunk + _ = H.pure (B.err "Unexpected end of request body" t)) + _ = H.bind (Stream.write (B.reverse reversed)) (reply : case reply of + ((t t), (t, t)) = pump client (N.subtract remaining (N.subtract limit unused)) rest + _ = H.pure reply) + consume _ = H.fail "invalid-response" "artifact-stream" "buffer" "Invalid body state" + in consume (HTTP.takeBodyBytes initial limit []) + +logged sink status response = H.bind + (Log.withSink sink (Log.withContext [["method" "POST"] ["target" "/_arboricx/bundle"]] (Log.info status))) + (reply : case reply of + ((t t), (t, t)) = H.pure response + _ = H.pure (HTTP.errorResponse 500 "Internal Server Error\n")) +upload root sink client count initial = H.bind + (Artifacts.publishStreamV2 root count (H.label "streaming-upload" (pump client count initial))) + (reply : case reply of + ((t t), (digest, t)) = logged sink 201 (HTTP.createdResponse digest) + (t, (_, t)) = logged sink 400 (HTTP.badRequestResponse "Upload failed\n") + _ = H.fail "invalid-response" "artifact-stream" "publish" "Invalid upload Result") +-- Supervised request interpretation retains the socket in its connection owner; +-- borrowing does not extend lifetime. Join before responding, so cleanup failure +-- cannot falsely acknowledge success. Cancellation is never rendered as HTTP 500. +response root sink client count initial = IO.scope (_ : IO.require + (IO.spawn 1 1 [] (_ : H.runIOV2 (upload root sink client count initial))) (spawned : case spawned of + [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" value] = IO.pure value + ["failed" _ _] = IO.pure (HTTP.errorResponse 500 "Internal Server Error\n") + ["cancelled" reason _] = IO.cancelled reason + _ = IO.fail "Invalid upload task outcome") + _ = IO.fail "Invalid upload spawn reply")) +boundedResponse root limit sink client count initial = case N.lte count limit of + (t t) = IO.bind (response root sink client count initial) (Transport.respond client "POST") + _ = Transport.respond client "POST" (HTTP.errorResponse 413 "Request body too large\n") +framed root limit sink client fields initial = case HTTP.contentLength fields of + ((t t), (t, _)) = boundedResponse root limit sink client 0 initial + ((t t), ((t count), _)) = boundedResponse root limit sink client count initial + (t, (status, message)) = Transport.respond client "POST" (HTTP.errorResponse status message) + _ = Transport.respond client "POST" (HTTP.badRequestResponse "Bad Request\n") +headers root limit sink client version raw = case HTTP.parseHeaders raw of + ((t t), (fields, initial)) = case HTTP.validateRequest "POST" "/_arboricx/bundle" version fields of + ((t t), _) = framed root limit sink client fields initial + (t, (status, message)) = Transport.respond client "POST" (HTTP.errorResponse status message) + _ = Transport.respond client "POST" (HTTP.badRequestResponse "Bad Request\n") + _ = Transport.respond client "" (HTTP.badRequestResponse "Bad Request\n") +line root limit sink client ((t t), (("POST", ("/_arboricx/bundle", version)), raw)) = + headers root limit sink client version raw +line root limit sink client parsed = Transport.handle (Registry.routerV2 root limit sink) client parsed +connection root limit sink client = IO.bind (Transport.recvHeaders client) (reply : case reply of + ((t t), (raw, _)) = line root limit sink client (HTTP.parseRequestLine raw) + (t, (431, _)) = Transport.respond client "" (HTTP.errorResponse 431 "Request Header Fields Too Large\n") + _ = Transport.respond client "" (HTTP.badRequestResponse "Bad Request\n")) + +worker root limit sink [client] = connection root limit sink client +worker _ _ _ _ = IO.fail "Invalid connection handoff" +settle ticket = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" _] = IO.pure t + ["failed" _ _] = IO.bind (Registry.writeLogV2 "registry connection failed\n") (_ : IO.pure t) + ["cancelled" reason _] = IO.cancelled reason + _ = IO.fail "Invalid connection task outcome") +drain [] = IO.pure t +drain [ticket | rest] = IO.bind (settle ticket) (_ : drain rest) +drain _ = IO.fail "Invalid connection tickets" +next (t count) = B.just (N.predecessor count) +next _ = t +-- FIFO bounded admission: consume the oldest ticket before accepting beyond the +-- configured window. No discarded tickets, detached tasks or unbounded backlog in +-- the language. A slow oldest connection can delay admission; no deadline promise. +admit _ _ _ _ _ _ (t 0) tickets = drain tickets +admit root limit sink owner listener window remaining tickets = case N.gte (B.length tickets) window of + (t t) = case tickets of + [ticket | rest] = IO.bind (settle ticket) (_ : admit root limit sink owner listener window remaining rest) + _ = IO.fail "Invalid admission window" + _ = IO.require (IO.acceptTCP listener owner) (client : IO.require + (IO.spawn 1 1 [client] (worker root limit sink)) (spawned : case spawned of + [_ ticket] = admit root limit sink owner listener window (next remaining) (B.append tickets [ticket]) + _ = IO.fail "Invalid connection spawn reply")) +start root limit sink address window remaining ready = case B.checkContract B.nat? limit of + ((t t), _) = startWindow root limit sink address window remaining ready + _ = IO.fail "Invalid artifact byte limit" +startWindow root limit sink address window remaining ready = case B.checkContract B.nat? window of + ((t t), _) = case B.and? (N.gte window 1) (N.lte window 256) of + (t t) = IO.scope (owner : do IO.require + H.runIOV2 (Artifacts.prepareFilesystemV2 root) + listener <- IO.listenTCP owner address 128 + IO.bind (ready listener) (_ : admit root limit sink owner listener window remaining [])) + _ = IO.fail "Connection window must be between 1 and 256" + _ = IO.fail "Invalid connection window" +serve root limit sink address window ready = start root limit sink address window t ready +serveCount root limit sink address window count ready = case B.checkContract B.nat? count of + ((t t), _) = start root limit sink address window (B.just count) ready + _ = IO.fail "Invalid connection count" diff --git a/lib/arboricx/streaming.tri b/lib/arboricx/streaming.tri @@ -0,0 +1,4 @@ +!export * from arboricx_streaming_v2 + +-- Canonical streaming registry composition: scoped TCP, source-owned publication, +-- FIFO admission and consumable joins. No v1 scheduling or grant-policy branch. diff --git a/lib/graph.tri b/lib/graph.tri @@ -3,7 +3,7 @@ -- Entries are (name, dependency names). Names are opaque trees. Lists must be -- proper, names unique, and every dependency present: frontend validation owns --- these checks and its diagnostics. This is not a malformed-graph validator. +-- these checks and their diagnostics before invoking this algorithm. graphFind entries name = find (entry : equal? (fst entry) name) entries graphContains names name = any? (equal? name) names graphReady dependencies available = all? (graphContains available) dependencies @@ -34,9 +34,8 @@ graphCycle current entries = (let rec (graphContains seen name)) in walk current []) --- Select the earliest currently ready entry each round (not FIFO batches). --- This simple small-graph algorithm retains linear membership/removal scans; --- it does not promise O(V+E) sorting for large dependency graphs. +-- Select the earliest currently ready entry each round. This small-graph +-- algorithm uses linear membership/removal scans on each iteration. graphSort entries = (let rec walk pending available reversed = (case pending of [] = ok (reverse reversed) t diff --git a/lib/handlers.tri b/lib/handlers.tri @@ -0,0 +1,165 @@ +!import base as B +!import io_v2 as V2 +!export pure +!export bind +!export request +!export checkedRequest +!export checkWith +!export checkRequest +!export handle +!export forward +!export label +!export fail +!export liftIO +!export runPure +!export runIO +!export collect +!export view +!export diagnostic +!export scopeV2 +!export spawnV2 +!export runIOV2 + +-- Request/response effects. Handler callbacks return computations yielding one +-- reply. liftIO bridges host actions into this protocol. +pure value = B.pair 100 value +bind action next = B.pair 101 (B.pair action next) +request service operation argument = B.pair 102 (B.pair [service operation argument] pure) +handle service handler action = B.pair 104 (B.pair [service handler] action) +label name action = B.pair 105 (B.pair [name] action) +fail kind service operation detail = B.pair 103 [kind service operation detail] + +-- Handlers execute outside their own installation scope, so forwarding is an +-- ordinary request there. At other call sites it uses the current environment. +-- Service names select routes; host operations use OS process authority. +forward = request +liftIO action = request "host.io" "run" action + +-- Boundary constructors snapshot the actual lexical environment during +-- normalization, including when invoked inside a handler callback. +-- Child frames start empty; immutable routing and label values are inherited. +scopeV2 body = B.pair 107 body +spawnV2 supervised keep handles body = B.pair 108 [supervised keep handles body] + +-- Diagnostics are data: kind, service, operation, detail, outer-to-inner labels. +diagnostic kind service operation detail labels = [kind service operation detail labels] + +-- Check once, then select one continuation. Success passes the checked value; +-- rejection passes its diagnostic; a malformed reply passes the original value. +-- Each continuation owns its outcome policy and only the selected one runs. +checkWith contract value rejected malformed next = case B.checkContract contract value of + ((t t), (checked, t)) = next checked + (t, (message, _)) = rejected message + _ = malformed value + +-- Request-boundary policy carries routing details into handler diagnostics. +checkRequest service operation contract argument = checkWith contract argument + (message : fail "invalid-request" service operation message) + (original : fail "invalid-request-contract" service operation original) + +checkedRequest service operation inputC outputC argument = + checkRequest service operation inputC argument (checked : do bind + reply <- request service operation checked + checkWith outputC reply + (message : fail "invalid-response" service operation message) + (original : fail "invalid-response-contract" service operation original) + pure) + +findHandler wanted [[service handler] | outer] = B.lazyBool + (_ : B.pair handler outer) + (_ : findHandler wanted outer) + (B.equal? wanted service) +findHandler _ _ = t + +-- Normalized views: 100 value; 103 complete diagnostic; 106 ([service op arg], +-- outer-to-inner labels, reply continuation). View stops at the first request. +-- Frames retain caller environments and labels; handlers use the environment +-- suffix outside their installation, but keep the requesting execution's labels. +normalize action environment frames labels = let + broken = B.pair 103 (diagnostic "invalid-computation" t t t labels) + resume value [] = pure value + -- Force the popped stack tail's outer constructor before reusing it. A lazy + -- projection here can keep consumed frames (and their old chunks) reachable. + resume value [[saved savedLabels next] | rest] = case rest of + [] = normalize (next value) saved [] savedLabels + more{[_ | _]} = normalize (next value) saved more savedLabels + _ = broken + resume _ _ = broken + dispatch event{[service operation argument]} next = case findHandler service environment of + (handler, outer) = normalize (handler operation argument) outer + (B.pair [environment labels next] frames) labels + _ = B.pair 106 [event labels (reply : normalize (next reply) environment frames labels)] + dispatch _ _ = broken + in case action of + (100, value) = resume value frames + (101, (first, next)) = normalize first environment (B.pair [environment labels next] frames) labels + (102, (event, next)) = dispatch event next + (103, [kind service operation detail]) = B.pair 103 (diagnostic kind service operation detail labels) + (104, (entry{[service handler]}, body)) = normalize body (B.pair entry environment) + (B.pair [environment labels pure] frames) labels + (105, (extraLabels, body)) = normalize body environment + (B.pair [environment labels pure] frames) (B.append labels extraLabels) + (107, body) = B.pair 107 [labels + (owner : normalize (body owner) environment [] labels) + (reply : resume reply frames)] + (108, [supervised keep handles body]) = B.pair 108 [supervised keep handles labels + (fresh : normalize (body fresh) environment [] labels) + (reply : resume reply frames)] + _ = broken + +view action = normalize action [] [] [] + +-- Execution Result is distinct from an operation's ordinary Result value. +-- Pure interpretation rejects every unhandled request, including host IO. +runPure action = case view action of + (100, value) = B.ok value t + (103, problem) = B.err problem t + (106, [[service operation argument] labels next]) = B.err + (diagnostic "unhandled-request" service operation argument labels) t + _ = B.err (diagnostic "invalid-view" t t t []) t + +-- Handler failures become terminal v2 outcomes. Ordinary service Results remain +-- values. Labels/continuations stay lazy; only the label list spine is checked. +runHostV2 current = case current of + (100, value) = V2.pure value + (103, problem) = V2.fail problem + (106, [["host.io" "run" action] labels next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind action (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + (106, [[service operation argument] labels next]) = V2.fail + (diagnostic "unhandled-request" service operation argument labels) + (107, [labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind (V2.scope (owner : runHostV2 (body owner))) + (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + (108, [supervised keep handles labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind (V2.spawn supervised keep handles (fresh : runHostV2 (body fresh))) + (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + _ = V2.fail (diagnostic "invalid-view" t t t []) +runIOV2 action = runHostV2 (view action) +runIO = runIOV2 + +-- Collection threads its accumulator through a boundary transformer. +-- Inner handlers run first. Other residual requests are relayed to outer +-- handlers, with their labels scoped ONLY around the forwarded request. +-- It returns [value chronological-events]. Failures retain their labels. +collect service operation argumentC reply action = let rec + walk current reversed = case current of + (100, value) = pure [value (B.reverse reversed)] + (103, [kind failedService failedOperation detail labels]) = B.pair 105 + (B.pair labels (fail kind failedService failedOperation detail)) + (106, [event{[wanted op argument]} labels next]) = B.lazyBool + (_ : case B.checkContract argumentC argument of + ((t t), (checked, t)) = walk (next reply) (B.pair checked reversed) + (t, (message, _)) = B.pair 105 (B.pair labels (fail "invalid-request" service operation message)) + _ = B.pair 105 (B.pair labels (fail "invalid-request-contract" service operation argument))) + (_ : bind (B.pair 105 (B.pair labels (request wanted op argument))) + (answer : walk (next answer) reversed)) + (B.and? (B.equal? wanted service) (B.equal? op operation)) + (107, [labels body next]) = B.pair 105 (B.pair labels + (fail "state-boundary" service operation "Install collection inside the scoped body")) + (108, [supervised keep handles labels body next]) = B.pair 105 (B.pair labels + (fail "state-boundary" service operation "Install child-local collection explicitly")) + _ = fail "invalid-view" t t t + in walk (view action) [] diff --git a/lib/handlers/artifacts.tri b/lib/handlers/artifacts.tri @@ -0,0 +1,120 @@ +!import base as B +!import handlers as H +!import io_v2 as V2 +!import io_v2_storage as Storage +!import io_v2_artifacts as Stream +!import arboricx_server as Store +!export load +!export loadFrom +!export publish +!export publishTo +!export withFilesystemV2 +!export withFilesystemForV2 +!export prepareFilesystemV2 +!export publishStreamV2 + +-- Domain Results always have empty remainders. Callers map them to HTTP statuses. +-- Invalid digests are ordinary input failures; +-- malformed request/reply protocols are interpreter failures. +-- Eight bounded bit steps, each checking exact Boolean shape and canonical +-- termination. The fixed continuation chain validates canonical octet shape +-- structurally, including the final high bit. +octetEnd = B.triage (t t) (_ : t) (_ _ : t) +octetStep next value = B.triage (t t) (_ : t) + (bit tail : B.triage + (B.triage t (_ : t) (_ _ : next tail) tail) + (child : B.triage (next tail) (_ : t) (_ _ : t) child) + (_ _ : t) bit) + value +octetValid = octetStep (octetStep (octetStep (octetStep + (octetStep (octetStep (octetStep (octetStep octetEnd))))))) +byteC = B.guardC "expected octet" octetValid + +-- These predicates preserve the original accepted list. The scanner allocates +-- one final Result and retains the first-error diagnostic and caller's remainder. +bytesC value rest = let rec + walk remaining = B.triage + (B.contractOk value rest) + (_ : B.contractErr "not a list" rest) + (byte tail : case octetValid byte of + (t t) = walk tail + _ = B.contractErr "expected octet" rest) + remaining + in walk value + +checkedDigest service digest next = H.checkWith Store.bundleHashC digest + (message : H.pure (B.err ["invalid-digest" message] t)) + (original : H.fail "invalid-contract" service "load" original) + next +loadFrom service digest = checkedDigest service digest + (checked : H.checkedRequest service "load" B.anyC loadReplyC checked) +load digest = loadFrom "artifacts" digest +publishTo service bytes = H.checkedRequest service "publish" bytesC publishReplyC bytes +publish bytes = publishTo "artifacts" bytes + +loadReplyC value{((t t), (bytes, t))} rest = case B.checkContract bytesC bytes of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "expected artifact bytes" rest +loadReplyC value{(t, (message, t))} rest = B.contractOk value rest +loadReplyC _ rest = B.contractErr "expected artifact-load Result with empty remainder" rest +publishReplyC value{((t t), (digest, t))} rest = case B.checkContract Store.bundleHashC digest of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "expected canonical artifact digest" rest +publishReplyC value{(t, (message, t))} rest = B.contractOk value rest +publishReplyC _ rest = B.contractErr "expected artifact-publication Result with empty remainder" rest + +-- Native v2 adapter for the domain interface. Limits count bytes; heap usage +-- requires a separate bound. Roots/paths follow OS authority. Verify and sync +-- every immutable winner, preserving corrupt winners for diagnosis. Loads +-- authenticate the exact bytes returned to the caller. +loadV2 root limit digest = do V2.onOk + bytes <- Storage.readBytes (Store.bundleObjectPath root digest) limit + actual <- Storage.hashBytes limit bytes + case B.equal? actual digest of + (t t) = V2.pure (B.ok bytes t) + _ = V2.pure (B.err ["integrity-failed" "artifacts/load" "digest mismatch" "unknown"] t) + +publishV2 root limit bytes = do V2.onOk + digest <- Storage.hashBytes limit bytes + case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = let directory = Store.objectDir root (Store.hashShard checked) in do V2.onOk + V2.ensureDirectory directory + V2.syncDirectory (Store.pathJoin root "objects") + Storage.publishBytes directory (Store.bundleObjectPath root checked) 0 limit bytes + V2.pure (B.ok checked t) + _ = V2.fail "Invalid artifact digest reply" + +filesystemV2 root limit service "load" digest = checkedDigest service digest + (checked : H.liftIO (loadV2 root limit checked)) +filesystemV2 root limit service "publish" bytes = H.checkWith bytesC bytes + (message : H.fail "invalid-request" service "publish" message) + (original : H.fail "invalid-contract" service "publish" original) + (checked : H.liftIO (publishV2 root limit checked)) +filesystemV2 _ _ service operation payload = H.forward service operation payload +withFilesystemForV2 service root limit action = H.handle service (filesystemV2 root limit service) action +withFilesystemV2 root limit action = withFilesystemForV2 "artifacts" root limit action + +-- Parent-first initialization; the configured root's parent must already exist. +-- Directory-entry synchronization follows host filesystem guarantees. +ensureV2 path = do V2.onOk + V2.ensureDirectory path + V2.syncDirectory (Storage.parentPath path) +prepareFilesystemV2 root = H.liftIO (do V2.onOk + ensureV2 root + ensureV2 (Store.pathJoin root "tmp") + ensureV2 (Store.pathJoin root "objects") + ensureV2 (Store.pathJoin root "aliases") + ensureV2 (Store.pathJoin (Store.pathJoin root "aliases") "names") + ensureV2 (Store.pathJoin (Store.pathJoin root "aliases") "packages") + ensureV2 (Store.pathJoin root "manifests") + V2.pure (B.ok t t)) + +-- Bounded producer, existing registry layout. The shard is chosen only after +-- hashing the complete stream incrementally. Temporary and object directories +-- must share a filesystem. +streamDestination root digest = H.liftIO (do V2.onOk + V2.ensureDirectory (Store.objectDir root (Store.hashShard digest)) + V2.syncDirectory (Store.pathJoin root "objects") + V2.pure (B.ok (Store.bundleObjectPath root digest) t)) +publishStreamV2 root expected producer = Stream.publishWith + (Store.pathJoin root "tmp") expected (streamDestination root) producer diff --git a/lib/handlers/log.tri b/lib/handlers/log.tri @@ -0,0 +1,38 @@ +!import base as B +!import handlers as H +!export emit +!export info +!export warn +!export withContext +!export withSink +!export capture +!export discard + +-- Events are [level ordered-context-fields payload]. Payload/fields are trees; +-- sinks choose their formatting and serialization. +eventC value{[level fields payload]} rest = case B.listOf B.anyC fields rest of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "log context must be a proper list" rest +eventC _ rest = B.contractErr "expected [level context payload]" rest + +-- Log sinks return ordinary Result unit; errors remain application-visible. +replyC value{((t t), (t, t))} rest = B.contractOk value rest +replyC value{(t, (message, t))} rest = B.contractOk value rest +replyC _ rest = B.contractErr "expected log Result with empty remainder" rest + +emit level payload = H.checkedRequest "logging" "emit" eventC replyC [level [] payload] +info payload = emit "info" payload +warn payload = emit "warn" payload + +contextHandler fields "emit" event{[level inner payload]} = + H.forward "logging" "emit" [level (B.append fields inner) payload] +contextHandler _ operation payload = H.forward "logging" operation payload +withContext fields action = case B.checkContract (B.listOf B.anyC) fields of + ((t t), (checked, t)) = H.handle "logging" (contextHandler checked) action + _ = H.fail "invalid-request" "logging" "context" "context must be a proper list" + +sinkHandler sink "emit" event = H.checkRequest "logging" "emit" eventC event sink +sinkHandler _ operation payload = H.forward "logging" operation payload +withSink sink action = H.handle "logging" (sinkHandler sink) action +capture action = H.collect "logging" "emit" eventC (B.ok t t) action +discard action = withSink (_ : H.pure (B.ok t t)) action diff --git a/lib/handlers/testing.tri b/lib/handlers/testing.tri @@ -0,0 +1,28 @@ +!import base as B +!import handlers as H +!export expect +!export run + +-- Scripts match exact named requests and payloads, supplying ordinary replies. +-- Return [execution-Result chronological-trace unused-script]. A success with +-- leftover expectations is an error. A failure retains its trace and remainder. +expect service operation argument reply = [[service operation argument] reply] +run script action = let rec + walk current remaining reversed = let + finish outcome = [outcome (B.reverse reversed) remaining] + rejected event problem = [(B.err problem t) (B.reverse (B.pair event reversed)) remaining] + consume event{[service operation argument]} labels next [[wanted reply] | rest] = B.lazyBool + (_ : walk (next reply) rest (B.pair event reversed)) + (_ : rejected event (H.diagnostic "script-mismatch" service operation [wanted event] labels)) + (B.equal? wanted event) + consume event{[service operation argument]} labels _ [] = rejected event + (H.diagnostic "script-exhausted" service operation argument labels) + consume event labels _ _ = rejected event (H.diagnostic "invalid-script" t t t labels) + in case current of + (100, value) = case remaining of + [] = finish (B.ok value t) + _ = finish (B.err (H.diagnostic "script-unused" t t remaining []) t) + (103, problem) = finish (B.err problem t) + (106, [event labels next]) = consume event labels next remaining + _ = finish (B.err (H.diagnostic "invalid-view" t t t []) t) + in walk (H.view action) script [] diff --git a/lib/handlers/upload.tri b/lib/handlers/upload.tri @@ -0,0 +1,5 @@ +!export * from io_v2_artifacts + +-- Source-owned exact-length producers over scoped staging and digest resources. +-- publish takes a staging directory; publishWith makes destination policy explicit. +-- There is no native upload session registry or implicit registry-store layout. diff --git a/lib/http.tri b/lib/http.tri @@ -1,6 +1,4 @@ !import prelude * -!import io * -!import socket * !import parser * !import native_math as Nat !export * @@ -49,9 +47,23 @@ buildResponse status headers body = (append (statusLine status (statusPhrase status)) (append (foldl httpAppendResponseHeader "" headers) (append "\r\n" body))) +-- Count a proper response-body list in one pass. Unlike general listOf, +-- this boundary never transforms elements, so no validated copy is needed. +-- Force each successor's outer constructor before advancing: do not retain a +-- successor continuation for every byte. Input elements remain unforced. +-- Malformed list spines retain length's ordinary contract-failure Result. +httpBodyLength values = let rec + walk remaining count = triage count + (_ : contractErr "not a list" t) + (_ tail : case Nat.successor count of + next{(_, _)} = walk tail next + _ = contractErr "Invalid internal length counter" t) + remaining + in walk values 0 + response status contentType body = (buildResponse status [(pair "Content-Type" contentType) - (pair "Content-Length" (showNumber (length body))) + (pair "Content-Length" (showNumber (httpBodyLength body))) (pair "Connection" "close")] body) @@ -87,35 +99,6 @@ responseForMethod "HEAD" response = headersOnly response responseForMethod _ response = response -- --------------------------------------------------------------------------- --- Header receive / framing --- --------------------------------------------------------------------------- - --- maxBytes bounds the entire accumulated receive buffer, including any --- prefetched suffix. Retain that policy; this is not a header-only byte count. -httpRecvUntil sock delimiter limit accumulated count = (let rec - receive buffer size = (do bind - reply <- recv sock 4096 - case reply of - (t, _) = pure (err 400 buffer) - ((t t), ([], _)) = pure (err 400 buffer) - ((t t), (chunk, _)) = (let - next = append buffer chunk - nextSize = Nat.add size (length chunk) - in case (Nat.lte nextSize limit) of - ^true = case (contains? delimiter next) of - ^true = pure (ok next t) - _ = receive next nextSize - _ = pure (err 431 next)) - _ = pure (err 400 buffer)) - in receive accumulated count) - -recvUntilMax sock delimiter limit = httpRecvUntil sock delimiter limit [] 0 - -recvUntil sock delimiter = recvUntilMax sock delimiter maxHeaderBytes - -recvHeaders sock = recvUntilMax sock crlfcrlf maxHeaderBytes - --- --------------------------------------------------------------------------- -- Request line parsing -- --------------------------------------------------------------------------- @@ -264,7 +247,9 @@ bodyReadState remaining reversed rest = pair remaining (pair reversed rest) -- State is (remaining, (reversed body, unused input)); zero consumes nothing. -- remaining is a canonical natural supplied by the bounded length decoder. takeBodyBytes bytes 0 reversed = bodyReadState 0 reversed bytes -takeBodyBytes [byte | rest] remaining reversed = takeBodyBytes rest (Nat.subtract remaining 1) (pair byte reversed) +-- The zero equation above excludes underflow. Specialized predecessor avoids +-- a full-width comparison/subtraction per byte of a canonical body counter. +takeBodyBytes [byte | rest] remaining reversed = takeBodyBytes rest (Nat.predecessor remaining) (pair byte reversed) takeBodyBytes _ remaining reversed = bodyReadState remaining reversed [] -- Retain the existing receive-size buckets, expressed as native comparisons @@ -277,32 +262,6 @@ httpRecvBucket _ _ = 1 recvChunkMax4096 remaining = httpRecvBucket remaining [4096 1024 256 64 16] --- One state machine owns both buffered and subsequent socket input. EOF before --- completion is a 400, not another recv with unchanged remaining length. -httpReadBodyState _ (0, (reversed, rest)) = pure (ok (reverse reversed) rest) -httpReadBodyState sock (remaining, (reversed, _)) = (let - received (t, (message, _)) = pure (err 400 (append "recv failed while reading body: " message)) - received ((t t), ([], _)) = pure (err 400 "Unexpected end of request body\n") - received ((t t), (chunk, _)) = httpReadBodyState sock (takeBodyBytes chunk remaining reversed) - received _ = pure httpBadRequest - in do bind - reply <- recv sock (recvChunkMax4096 remaining) - received reply) -httpReadBodyState _ _ = pure httpBadRequest - -readBodyMore sock remaining reversed = httpReadBodyState sock (bodyReadState remaining reversed []) -readBodyExact sock expected initial = httpReadBodyState sock (takeBodyBytes initial expected []) - --- readBodyExact starts with remaining + length(reversed) = expected. --- takeBodyBytes preserves that sum; only remaining=0 succeeds, while early EOF --- or recv failure returns an error. A successful body already has exact length: --- do not traverse it again solely to recount it. -readBody sock headers initial = (let - selected ((t t), (t, _)) = pure (ok [] initial) - selected ((t t), ((t count), _)) = readBodyExact sock count initial - selected failure = pure failure - in selected (contentLength headers)) - -- --------------------------------------------------------------------------- -- Request validation -- --------------------------------------------------------------------------- @@ -336,47 +295,3 @@ validateRequest method target version _ = (let routerMethod "HEAD" = "GET" routerMethod method = method - --- finally preserves a send Result and attempts close even after send failure. --- Like the existing IO helper, it does not catch host exceptions/abortIO. -respondAndClose sock response = (do onOk_ - finally (send sock response) (closeSocket_ sock) - pure (ok t t)) - -httpRespond client method response = respondAndClose client (responseForMethod method response) - -httpHandleBody router client method target headers reply = (let - handle ((t t), (body, _)) = (do bind - response <- router (routerMethod method) target headers body - httpRespond client method response) - handle (t, (status, message)) = httpRespond client method (errorResponse status message) - handle _ = httpRespond client method (badRequestResponse "Bad Request\n") - in handle reply) - -httpHandleHeaders router client method target version input = (let - headersParsed ((t t), (headers, initial)) = (let - validated ((t t), _) = (do bind - reply <- readBody client headers initial - httpHandleBody router client method target headers reply) - validated (t, (status, message)) = httpRespond client method (errorResponse status message) - validated _ = httpRespond client method (badRequestResponse "Bad Request\n") - in validated (validateRequest method target version headers)) - -- Retain historical behavior: a header syntax failure has no trusted HEAD - -- response handling, even though the request line has already been parsed. - headersParsed _ = respondAndClose client (badRequestResponse "Bad Request\n") - in headersParsed (parseHeaders input)) - -httpHandleRequest router client ((t t), ((method, (target, version)), headers)) = httpHandleHeaders router client method target version headers -httpHandleRequest _ client _ = respondAndClose client (badRequestResponse "Bad Request\n") - -httpHandleReceived router client ((t t), (raw, _)) = httpHandleRequest router client (parseRequestLine raw) -httpHandleReceived _ client (t, (431, _)) = respondAndClose client (errorResponse 431 "Request Header Fields Too Large\n") -httpHandleReceived _ client _ = respondAndClose client (badRequestResponse "Bad Request\n") - -httpHandlerIO router client _ = (do bind - reply <- recvHeaders client - httpHandleReceived router client reply) - --- One effectful pipeline; a pure router needs only this value-to-action adapter. -httpPureRouter router method target headers body = pure (router method target headers body) -httpHandler router client peer = httpHandlerIO (httpPureRouter router) client peer diff --git a/lib/io-v2.tri b/lib/io-v2.tri @@ -0,0 +1,181 @@ +!import base as B +!import native_math as N +!export * + +-- Structured IO with named, versioned facilities. +-- Native execution uses OS process authority. +io action = B.pair "tricuIO" (B.pair 2 action) +pure value = B.pair 0 value +bind action next = B.pair 1 (B.pair action next) +fail diagnostic = B.pair 2 diagnostic +scope body = B.pair 3 body +currentScope = B.pair 4 t + +-- Flags are canonical 0/1. Callback receives fresh handles. +-- Success is [task joinTicket]; non-result children receive t in the ticket slot. +spawn supervised keep handles body = B.pair 5 + (B.pair supervised (B.pair keep (B.pair handles body))) + +join ticket = B.pair 6 ticket +discard ticket = B.pair 7 ticket +cancel task reason = B.pair 8 (B.pair task reason) +cancelled reason = B.pair 9 reason + +-- Named, independently versioned host facilities; core sequencing stays separate. +request group revision operation arguments = B.pair 10 + (B.pair group (B.pair revision (B.pair operation arguments))) + +capabilities = request "runtime" 1 "capabilities" t +inspect action = request "runtime" 1 "inspect" action + +stdin = request "standard" 1 "input" t +stdout = request "standard" 1 "output" t +stderr = request "standard" 1 "error" t + +openReader owner path = request "file" 1 "open-reader" (B.pair owner path) +openWriter owner path = request "file" 1 "open-writer" (B.pair owner path) +openAppender owner path = request "file" 1 "open-appender" (B.pair owner path) +openUpdate owner path = request "file" 1 "open-update" (B.pair owner path) + +seekFile handle offset = request "file" 1 "seek" (B.pair handle offset) +filePosition handle = request "file" 1 "position" handle +syncFile handle = request "file" 1 "sync" handle + +syncDirectory path = request "directory" 1 "sync" path + +fileSize handle = request "file" 1 "size" handle +resizeFile handle size = request "file" 1 "resize" (B.pair handle size) + +statPath path = request "path" 1 "stat" path +lstatPath path = request "path" 1 "lstat" path +removeFile path = request "path" 1 "remove-file" path +renamePath source destination = request "path" 1 "rename" (B.pair source destination) + +createDirectory path = request "directory" 1 "create" path +removeDirectory path = request "directory" 1 "remove" path +openDirectory owner path = request "directory" 1 "open" (B.pair owner path) +nextDirectory directory = request "directory" 1 "next" directory + +ipv4 octets port = ["ipv4" octets port] +ipv6 words scope port = ["ipv6" words scope port] + +resolveTCP host family port limit = request "resolver" 1 "resolve" [host family port limit] + +listenTCP owner address backlog = request "tcp" 1 "listen" (B.pair owner (B.pair address backlog)) +connectTCP owner address = request "tcp" 1 "connect" (B.pair owner address) +acceptTCP listener owner = request "tcp" 1 "accept" (B.pair listener owner) + +localAddress socket = request "tcp" 1 "local-address" socket +peerAddress socket = request "tcp" 1 "peer-address" socket +shutdownWrite socket = request "tcp" 1 "shutdown-write" socket + +-- Read exactly one authenticated frame from the current cursor. +-- Temporary disk <= size; the caller supplies a per-operation limit. +readIndexedTree reader directory limit size digest = request "indexed-tree" 1 "read" + [reader directory limit size digest] + +-- Canonical native encoding avoids expanded tree/byte lists. The writer checks +-- the size and expected digest before sending. Retry decisions require confirmed +-- progress; a failed write can have partial or uncertain effects. +hashIndexedTree limit tree = request "indexed-tree" 1 "hash" (B.pair limit tree) + +writeIndexedTree writer limit digest tree = request "indexed-tree" 1 "write" + [writer limit digest tree] + +-- Exact canonical-byte comparison consumes precisely one frame. +verifyIndexedTree reader limit tree = request "indexed-tree" 1 "verify" [reader limit tree] + +-- Stateless bounded codecs: decode returns [text carry], with carry <=3 octets. +-- Set final=1 at EOF to reject incomplete suffixes. NUL and BOM are valid scalars; +-- source-file BOM rejection belongs to library/application policy. +encodeUTF8 text = request "utf8" 1 "encode" text +decodeUTF8 final bytes = request "utf8" 1 "decode" [final bytes] + +readSome reader limit = request "stream" 1 "read" (B.pair reader limit) +writeSome writer bytes = request "stream" 1 "write" (B.pair writer bytes) +close handle = request "resource" 1 "close" handle + +openStage owner directory = request "stage" 1 "open" (B.pair owner directory) +sealStage stage = request "stage" 1 "seal" stage +publishNew stage destination = request "stage" 1 "publish" (B.pair stage (B.pair 0 destination)) +publishReplace stage destination = request "stage" 1 "publish" (B.pair stage (B.pair 1 destination)) + +openDigest owner algorithm = request "digest" 1 "open" (B.pair owner algorithm) +updateDigest digest bytes = request "digest" 1 "update" (B.pair digest bytes) +finishDigest digest = request "digest" 1 "finish" digest + +onOk action next = bind action (reply : case reply of + ((t t), (value, t)) = next value + (t, (_, t)) = pure reply + _ = fail "Invalid operation Result") + +-- Nonrecursive initialization for caller-ordered store directory creation. +-- stat follows an existing symlink and accepts it when its target is a directory. +ensureDirectory path = bind (createDirectory path) (reply : case reply of + (t, (["already-exists" _ _ _], t)) = onOk (statPath path) (status : case status of + ["directory"] = pure (B.ok t t) + _ = pure (B.err ["not-directory" "directory/ensure" "" "none"] t)) + _ = pure reply) + +-- Explicit sequential fallback policy, separate from native connect/resolve. +-- Stop on the first success; preserve the last ordinary failure unchanged. +connectFirstTCP owner addresses = connectCandidates owner addresses + (B.err ["not-found" "tcp/connect-first" "No endpoint candidates" "none"] t) +connectCandidates _ [] previous = pure previous +connectCandidates owner [address | rest] _ = bind (connectTCP owner address) (reply : case reply of + ((t t), (_, t)) = pure reply + (t, (_, t)) = connectCandidates owner rest reply + _ = fail "Invalid connection Result") +connectCandidates _ _ _ = pure (B.err ["invalid-argument" "tcp/connect-first" "Invalid endpoint list" "none"] t) + +-- Turn an explicitly selected operation failure into terminal task failure. +-- Ordinary Results elsewhere remain values. +require action next = bind action (reply : case reply of + ((t t), (value, t)) = next value + (t, (problem, t)) = fail problem + _ = fail "Invalid operation Result") + +-- Whole-chunk convenience over the native positive-progress contract. Failure +-- stops immediately, preserving partial or uncertain progress for the caller. +dropWritten 0 bytes = bytes +dropWritten count [_ | rest] = dropWritten (N.predecessor count) rest +dropWritten _ _ = [] +-- Failure carries confirmed progress BEFORE the failing writeSome, plus its +-- unchanged diagnostic (whose own progress can be unknown). Composite progress +-- includes earlier successful writes even when the last operation reports "none". +writeAll writer bytes = let rec + walk accepted remaining = case remaining of + [] = pure (B.ok t t) + _ = bind (writeSome writer remaining) (reply : case reply of + ((t t), (count, t)) = walk (N.add accepted count) (dropWritten count remaining) + (t, (problem, t)) = pure (B.err ["write-all" accepted problem] t) + _ = fail "Invalid operation Result") + in walk 0 bytes + +-- Sequential bounded pumping, without acquiring or closing either endpoint. +-- Errors retain writeAll's confirmed prefix and original uncertain progress. +copyStream reader writer = do onOk + received <- readSome reader 4096 + case received of + ("eof", t) = pure (B.ok t t) + ("data", bytes) = do onOk + writeAll writer bytes + copyStream reader writer + _ = fail "Invalid read reply" + +-- Relative waits and native monotonic observations. Scheduler latency can extend +-- waits; yield offers a scheduling opportunity with scheduler-selected ordering. +monotonicNanoseconds = request "clock" 1 "monotonic" t +sleepMilliseconds milliseconds = request "clock" 1 "sleep" milliseconds +yield = request "scheduler" 1 "yield" t + +-- Returns Result [task joinTicket stdinWriter stdoutReader stderrReader]. +-- cwd: ["inherit"] or ["at" path]; env: ["inherit"] or ["replace" [[key value] ...]]. +-- Normal scope exit waits. Close stdin for EOF; drain stdout/stderr concurrently. +-- Join completion holds ["exit" code] or ["signal" number]. +spawnProcess executable arguments directory environment graceMilliseconds = + request "process" 1 "spawn" [executable arguments directory environment graceMilliseconds] + +-- Read-only host context. Environment query returns ["missing"] or ["value" text]; empty and absent remain distinct. +workingDirectory = request "process" 1 "working-directory" t +environmentVariable name = request "process" 1 "environment-variable" name diff --git a/lib/io-v2/artifacts.tri b/lib/io-v2/artifacts.tri @@ -0,0 +1,90 @@ +!import base as B +!import native_math as N +!import http as HTTP +!import io_v2 as IO +!import io_v2_storage as Storage +!import handlers as H +!import arboricx_server as Store +!export write +!export publish +!export publishWith + +-- Source-owned upload policy. Only writes observed by this boundary advance +-- its byte count; producer acknowledgements report producer status. +write bytes = H.request "artifact-stream" "write" bytes +onOk action next = H.bind (H.liftIO action) (reply : case reply of + ((t t), (value, t)) = next value + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "host" "Invalid host Result") + +hashReader reader digest remaining = IO.onOk (IO.readSome reader 4096) (reply : case reply of + ("eof", t) = case remaining of + 0 = IO.finishDigest digest + _ = IO.pure (B.err "Existing artifact length mismatch" t) + ("data", bytes) = let count = HTTP.httpBodyLength bytes in case N.lte count remaining of + (t t) = IO.onOk (IO.updateDigest digest bytes) + (_ : hashReader reader digest (N.subtract remaining count)) + _ = IO.pure (B.err "Existing artifact length mismatch" t) + _ = IO.fail "Invalid read reply") + +-- Accept an existing name only after verifying its complete contents and length +-- independently with fresh reader/digest resources. +verifyWinner destination expected wanted = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner destination + digest <- IO.openDigest owner "sha256" + actual <- hashReader reader digest expected + case B.equal? actual wanted of + (t t) = do IO.onOk + IO.syncFile reader + IO.syncDirectory (Storage.parentPath destination) + IO.pure (B.ok t t) + _ = IO.pure (B.err "Existing artifact digest mismatch" t)) + +-- Destination policy runs only after exact-length completion and sealing. It +-- returns a handler Result path and can create a digest-derived shard. Native +-- publication remains generic and rejects cross-filesystem moves without copying. +finish destination expected stage digest = onOk (IO.finishDigest digest) (hash : + case B.checkContract Store.bundleHashC hash of + ((t t), (checked, t)) = onOk (IO.sealStage stage) (_ : + H.bind (destination checked) (reply : case reply of + ((t t), (path, t)) = onOk (IO.publishNew stage path) (outcome : case outcome of + "published" = H.pure (B.ok checked t) + "exists" = onOk (verifyWinner path expected checked) (_ : H.pure (B.ok checked t)) + _ = H.fail "invalid-response" "artifact-stream" "publish" "Invalid publication reply") + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "destination" "Expected destination Result")) + _ = H.fail "invalid-response" "artifact-stream" "digest" "Invalid digest reply") + +publish directory expected producer = publishWith directory expected + (digest : H.pure (B.ok (B.append directory (B.pair 47 digest)) t)) producer +publishWith directory expected destination producer = case B.checkContract B.nat? expected of + ((t t), (count, t)) = case N.lte count 67108864 of + (t t) = H.scopeV2 (owner : onOk (IO.openStage owner directory) (stage : + onOk (IO.openDigest owner "sha256") (digest : let rec + walk current remaining = case current of + (100, reply) = case reply of + ((t t), (t, t)) = case remaining of + 0 = finish destination count stage digest + _ = H.pure (B.err "Incomplete artifact stream" t) + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "producer" "Expected Result unit" + (103, [kind service operation detail labels]) = B.pair 105 + (B.pair labels (H.fail kind service operation detail)) + (106, [["artifact-stream" "write" bytes] labels next]) = + onOk (IO.writeAll stage bytes) (_ : case bytes of + [] = H.pure (B.err "Empty artifact chunk" t) + _ = let length = HTTP.httpBodyLength bytes in case N.lte length remaining of + (t t) = onOk (IO.updateDigest digest bytes) + (_ : walk (next (B.ok t t)) (N.subtract remaining length)) + _ = H.pure (B.err "Artifact exceeds declared length" t)) + (106, [[service operation argument] labels next]) = H.bind + (B.pair 105 (B.pair labels (H.request service operation argument))) + (reply : walk (next reply) remaining) + (107, [labels body next]) = B.pair 105 (B.pair labels + (H.fail "producer-boundary" "artifact-stream" "publish" "Producer must be sequential")) + (108, [supervised keep handles labels body next]) = B.pair 105 (B.pair labels + (H.fail "producer-boundary" "artifact-stream" "publish" "Producer must be sequential")) + _ = H.fail "invalid-view" "artifact-stream" "publish" "Invalid producer view" + in walk (H.view producer) count))) + _ = H.pure (B.err "Artifact length exceeds limit" t) + _ = H.pure (B.err "Invalid artifact length" t) diff --git a/lib/io-v2/http-transport.tri b/lib/io-v2/http-transport.tri @@ -0,0 +1,53 @@ +!import base as B +!import native_math as N +!import http as HTTP +!import io_v2 as IO +!export recvHeaders +!export readBody +!export respond +!export handle + +-- Reuse pure HTTP grammar/limits. The header cap covers the entire accumulated +-- receive buffer, including prefetched body bytes, exactly as in the v1 reader. +receiveHeaders client buffer count = IO.bind (IO.readSome client 4096) (reply : case reply of + ((t t), (("data", chunk), t)) = let + next = B.append buffer chunk + size = N.add count (HTTP.httpBodyLength chunk) + in case N.lte size HTTP.maxHeaderBytes of + (t t) = case B.contains? HTTP.crlfcrlf next of + (t t) = IO.pure (B.ok next t) + _ = receiveHeaders client next size + _ = IO.pure (B.err 431 t) + _ = IO.pure (B.err 400 t)) +recvHeaders client = receiveHeaders client [] 0 + +readState _ (0, (reversed, rest)) = IO.pure (B.ok (B.reverse reversed) rest) +readState client (remaining, (reversed, _)) = IO.bind + (IO.readSome client (HTTP.recvChunkMax4096 remaining)) (reply : case reply of + ((t t), (("data", chunk), t)) = readState client (HTTP.takeBodyBytes chunk remaining reversed) + _ = IO.pure (B.err 400 "Unexpected end of request body\n")) +readState _ _ = IO.pure HTTP.httpBadRequest +readBody client fields initial = case HTTP.contentLength fields of + ((t t), (t, _)) = IO.pure (B.ok [] initial) + ((t t), ((t count), _)) = readState client (HTTP.takeBodyBytes initial count []) + failure = IO.pure failure + +-- The connection's owning scope closes it even on partial write/failure. Do not +-- retry uncertain writes or let a cleanup failure turn into a second response. +send client bytes = case HTTP.takeBodyBytes bytes 4096 [] of + (_, ([], _)) = IO.pure t + (_, (reversed, rest)) = IO.require (IO.writeAll client (B.reverse reversed)) (_ : send client rest) + _ = IO.fail "Invalid response byte list" +respond client method response = send client (HTTP.responseForMethod method response) +body router client method target fields ((t t), (bytes, _)) = IO.bind + (router (HTTP.routerMethod method) target fields bytes) (respond client method) +body _ client method _ _ (t, (status, message)) = respond client method (HTTP.errorResponse status message) +body _ client method _ _ _ = respond client method (HTTP.badRequestResponse "Bad Request\n") +fields router client method target version raw = case HTTP.parseHeaders raw of + ((t t), (headers, initial)) = case HTTP.validateRequest method target version headers of + ((t t), _) = IO.bind (readBody client headers initial) (body router client method target headers) + (t, (status, message)) = respond client method (HTTP.errorResponse status message) + _ = respond client method (HTTP.badRequestResponse "Bad Request\n") + _ = respond client "" (HTTP.badRequestResponse "Bad Request\n") +handle router client ((t t), ((method, (target, version)), raw)) = fields router client method target version raw +handle _ client _ = respond client "" (HTTP.badRequestResponse "Bad Request\n") diff --git a/lib/io-v2/objects.tri b/lib/io-v2/objects.tri @@ -0,0 +1,85 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export putTree +!export getTree +!export validDigest + +-- Source-owned indexed-object policy. The store root must already exist; its +-- creation/parent durability belongs to the caller. Root paths use OS resolution; +-- callers must account for symlinks, renames and external mutable writers. +hexDigit value = case value of + 48 = t t + 49 = t t + 50 = t t + 51 = t t + 52 = t t + 53 = t t + 54 = t t + 55 = t t + 56 = t t + 57 = t t + 97 = t t + 98 = t t + 99 = t t + 100 = t t + 101 = t t + 102 = t t + _ = t + +validDigest value = let rec + walk 0 [] = t t + walk 0 _ = t + walk count [c | rest] = case hexDigit c of + (t t) = walk (N.predecessor count) rest + _ = t + walk _ _ = t + in walk 64 value + +shard [a b c | _] = [a b c] +shard _ = [] +objects root = B.append root "/objects" +shardPath root digest = B.append (objects root) (B.pair 47 (shard digest)) +objectPath root digest = B.append (shardPath root digest) (B.pair 47 digest) + +-- Verify every winner, including a new publication, byte-for-byte and through +-- EOF. Sync the verified open inode and its containing directory before returning +-- a digest that callers may publish in a manifest/cache/alias. Failed verification +-- leaves the winner intact. Durability follows OS/filesystem synchronization +-- guarantees; external writers can mutate paths concurrently. +verify owner root limit digest tree = do IO.onOk + reader <- IO.openReader owner (objectPath root digest) + IO.verifyIndexedTree reader limit tree + end <- IO.readSome reader 1 + case end of + ("eof", t) = do IO.onOk + IO.syncFile reader + IO.syncDirectory (shardPath root digest) + IO.pure (B.ok digest t) + _ = IO.pure (B.err ["invalid-data" "objects/verify" "trailing bytes" "unknown"] t) + +putTree root limit tree = IO.scope (owner : do IO.onOk + IO.syncDirectory root + info <- IO.hashIndexedTree limit tree + case info of + [size digest] = do IO.onOk + IO.ensureDirectory (objects root) + IO.syncDirectory root + IO.ensureDirectory (shardPath root digest) + IO.syncDirectory (objects root) + stage <- IO.openStage owner (shardPath root digest) + IO.writeIndexedTree stage limit digest tree + IO.sealStage stage + status <- IO.publishNew stage (objectPath root digest) + case status of + "published" = verify owner root limit digest tree + "exists" = verify owner root limit digest tree + _ = IO.fail "Invalid publication reply" + _ = IO.fail "Invalid indexed hash reply") + +getTree root limit digest = case validDigest digest of + (t t) = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner (objectPath root digest) + size <- IO.fileSize reader + IO.readIndexedTree reader root limit size digest) + _ = IO.pure (B.err ["invalid-argument" "objects/get" "invalid CAS digest" "none"] t) diff --git a/lib/io-v2/storage.tri b/lib/io-v2/storage.tri @@ -0,0 +1,147 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export readBytes +!export hashBytes +!export publishBytes +!export readTree +!export publishTree +!export parentPath + +-- Caller-selected limits and directories. Whole byte lists are resident source +-- values; transport chunks have a fixed bound. Paths follow OS authority. +problem op detail = ["invalid-data" op detail "unknown"] +checkedLimit limit next = case B.isNat? limit of + (t t) = case N.lte limit 9223372036854775807 of + (t t) = next limit + _ = IO.pure (B.err ["invalid-bound" "storage/limit" "invalid byte limit" "none"] t) + _ = IO.pure (B.err ["invalid-bound" "storage/limit" "invalid byte limit" "none"] t) +split 0 rest = [[] rest] +split _ [] = [[] []] +split count [c | rest] = case split (N.predecessor count) rest of + [prefix after] = [(B.pair c prefix) after] + _ = [] +split _ _ = [] +flatten chunks = B.foldr (suffix chunk : B.append chunk suffix) [] (B.reverse chunks) +readCount 0 = 1 +readCount remaining = case N.lte remaining 4096 of + (t t) = remaining + _ = 4096 +readLoop reader remaining chunks = do IO.onOk + received <- IO.readSome reader (readCount remaining) + case received of + ("eof", t) = IO.pure (B.ok (flatten chunks) t) + ("data", bytes) = case N.lte (B.length bytes) remaining of + (t t) = readLoop reader (N.subtract remaining (B.length bytes)) (B.pair bytes chunks) + _ = IO.pure (B.err ["invalid-bound" "storage/read" "file exceeds byte limit" "unknown"] t) + _ = IO.fail "Invalid storage read reply" +-- May consume one excess byte in the EOF probe at the budget boundary. +readBytes path limit = checkedLimit limit (count : IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + readLoop reader count [])) + +hashLoop digest remaining bytes = case split 4096 bytes of + [[] []] = IO.finishDigest digest + [part rest] = case N.lte (B.length part) remaining of + (t t) = do IO.onOk + IO.updateDigest digest part + hashLoop digest (N.subtract remaining (B.length part)) rest + _ = IO.pure (B.err ["invalid-bound" "storage/hash" "bytes exceed limit" "none"] t) + _ = IO.pure (B.err ["invalid-argument" "storage/hash" "improper byte list" "none"] t) +hashBytes limit bytes = checkedLimit limit (count : IO.scope (owner : do IO.onOk + digest <- IO.openDigest owner "sha256" + hashLoop digest count bytes)) +writeLoop writer bytes = case split 4096 bytes of + [[] []] = IO.pure (B.ok t t) + [part rest] = do IO.onOk + IO.writeAll writer part + writeLoop writer rest + _ = IO.fail "Invalid preflighted byte list" + +verifyBytes reader remaining = do IO.onOk + received <- IO.readSome reader 4096 + case received of + ("eof", t) = case remaining of + [] = IO.pure (B.ok t t) + _ = IO.pure (B.err (problem "storage/verify" "different bytes") t) + ("data", bytes) = case split (B.length bytes) remaining of + [expected rest] = case B.equal? bytes expected of + (t t) = verifyBytes reader rest + _ = IO.pure (B.err (problem "storage/verify" "different bytes") t) + _ = IO.fail "Invalid preflighted byte list" + _ = IO.fail "Invalid verification read reply" +verified owner directory destination verify created = do IO.onOk + reader <- IO.openReader owner destination + verify reader + IO.syncFile reader + IO.syncDirectory directory + IO.pure (B.ok created t) +-- Immutable names verify every winner, through EOF, before syncing its opened +-- inode and directory. Mismatches remain intact for diagnosis. Mutable +-- replacement acknowledges its own stage publication; a concurrent writer can +-- legitimately replace the alias immediately afterward. +publishBytes directory destination replace limit bytes = case replace of + 0 = do IO.onOk + hashBytes limit bytes + IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + writeLoop stage bytes + IO.sealStage stage + status <- IO.publishNew stage destination + case status of + "published" = verified owner directory destination (reader : verifyBytes reader bytes) (t t) + "exists" = verified owner directory destination (reader : verifyBytes reader bytes) t + _ = IO.fail "Invalid publication reply") + 1 = do IO.onOk + hashBytes limit bytes + IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + writeLoop stage bytes + IO.sealStage stage + IO.publishReplace stage destination + IO.pure (B.ok (t t) t)) + _ = IO.pure (B.err ["invalid-argument" "storage/publish" "invalid replacement flag" "none"] t) + +verifyTree reader limit tree = do IO.onOk + IO.verifyIndexedTree reader limit tree + end <- IO.readSome reader 1 + case end of + ("eof", t) = IO.pure (B.ok t t) + _ = IO.pure (B.err (problem "storage/verify" "trailing bytes") t) +publishTree directory destination limit expected tree = do IO.onOk + info <- IO.hashIndexedTree limit tree + case info of + [_ actual] = case B.equal? actual expected of + (t t) = IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + IO.writeIndexedTree stage limit expected tree + IO.sealStage stage + status <- IO.publishNew stage destination + case status of + "published" = verified owner directory destination (reader : verifyTree reader limit tree) (t t) + "exists" = verified owner directory destination (reader : verifyTree reader limit tree) t + _ = IO.fail "Invalid publication reply") + _ = IO.pure (B.err ["integrity-failed" "storage/publish" "digest mismatch" "none"] t) + _ = IO.fail "Invalid indexed hash reply" +readTree directory path limit expected = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + size <- IO.fileSize reader + IO.readIndexedTree reader directory limit size expected) + +-- POSIX lexical parent, retaining separators and literal '..' components. +-- Callers pass complete paths to the native path validators for OS resolution. +slashes [] = t t +slashes [47 | rest] = slashes rest +slashes _ = t +trimSeparators [47 | rest] = trimSeparators rest +trimSeparators rest = rest +parentReverse [] = "." +parentReverse [47 | rest] = case slashes rest of + (t t) = B.pair 47 (B.reverse rest) + _ = B.reverse rest +parentReverse [_ | rest] = parentReverse rest +parentReverse _ = "." +parentPath [] = "." +parentPath path = case trimSeparators (B.reverse path) of + [] = path + rest = parentReverse rest diff --git a/lib/io-v2/text.tri b/lib/io-v2/text.tri @@ -0,0 +1,148 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export readSource +!export readText +!export encodeText +!export writeText +!export lineCursor +!export readLine + +problem operation detail progress = ["invalid-data" operation detail progress] +-- base.foldr supplies the accumulator first. +flatten chunks = B.foldr (suffix chunk : B.append chunk suffix) [] (B.reverse chunks) + +checkedLimit limit next = case B.checkContract B.nat? limit of + ((t t), (count, t)) = case N.lte count 9223372036854775807 of + (t t) = next count + _ = IO.pure (B.err ["invalid-bound" "text/limit" "invalid text byte limit" "none"] t) + _ = IO.pure (B.err ["invalid-bound" "text/limit" "invalid text byte limit" "none"] t) + +-- Split a bounded prefix; native encode validates its scalars before producing +-- bytes. [] signals an improper character spine. +split 0 rest = [[] rest] +split _ [] = [[] []] +split count [c | rest] = case split (N.predecessor count) rest of + [prefix after] = [(B.pair c prefix) after] + _ = [] +split _ _ = [] + +encodeLoop remaining text chunks = case split 1024 text of + [[] []] = IO.pure (B.ok (flatten chunks) t) + [part rest] = do IO.onOk + bytes <- IO.encodeUTF8 part + case N.lte (B.length bytes) remaining of + (t t) = encodeLoop (N.subtract remaining (B.length bytes)) rest (B.pair bytes chunks) + _ = IO.pure (B.err ["invalid-bound" "text/encode" "encoded text exceeds byte limit" "none"] t) + _ = IO.pure (B.err (problem "text/encode" "improper scalar list" "none") t) +encodeText limit text = checkedLimit limit (remaining : encodeLoop remaining text []) + +-- Force the small acknowledged-byte counter before the next request, without +-- retaining arithmetic that depends on previously emitted chunks. +forceCount t next = next t +forceCount (t bit rest) next = case bit of + t = forceCount rest next + (t t) = forceCount rest next + _ = IO.fail "Invalid text counter" +forceCount _ _ = IO.fail "Invalid text counter" +writeFailure confirmed cause = IO.pure (B.err ["write-text" confirmed cause] t) +writeLoop writer text confirmed = forceCount confirmed (_ : case split 1024 text of + [[] []] = IO.pure (B.ok t t) + [part rest] = IO.bind (IO.encodeUTF8 part) (encoded : case encoded of + ((t t), (bytes, t)) = IO.bind (IO.writeAll writer bytes) (written : case written of + ((t t), (t, t)) = writeLoop writer rest (N.add confirmed (B.length bytes)) + (t, (cause, t)) = writeFailure confirmed cause + _ = IO.fail "Invalid text write reply") + (t, (cause, t)) = writeFailure confirmed cause + _ = IO.fail "Invalid UTF-8 encode reply") + _ = writeFailure confirmed (problem "text/write" "improper scalar list" "none")) +writeText writer text = writeLoop writer text 0 + +-- Whole-source convenience retaining the exact received octets and decoded +-- scalars. Source policy rejects an initial BOM. A one-byte EOF probe detects +-- excess input at the budget boundary. Callers serialize use of this cursor +-- across the complete operation; native leases cover individual reads. +-- Composite failure retains confirmed input +-- bytes (before any uncertain failing read) plus the unchanged native problem. +readFailure consumed cause = IO.pure (B.err ["read-source" consumed cause] t) +readCount 0 = 1 +readCount remaining = case N.lte remaining 4093 of + (t t) = remaining + _ = 4093 +readLoop reader limit remaining carry first rawChunks textChunks = + IO.bind (IO.readSome reader (readCount remaining)) (received : case received of + ((t t), (("eof", t), t)) = IO.bind (IO.decodeUTF8 1 carry) (decoded : case decoded of + ((t t), ([[] []], t)) = IO.pure (B.ok (B.pair (flatten rawChunks) (flatten textChunks)) t) + (t, (cause, t)) = readFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid UTF-8 final reply") + ((t t), (("data", bytes), t)) = let count = B.length bytes in + case N.lte count remaining of + (t t) = IO.bind (IO.decodeUTF8 0 (B.append carry bytes)) (decoded : case decoded of + ((t t), ([text pending], t)) = case first of + 1 = case text of + [] = readLoop reader limit (N.subtract remaining count) pending 1 (B.pair bytes rawChunks) textChunks + [65279 | _] = readFailure (N.add (N.subtract limit remaining) count) + (problem "text/read-source" "initial source BOM is not permitted" "none") + _ = readLoop reader limit (N.subtract remaining count) pending 0 (B.pair bytes rawChunks) (B.pair text textChunks) + _ = readLoop reader limit (N.subtract remaining count) pending 0 (B.pair bytes rawChunks) (B.pair text textChunks) + (t, (cause, t)) = readFailure (N.add (N.subtract limit remaining) count) cause + _ = IO.fail "Invalid UTF-8 decode reply") + _ = readFailure (N.add (N.subtract limit remaining) count) + ["invalid-bound" "text/read-source" "source exceeds byte limit" "none"] + (t, (cause, t)) = readFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid source read reply") +readSource reader limit = checkedLimit limit (count : readLoop reader count count [] 1 [] []) + +-- Single-consumer source cursor represented as a source value. Thread the +-- returned cursor forward, serialize its use with all reads on the underlying +-- reader, and retire it on read failure. Read-ahead is bounded to 4093 bytes. +lineCursor reader = [reader [] 0] + +-- The byte budget counts line content; LF delimits lines. CR, BOM and NUL +-- remain content. +-- Scan before decoding: malformed bytes after LF belong to the NEXT line. +lineSplit [] = [[] [] 0] +lineSplit [10 | rest] = [[] rest 1] +lineSplit [byte | rest] = case lineSplit rest of + [prefix after ended] = [(B.pair byte prefix) after ended] + _ = [] +lineSplit _ = [] +lineFailure consumed cause = IO.pure (B.err ["read-line" consumed cause] t) +lineReady reader after ending chunks = IO.pure + (B.ok [["line" (flatten chunks) ending] [reader after 0]] t) +lineEOF reader consumed carry chunks = IO.bind (IO.decodeUTF8 1 carry) (decoded : case decoded of + ((t t), ([[] []], t)) = case consumed of + 0 = IO.pure (B.ok [["eof"] [reader [] 1]] t) + _ = IO.pure (B.ok [["line" (flatten chunks) "eof"] [reader [] 1]] t) + (t, (cause, t)) = lineFailure consumed cause + _ = IO.fail "Invalid line final UTF-8 reply") +lineLoop reader limit remaining carry chunks buffered = forceCount remaining (_ : case buffered of + [] = IO.bind (IO.readSome reader 4093) (received : case received of + ((t t), (("eof", t), t)) = lineEOF reader (N.subtract limit remaining) carry chunks + ((t t), (("data", bytes), t)) = lineLoop reader limit remaining carry chunks bytes + (t, (cause, t)) = lineFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid line read reply") + _ = case lineSplit buffered of + [part after ended] = let count = B.length part in + case N.lte count remaining of + (t t) = IO.bind (IO.decodeUTF8 ended (B.append carry part)) (decoded : case decoded of + ((t t), ([text pending], t)) = case ended of + 1 = lineReady reader after "lf" (B.pair text chunks) + _ = lineLoop reader limit (N.subtract remaining count) pending (B.pair text chunks) [] + (t, (cause, t)) = lineFailure (N.add (N.subtract limit remaining) count) cause + _ = IO.fail "Invalid line UTF-8 reply") + _ = lineFailure (N.add (N.subtract limit remaining) count) + ["invalid-bound" "text/read-line" "line exceeds byte limit" "none"] + _ = IO.fail "Invalid line cursor bytes") +readLine cursor limit = checkedLimit limit (count : case cursor of + [reader buffered 0] = lineLoop reader count count [] [] buffered + [reader [] 1] = IO.pure (B.ok [["eof"] cursor] t) + _ = IO.pure (B.err ["invalid-argument" "text/read-line" "invalid line cursor" "none"] t)) + +-- Generic bounded whole-text convenience preserving an initial BOM as content. +-- It uses the shared incremental decoder with this text policy. +readText reader limit = checkedLimit limit (count : IO.onOk + (readLoop reader count count [] 0 [] []) + (received : case received of + (_, text) = IO.pure (B.ok text t) + _ = IO.fail "Invalid text read result")) diff --git a/lib/io.tri b/lib/io.tri @@ -1,184 +1,4 @@ -!import base * -!export * +!export * from io_v2 --- IO constructors for host-interpreted interaction trees. --- Free-monad style: Bind is the single sequencing mechanism. - -version = 1 - -io = action : pair "tricuIO" (pair version action) - -pure = x : pair 0 x -bind = action k : pair 1 (pair action k) --- Terminal diagnostic control for the Haskell IO scheduler. -abortIO = diagnostic : pair 2 diagnostic - -putStr = s : pair 10 s -getLine = pair 11 t - --- Source boundary only: Result (raw octets, Unicode scalar list), strict UTF-8. --- Initial BOM is rejected; binary readFile remains unchanged. -readSourceFile = p : pair 37 p -encodeSourceText = chars : pair 38 chars - -readFile = p : pair 20 p -writeFile = p c : pair 21 (pair p c) -putBytes = bs : pair 12 bs -writeBytes = p c : pair 22 (pair p c) - -listDirectory = p : pair 23 p -renameFile = old new : pair 24 (pair old new) -createDirectory = p : pair 25 p -deleteFile = p : pair 26 p -fileExists = p : pair 27 p - -sha256Hex = bs : pair 28 bs -currentTime = pair 29 t --- Filesystem-only atomic primitives. writeBytesIfAbsent returns ok true --- for a new object and ok false if the path already exists, without changing --- it. atomicWriteBytes returns ok true after atomic replacement of an alias. --- Hosts sync file contents before publication and the parent directory --- afterwards. Publish referenced objects before replacing a lock or alias; --- a post-publication sync failure can leave the new name visible. -writeBytesIfAbsent = p bs : pair 32 (pair p bs) -atomicWriteBytes = p bs : pair 33 (pair p bs) - --- Read-permitted canonical tree-term transport. Verify the fixed --- arboricx.tree-term.v1 digest before decoding; returns Result tree. --- This loads an inert normal-form tree, never source or an applied program. -readTreeTerm = p digest : pair 34 (pair p digest) - --- Fixed canonical tree-term domain. No byte-list marshalling. Publication --- requires read and write permission, never replaces an existing path, and --- verifies its bytes even when another publisher wins. Directories are explicit. -hashTreeTerm = tree : pair 35 tree -writeTreeTermIfAbsent = p digest tree : pair 36 (pair p (pair digest tree)) - --- Name-neutral indexed DAG transport under arboricx.indexed-tree.v1. --- These primitive codecs preserve sharing; tricu chooses paths and publication --- order. Read verifies before decode; write verifies an existing/racing winner. --- Both read and write permission are required for no-replace publication. -readIndexedTree = p digest : pair 50 (pair p digest) -hashIndexedTree = tree : pair 51 tree -writeIndexedTreeIfAbsent = p digest tree : pair 52 (pair p (pair digest tree)) - -ask = pair 30 t -local = f action : pair 31 (pair f action) - -get = pair 40 t -put = s : pair 41 s - -fork = action : pair 60 action -await = handle : pair 61 handle -yield = pair 62 t -sleep = ms : pair 63 ms - --- --------------------------------------------------------------------------- --- Generic sequencing combinators --- --------------------------------------------------------------------------- - -thenIO = a b : bind a (_ : b) -mapIO = action f : bind action (x : pure (f x)) -void = action : bind action (_ : pure t) - --- --------------------------------------------------------------------------- --- Conditional execution --- --------------------------------------------------------------------------- - -when = cond action : matchBool action (pure t) cond -unless = cond action : matchBool (pure t) action cond - --- --------------------------------------------------------------------------- --- Infinite loop --- --------------------------------------------------------------------------- - -forever action = bind action (_ : forever action) - --- --------------------------------------------------------------------------- --- Result-aware combinators --- --------------------------------------------------------------------------- - --- Propagate driver Result on error; run okCase on success. -onOk action okCase = (do bind - reply <- action - matchResult (_ _ : pure reply) okCase reply) - --- Value-only adapter: success remainder is explicitly discarded. -onOk_ action okCase = onOk action (value _ : okCase value) - -onResult action errCase okCase = (do bind - reply <- action - matchResult errCase okCase reply) - --- Value-only handlers explicitly discard both Result remainders. -onResult_ action errCase okCase = (onResult action - (message _ : errCase message) - (value _ : okCase value)) - -mapErrIO prefix action = - onResult_ action - (e : pure (err (append prefix e) t)) - (v : pure (ok v t)) - --- --------------------------------------------------------------------------- --- Convenience helpers --- --------------------------------------------------------------------------- - -print = s : void (putStr s) -putStrLn = s : void (putStr (append s "\n")) - --- --------------------------------------------------------------------------- --- Result-aware file helpers --- --------------------------------------------------------------------------- - -onReadFile = path : onResult (readFile path) - -onWriteFile = path contents : onResult (writeFile path contents) - -onListDirectory = path : onResult (listDirectory path) -onRenameFile = old new : onResult (renameFile old new) -onCreateDirectory = path : onResult (createDirectory path) -onDeleteFile = path : onResult (deleteFile path) -onFileExists = path : onResult (fileExists path) -onSha256Hex = bs : onResult (sha256Hex bs) -onCurrentTime = onResult currentTime - --- --------------------------------------------------------------------------- --- Convenience helpers for the common cases --- --------------------------------------------------------------------------- - -readFileOrPrintError = (path okCase : - onReadFile path - (err rest : putStrLn (append "Read failed: " err)) - okCase) - -writeFileOrPrintError = (path contents okCase : - onWriteFile path contents - (err rest : putStrLn (append "Write failed: " err)) - okCase) - -copyFile = (src dst : - onResult (readFile src) - (err rest : putStrLn (append "Read failed: " err)) - (contents rest : - onResult (writeFile dst contents) - (err rest : putStrLn (append "Write failed: " err)) - (_ _ : pure t))) - --- --------------------------------------------------------------------------- --- Ordinary-completion cleanup (not exception masking or abort recovery) --- --------------------------------------------------------------------------- - --- Run cleanup once after action returns any ordinary value/Result, including --- failure; ignore cleanup's ordinary result and preserve the action's result. --- Terminal abortIO, divergence and host exceptions can prevent cleanup. -finally action cleanup = (do bind - result <- action - cleanup - pure result) - --- Acquisition returns a Result. Failure propagates unchanged without use or --- release; success supplies the resource value (not the Result envelope). --- Use the same ordinary-completion/cleanup-result policy as finally. -bracket acquire release use = (onOk_ acquire - (resource : finally (use resource) (release resource))) +-- Canonical application IO is protocol 2. Scoped ownership and OS process +-- authority replace the retired grant, environment and mutable-state actions. diff --git a/lib/packages/io-v2.tri b/lib/packages/io-v2.tri @@ -0,0 +1,17 @@ +!import io_v2 as I +!import io_v2_storage as Storage +!import io_v2_text as Text +!export io +!export bind +!export readBytes +!export sha256Hex +!export putStr +!export abortIO +-- Explicit native-v2 package candidate. The published v1 wrapper stays separate. +-- Whole-value reads/hashes require a caller-selected budget; no runtime grants. +io = I.io +bind = I.bind +readBytes path limit = Storage.readBytes path limit +sha256Hex bytes limit = Storage.hashBytes limit bytes +putStr text = I.require I.stdout (writer : I.require (Text.writeText writer text) I.pure) +abortIO = I.fail diff --git a/lib/packages/io.tri b/lib/packages/io.tri @@ -1,16 +1,16 @@ -!import io as I +!import io_v2 as I +!import io_v2_storage as Storage +!import io_v2_text as Text !export io !export bind -!export readFile +!export readBytes !export sha256Hex !export putStr !export abortIO - --- Construct interaction trees only. Importing this package grants no effects; --- the Haskell host still requires explicit application IO permissions. +-- Packaged IO v2 facade. Reads and hashes take an explicit byte budget. io = I.io bind = I.bind -readFile = I.readFile -sha256Hex = I.sha256Hex -putStr = I.putStr -abortIO = I.abortIO +readBytes path limit = Storage.readBytes path limit +sha256Hex bytes limit = Storage.hashBytes limit bytes +putStr text = I.require I.stdout (writer : I.require (Text.writeText writer text) I.pure) +abortIO = I.fail diff --git a/lib/patterns.tri b/lib/patterns.tri @@ -3,7 +3,7 @@ -- Ordered predicate/action rows. Each row is exactly [predicate action]. -- First match wins; no match or a malformed reached row returns Leaf. --- Unselected actions and subsequent rows are not evaluated. +-- Unselected actions and subsequent rows remain lazy. match value rules = (let rec walk remaining = case remaining of [rule | rest] = (case rule of @@ -16,7 +16,7 @@ match value rules = (let rec otherwise = const true --- guard stores thunks, rather than a predicate/action on a shared value. +-- cond stores test/action thunks, each called with Leaf. cond rules = (let rec walk remaining = case remaining of [(testK, actionK) | rest] = (case testK t of diff --git a/lib/socket.tri b/lib/socket.tri @@ -1,94 +1,18 @@ -!import prelude * -!import io * +!import io_v2 as IO !export * --- Socket primitives for the IO driver. --- ok value t -- pair true (pair value t) --- err msg t -- pair false (pair msg t) - -socket = pair 70 t -closeSocket = sock : pair 71 sock -bindSocket = sock addr port : pair 72 (pair sock (pair addr port)) -listen = sock backlog : pair 73 (pair sock backlog) -accept = sock : pair 74 sock -connect = sock addr port : pair 75 (pair sock (pair addr port)) -recv = sock maxBytes : pair 76 (pair sock maxBytes) -send = sock bytes : pair 77 (pair sock bytes) -getSocketName = sock : pair 78 sock - --- Result-aware wrappers over raw socket actions -onSocket = onResult socket -onBindSocket = sock addr port : onResult (bindSocket sock addr port) -onListen = sock backlog : onResult (listen sock backlog) -onAccept = sock : onResult (accept sock) -onConnect = sock addr port : onResult (connect sock addr port) -onRecv = sock maxBytes : onResult (recv sock maxBytes) -onSend = sock bytes : onResult (send sock bytes) -onGetSocketName = sock : onResult (getSocketName sock) - --- Result-aware wrappers that drop the 'rest' parameter -onSocket_ = onResult_ socket -onBindSocket_ = sock addr port : onResult_ (bindSocket sock addr port) -onListen_ = sock backlog : onResult_ (listen sock backlog) -onAccept_ = sock : onResult_ (accept sock) -onConnect_ = sock addr port : onResult_ (connect sock addr port) -onRecv_ = sock maxBytes : onResult_ (recv sock maxBytes) -onSend_ = sock bytes : onResult_ (send sock bytes) -onGetSocketName_ = sock : onResult_ (getSocketName sock) - --- Close a socket, ignoring errors. -closeSocket_ = sock : void (closeSocket sock) - --- Close only on an ordinary Result failure, preserving its exact message and --- remainder even if close fails. Not an exception/abortIO cleanup mechanism; --- ownership of a successful socket transfers to the caller. -closeSocketOnError sock action = (do bind - reply <- action - case reply of - (t, _) = finally (pure reply) (closeSocket_ sock) - _ = pure reply) - --- Create a listening socket bound to an address and port. --- Returns ok listenSocket or err message. An acquired socket is closed if --- binding or listening fails; acquisition failure has nothing to release. -listenSocket addr port backlog = (do onOk_ - server <- socket - closeSocketOnError server (do onOk_ - bindSocket server addr port - listen server backlog - pure (ok server t))) - --- Accept a connection with explicit error and ok branches. --- okHandler receives (clientSocket, peerAddr). -withAccepted = (server errHandler okHandler : - onResult (accept server) - errHandler - (accepted rest : - okHandler (fst accepted) (snd accepted))) - --- Same as withAccepted, but handlers drop the useless 'rest' parameter. -withAccepted_ = (server errHandler okHandler : - onResult_ (accept server) - errHandler - (accepted : - okHandler (fst accepted) (snd accepted))) - -serveOnce = (server handler : - withAccepted_ server - (err : pure t) - (client peer : - handler client peer)) - -serveForkingOnce = (server handler : - withAccepted_ server - (err : pure t) - (client peer : - fork (handler client peer))) - -serveForever = (server handler : - forever (serveForkingOnce server handler)) - -connectTo addr port = (do onOk_ - client <- socket - closeSocketOnError client (onOk (connect client addr port) - (_ rest : pure (ok client rest)))) +-- Scoped TCP facilities. Owner arguments and Result replies are explicit; +-- sockets are resources, not reusable numeric descriptors or detached workers. +ipv4 = IO.ipv4 +ipv6 = IO.ipv6 +resolveTCP = IO.resolveTCP +listenTCP = IO.listenTCP +connectTCP = IO.connectTCP +acceptTCP = IO.acceptTCP +localAddress = IO.localAddress +peerAddress = IO.peerAddress +shutdownWrite = IO.shutdownWrite +readSome = IO.readSome +writeSome = IO.writeSome +writeAll = IO.writeAll +close = IO.close diff --git a/lib/source/build.tri b/lib/source/build.tri @@ -1,5 +1,5 @@ !import prelude * -!import io * +!import compiler_io * !import source_object * !import source_module * !import source_lock * diff --git a/lib/source/bundle.tri b/lib/source/bundle.tri @@ -1,5 +1,5 @@ !import prelude * -!import io * +!import compiler_io * !import source_object * !import source_module * !import source_resolve * diff --git a/lib/source/export-cache.tri b/lib/source/export-cache.tri @@ -1,5 +1,7 @@ !import prelude * -!import io * +!import compiler_io * +!import io_v2 as RuntimeIO +!import io_v2_text as Text !import source_object * !import source_module * !import source_lock * @@ -10,17 +12,20 @@ -- Opt-in bootstrap acceleration. The runner supplies its compiler identity; -- rewritten source fixes exact imported manifests. Indices are hints, not CAS. +sourceExportCacheLog line = RuntimeIO.require + (Text.writeText RuntimeIO.stdout (append line "\n")) + sourceExportCacheOne root scope name tree = onOk_ (sourceObjectDigest "arboricx.source-export-name.v1" name) (nameHash : - (path : thenIO (putStrLn (append "export-start=" name)) + (path : thenIO (sourceExportCacheLog (append "export-start=" name)) (onOk_ (fileExists path) (exists : lazyBool (_ : onOk_ (readFile path) (hash : onOk_ (sourceObjectGetTree root hash) (_ : - thenIO (putStrLn (append "export-reused=" name)) + thenIO (sourceExportCacheLog (append "export-reused=" name)) (pure (ok hash t))))) (_ : onOk_ (sourceObjectPutTree root tree) (hash : onOk_ (atomicWriteBytes path hash) (_ : - thenIO (putStrLn (append "export-complete=" + thenIO (sourceExportCacheLog (append "export-complete=" (append name (append "\t" hash)))) (pure (ok hash t))))) exists))) (append scope (append "/" nameHash))) diff --git a/lib/source/io-v2.tri b/lib/source/io-v2.tri @@ -0,0 +1,51 @@ +!import base as B +!import io_v2 as IO +!import io_v2_storage as Storage +!import io_v2_text as Text +!export * + +-- Compiler storage policy over IO v2, imported explicitly as compiler_io. +-- Explicit application policy: resident source/metadata bytes and encoded trees +-- are limited to 64 MiB. Heap use, expanded-tree size and CPU work require +-- separate resource controls. +byteLimit = 67108864 +version = 2 +io = IO.io +pure = IO.pure +bind = IO.bind +abortIO = IO.fail +thenIO first second = bind first (_ : second) +mapIO action f = bind action (value : pure (f value)) +void action = bind action (_ : pure t) +onOk action next = bind action (reply : B.matchResult (_ _ : pure reply) next reply) +onOk_ action next = IO.onOk action next +onResult action failed succeeded = bind action (reply : B.matchResult failed succeeded reply) +onResult_ action failed succeeded = onResult action (problem _ : failed problem) (value _ : succeeded value) + +readFile path = Storage.readBytes path byteLimit +sha256Hex bytes = Storage.hashBytes byteLimit bytes +readSourceFile path = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + Text.readSource reader byteLimit) +encodeSourceText text = Text.encodeText byteLimit text +-- Compiler callers create directories in parent-first order. A missing ancestor +-- produces an error. Sync the parent entry before use. +createDirectory path = do IO.onOk + IO.ensureDirectory path + IO.syncDirectory (Storage.parentPath path) +fileExists path = bind (IO.statPath path) (reply : case reply of + ((t t), (["file" _], t)) = pure (B.ok (t t) t) + ((t t), (_, t)) = pure (B.ok t t) + (t, (["not-found" _ _ _], t)) = pure (B.ok t t) + _ = pure reply) +writeBytesIfAbsent path bytes = Storage.publishBytes (Storage.parentPath path) path 0 byteLimit bytes +atomicWriteBytes path bytes = Storage.publishBytes (Storage.parentPath path) path 1 byteLimit bytes +hashIndexedTree tree = do IO.onOk + info <- IO.hashIndexedTree byteLimit tree + case info of + [_ digest] = pure (B.ok digest t) + _ = IO.fail "Invalid indexed hash reply" +writeIndexedTreeIfAbsent path digest tree = + Storage.publishTree (Storage.parentPath path) path byteLimit digest tree +readIndexedTree path digest = + Storage.readTree (Storage.parentPath path) path byteLimit digest diff --git a/lib/source/lock.tri b/lib/source/lock.tri @@ -1,5 +1,5 @@ !import prelude * -!import io * +!import compiler_io * !import source_module * !import source_object * !import source_resolve * diff --git a/lib/source/map.tri b/lib/source/map.tri @@ -1,5 +1,5 @@ !import prelude * -!import io * +!import compiler_io * !import source_object * !import source_module * !import source_resolve * diff --git a/lib/source/module.tri b/lib/source/module.tri @@ -1,5 +1,5 @@ !import prelude * -!import io * +!import compiler_io * !import source_object * !import source_unit * !import source_resolve * diff --git a/lib/source/object.tri b/lib/source/object.tri @@ -1,5 +1,5 @@ !import prelude * -!import io * +!import compiler_io * !import native_math as N !export sourceObjectTreeDomain !export sourceObjectFlatTreeDomain diff --git a/lib/source/program.tri b/lib/source/program.tri @@ -40,10 +40,9 @@ sourceProgramDefinitionHead? tokens = (equal? (sourceTokenKind token) sourceTokenName)) tokens --- Bootstrap contract profile: definition heads only. Contract expressions --- are ordinary names or parenthesized applications, never static types. --- Keep the runtime Result checks (including short-circuit and value passing) --- identical to Frontend.ContractDesugar's supported base-library forms. +-- Contract annotations on plain definition heads. Contract expressions are +-- ordinary names or parenthesized applications. Lowering preserves Result +-- short-circuiting and passes each accepted/transformed value to the body. sourceProgramContract = parserOr (sourceGateParseGroup (tokens : sourceGateExprWithLayout tokens sourceGateLayoutNested)) (parserMap sourceAstName (sourceGateExpect sourceTokenName)) diff --git a/lib/source/stable.tri b/lib/source/stable.tri @@ -45,20 +45,19 @@ stableTermApply function argument location = (lazyBool -- tricu's stem rule is t (t a) b c = a c (b c), unlike the book's -- reversed b c (a c). Thus [x](u v) = t (t ([x]u)) ([x]v). -- The annotated path below may prune independent constructor-only subtrees. --- Literal payloads are represented trees, not deferred applications. +-- Literal payloads are represented trees. stableApplication left right location = (stableTermApply (stableTermApply (sourceTermLiteral t location) (stableTermApply (sourceTermLiteral t location) left location) location) right location) -- Internal annotated residual: ((shape, minimum-free-index+1), ordinary node). --- Its application children are annotated, not ordinary sourceTerm values. --- Metadata is maintained as nodes are built, never by scanning a subtree at --- each bracket visit. A zero minimum denotes a closed subtree; one means the --- binder being eliminated occurs. Any larger minimum shifts down by one when +-- Its application children carry the same annotations. Metadata is maintained +-- incrementally as nodes are built. A zero minimum denotes a closed subtree; +-- one means the binder being eliminated occurs. Any larger minimum shifts down by one when -- removing an unused binder. Literal payloads are already represented trees. -- Shape certificates permit only Leaf/Stem construction with certified --- arguments. A variable is an opaque value, never a known callable shape. +-- arguments. A variable has an opaque shape. -- Fork applications, unknown calls and constructors containing computations -- remain residual; neither binder independence alone nor eta is sufficient. -- The ordinary residual API is restored at the public boundary. Keep both diff --git a/src/ContentStore/Indexed.hs b/src/ContentStore/Indexed.hs @@ -1,7 +1,8 @@ -- Name-neutral indexed object codec, shared by import, loading and publication. module ContentStore.Indexed - ( indexedTreeDomain, encodeIndexedTree, decodeIndexedTree ) where + ( indexedTreeDomain, encodeIndexedTree, decodeIndexedTree, decodeIndexedSnapshot ) where +import ContentStore.Snapshot (Snapshot) import ContentStore.Object import Runtime (T) import Wire @@ -21,6 +22,17 @@ encodeIndexedTree tree = encodeBundle <$> buildBundleShared [("root", tree)] decodeIndexedTree :: ByteString -> Either String T decodeIndexedTree payload = do bundle <- decodeBundle payload + decodeIndexedBundle bundle + +-- Metadata limits are caller policy; the returned tree still occupies memory +-- proportional to its indexed DAG. This operation does not evaluate the tree. +decodeIndexedSnapshot :: Int -> Int -> Snapshot -> IO (Either String T) +decodeIndexedSnapshot sections metadata snapshot = do + decoded <- decodeBundleSnapshot sections metadata snapshot + pure (decoded >>= decodeIndexedBundle) + +decodeIndexedBundle :: Bundle -> Either String T +decodeIndexedBundle bundle = do verifyBundle bundle root <- case (bundleRoots bundle, manifestExports $ bundleManifest bundle) of ([index], [exported]) diff --git a/src/ContentStore/Snapshot.hs b/src/ContentStore/Snapshot.hs @@ -0,0 +1,97 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE RankNTypes #-} +-- Internal authenticated transport for indexed codecs, not a store service. +-- No paths, source trees or application publication policy are interpreted here. +module ContentStore.Snapshot + ( Snapshot, snapshotLength, readSnapshot + , SnapshotError(..), withAuthenticatedSnapshot, withAuthenticatedSnapshotUsing + ) where + +import ContentStore.Object (Domain(..), ObjectHash, hashToText, validateObjectHash) +import Control.Concurrent.MVar +import Control.Exception +import Control.Monad (unless, when) +import Crypto.Hash (Context, SHA256, hashInit, hashUpdate, hashFinalize) +import qualified Data.ByteString as BS +import Data.Text.Encoding (encodeUtf8) +import System.Directory (removeFile) +import System.IO + +-- The constructor and writable handle never escape this module. A Snapshot is +-- valid only during the callback; its read operation serializes seek/read pairs. +-- This is not confinement against other code with the process's OS authority. +data Snapshot = Snapshot !Integer !(MVar Handle) + +snapshotLength :: Snapshot -> Integer +snapshotLength (Snapshot size _) = size + +data SnapshotError = InvalidSnapshotBound | InvalidSnapshotDigest + | SnapshotTruncated | SnapshotReaderExceededBound | SnapshotDigestMismatch + | InvalidSnapshotRange + deriving (Eq, Show) +instance Exception SnapshotError + +chunkSize :: Int +chunkSize = 4096 + +-- Both limits are checked before allocating a file or consuming input. The +-- caller supplies an EXACT frame length and a policy limit: neither an EOF probe +-- nor any byte of the following frame is requested. A reader must return between +-- zero and the requested number of bytes; zero before the boundary is truncation. +-- +-- Disk: one unlinked file, at most size payload bytes (+ filesystem bookkeeping). +-- Transfer memory: one bounded chunk and hash context, independent of size. +-- Authentication finishes before invoking the decoder callback, against the same +-- private bytes it will read, never a second pass over the original source. +-- No fsync is necessary: this is a temporary snapshot, not durable publication. +withAuthenticatedSnapshot :: FilePath -> Integer -> Integer -> Domain -> ObjectHash + -> (Int -> IO BS.ByteString) -> (Snapshot -> IO a) -> IO a +withAuthenticatedSnapshot = withAuthenticatedSnapshotUsing (\allocate use -> bracket allocate hClose use) + +-- Native structured IO supplies registry-backed lifetime management here, so a +-- terminal fault/cancellation leaves cleanup to lexical settlement (including its +-- diagnostic precedence), rather than allowing bracket cleanup to mask a fault. +withAuthenticatedSnapshotUsing :: (forall a. IO Handle -> (Handle -> IO a) -> IO a) + -> FilePath -> Integer -> Integer -> Domain -> ObjectHash + -> (Int -> IO BS.ByteString) -> (Snapshot -> IO b) -> IO b +withAuthenticatedSnapshotUsing lifetime directory limit size (Domain domain) expected receive consume = do + when (limit < 0 || size < 0 || size > limit || limit > 2^(63 :: Int)-1) $ + throwIO InvalidSnapshotBound + case validateObjectHash expected of + Left _ -> throwIO InvalidSnapshotDigest + Right () -> pure () + lifetime acquire $ \handle -> do + let initial = hashUpdate (hashInit :: Context SHA256) (encodeUtf8 domain <> BS.singleton 0) + copy !remaining !context + | remaining == 0 = pure context + | otherwise = do + let wanted = fromInteger (min remaining (toInteger chunkSize)) + bytes <- receive wanted + let count = BS.length bytes + when (count > wanted) (throwIO SnapshotReaderExceededBound) + when (count == 0) (throwIO SnapshotTruncated) + BS.hPut handle bytes + next <- evaluate (hashUpdate context bytes) + copy (remaining - toInteger count) next + context <- copy size initial + unless (hashToText (hashFinalize context) == expected) (throwIO SnapshotDigestMismatch) + hFlush handle + mutex <- newMVar handle + consume (Snapshot size mutex) + where + acquire = mask_ $ do + (path, handle) <- openBinaryTempFile directory ".tricu-snapshot-" + -- Unlink before reading any untrusted input. No named scratch artifact is + -- retained after successful acquisition, including on process termination. + (removeFile path >> hSetBuffering handle NoBuffering >> pure handle) + `onException` hClose handle + +-- Bounded positional reads from the authenticated snapshot. The supplied offset +-- is absolute; EOF is sticky by length, not inferred from the public input. +readSnapshot :: Snapshot -> Integer -> Int -> IO BS.ByteString +readSnapshot (Snapshot size mutex) offset count = do + when (offset < 0 || offset > size || count < 0 || count > chunkSize) $ + throwIO InvalidSnapshotRange + withMVar mutex $ \handle -> do + hSeek handle AbsoluteSeek offset + BS.hGet handle (fromInteger (min (toInteger count) (size - offset))) diff --git a/src/IODriver.hs b/src/IODriver.hs @@ -1,1619 +1,21 @@ -{-# LANGUAGE ScopedTypeVariables #-} +-- Version dispatch only. Historical envelopes remain inspectable data, but the +-- current host executes protocol 2 exclusively. No probing, retry or grants. +module IODriver (checkIOSentinel, runIO) where -module IODriver - ( IOPermissions(..) - , defaultPerms - , unsafePerms - , checkIOSentinel - , runIO - , runIOWithEnv - , runIOWith - , runIOWithYieldHook - , YieldStats(..) - , runIOWithYieldStatsHook - ) where - -import Runtime (T(..), EvaluatedForm(Decode), formatT, apply, toString, toNumber, ofString, ofNumber, ofBytes, toBytes, ofList) -import ContentStore.Arboricx (treeTermDomain, decodeTreeTermShared, encodeTreeTerm) -import ContentStore.Object (hashObject, validateObjectHash) -import ContentStore.Indexed (indexedTreeDomain, decodeIndexedTree, encodeIndexedTree) -import qualified Data.ByteString as BS -import System.IO (putStr, getLine) -import System.Posix.Files (createLink) -import System.Posix.IO (openFd, closeFd, defaultFileFlags, OpenMode(ReadOnly)) -import System.Posix.Unistd (fileSynchronise) -import qualified System.IO as IO -import Control.Exception (try, catch, bracket, bracketOnError, evaluate, IOException, SomeException, - SomeAsyncException, fromException, throwIO, mask_, finally) -import System.IO.Error (isDoesNotExistError, isPermissionError, isAlreadyExistsError) -import Data.List (isPrefixOf, isInfixOf) -import System.FilePath (addTrailingPathSeparator, takeDirectory) -import System.Directory (canonicalizePath, doesPathExist, listDirectory, createDirectory, renameFile, removeFile, doesDirectoryExist) -import Data.Time.Clock.POSIX (getPOSIXTime) -import Crypto.Hash (hash, SHA256, Digest) -import Data.ByteArray (convert) -import Data.ByteString.Base16 (encode) -import Data.Text.Encoding (decodeUtf8, decodeUtf8', encodeUtf8) -import qualified Data.Text as T -import Data.Char (toLower) -import qualified Data.Map.Strict as Map -import Data.Map.Strict (Map) -import qualified Data.Sequence as Seq -import Data.Sequence (Seq, (|>), ViewL(..)) -import Data.Time.Clock (UTCTime, getCurrentTime, addUTCTime, diffUTCTime) -import Control.Concurrent (threadDelay, forkIOWithUnmask, killThread, ThreadId, MVar, newEmptyMVar, putMVar, readMVar) -import Control.Concurrent.STM (TVar, newTVarIO, atomically, readTVar, writeTVar, modifyTVar', retry) -import qualified Data.Set as Set -import Data.Set (Set) -import qualified Data.Foldable as Fold -import qualified Network.Socket as NS -import qualified Network.Socket.ByteString as NSB - --- --------------------------------------------------------------------------- --- Permissions --- --------------------------------------------------------------------------- - -data IOPermissions = IOPermissions - { allowRead :: [FilePath] - , allowWrite :: [FilePath] - , allowReadAll :: Bool - , allowWriteAll :: Bool - } - deriving (Show) - -defaultPerms :: IOPermissions -defaultPerms = IOPermissions [] [] False False - -unsafePerms :: IOPermissions -unsafePerms = IOPermissions [] [] True True +import Runtime (T(..), ofString) +import qualified IOV2.Driver as V2 checkIOSentinel :: T -> Either String (Integer, T) -checkIOSentinel tree = - case tree of - Fork sentinel (Fork version action) -> do - s <- toString sentinel - case s of - "tricuIO" -> do - v <- toNumber version - return (v, action) - _ -> Left "sentinel mismatch (expected \"tricuIO\")" - _ -> Left "root is not an IO sentinel pair" - --- --------------------------------------------------------------------------- --- Runtime, Frames, and Machine --- --------------------------------------------------------------------------- - -data Runtime = Runtime - { rtPerms :: IOPermissions - , rtEnv :: T - , rtState :: T - } - deriving (Show) - -data Frame - = BindFrame T - | LocalFrame T - deriving (Show) - -data Machine = Machine - { machineRuntime :: Runtime - , machineCurrent :: T - , machineFrames :: [Frame] - } - deriving (Show) - --- --------------------------------------------------------------------------- --- Result convention --- --------------------------------------------------------------------------- --- Direct-return actions pass the raw value to the continuation: --- pure, bind, putStr, getLine, ask, local, get, put, --- fork, await, yield, sleep --- --- Result-return actions wrap the outcome as an ok/err pair: --- ok val = Fork (Stem Leaf) (Fork val Leaf) -- (t t) val t --- err code = Fork Leaf (Fork code Leaf) -- t code t --- readFile, writeFile --- --- Runtime protocol errors are returned as direct values via errResult. - -okResult :: T -> T -okResult val = Fork (Stem Leaf) (Fork val Leaf) - -errResult :: String -> T -errResult msg = Fork Leaf (Fork (ofString msg) Leaf) - -pureAction :: T -> T -pureAction x = Fork (ofNumber 0) x - -invalidAsyncHandleResult :: T -invalidAsyncHandleResult = errResult "invalid task handle" - -invalidSocketHandleResult :: T -invalidSocketHandleResult = errResult "invalid socket handle" - -selfAwaitResult :: T -selfAwaitResult = errResult "self await" - -deadlockResult :: T -deadlockResult = errResult "deadlock" - -invalidSleepResult :: T -invalidSleepResult = errResult "invalid sleep" - -ioErrorString :: IOException -> String -ioErrorString e - | isDoesNotExistError e = "does not exist" - | isPermissionError e = "permission denied" - | isAlreadyExistsError e = "already exists" - | otherwise = "io error" - --- --------------------------------------------------------------------------- --- Task identity and handles --- --------------------------------------------------------------------------- - -newtype TaskId = TaskId Integer - deriving (Eq, Ord, Show) - -taskHandle :: TaskId -> T -taskHandle (TaskId n) = - Fork (ofString "task") (ofNumber n) - -decodeTaskHandle :: T -> Either String TaskId -decodeTaskHandle tree = - case tree of - Fork tag nTree -> do - tagString <- toString tag - if tagString == "task" - then TaskId <$> toNumber nTree - else Left "invalid task handle tag" - _ -> - Left "invalid task handle" - --- --------------------------------------------------------------------------- --- Socket identity and handles --- --------------------------------------------------------------------------- - -newtype SockId = SockId Integer - deriving (Eq, Ord, Show) - -sockHandle :: SockId -> T -sockHandle (SockId n) = - Fork (ofString "sock") (ofNumber n) - -decodeSockHandle :: T -> Either String SockId -decodeSockHandle tree = - case tree of - Fork tag nTree -> do - tagString <- toString tag - if tagString == "sock" - then SockId <$> toNumber nTree - else Left "invalid socket handle tag" - _ -> - Left "invalid socket handle" - -getSocketPort :: NS.Socket -> IO (Maybe Integer) -getSocketPort sock = do - addr <- NS.getSocketName sock - case addr of - NS.SockAddrInet p _ -> return (Just (fromIntegral p)) - NS.SockAddrInet6 p _ _ _ -> return (Just (fromIntegral p)) - _ -> return Nothing - --- --------------------------------------------------------------------------- --- Socket registry --- --------------------------------------------------------------------------- - -data SocketRegistry = SocketRegistry - { sockMap :: Map SockId NS.Socket - , sockNextId :: Integer - } - --- Call with async exceptions masked from acquisition through registration. -registerSocket :: TVar SocketRegistry -> NS.Socket -> IO SockId -registerSocket registry sock = atomically $ do - SocketRegistry sockets next <- readTVar registry - let sid = SockId next - writeTVar registry (SocketRegistry (Map.insert sid sock sockets) (next + 1)) - pure sid - --- All OS workers belong to one run. Completed workers unregister immediately; --- cleanup cancels and joins the remaining workers before closing their sockets. -type Workers = TVar (Map ThreadId (MVar ())) - -startWorker :: Workers -> IO () -> IO () -startWorker workers action = mask_ $ do - start <- newEmptyMVar - done <- newEmptyMVar - tid <- forkIOWithUnmask $ \unmask -> do - self <- readMVar start - unmask action `finally` do - atomically $ modifyTVar' workers (Map.delete self) - putMVar done () - atomically $ modifyTVar' workers (Map.insert tid done) - putMVar start tid - -cleanupRun :: (TVar (Map TaskId T), TVar SocketRegistry, Workers) -> IO () -cleanupRun (_, sockets, workers) = do - pending <- atomically $ readTVar workers - mapM_ killThread (Map.keys pending) - mapM_ readMVar (Map.elems pending) - owned <- atomically $ do - SocketRegistry owned next <- readTVar sockets - writeTVar sockets (SocketRegistry Map.empty next) - pure owned - mapM_ (\sock -> NS.close sock `catch` (\(_ :: IOException) -> pure ())) owned - --- --------------------------------------------------------------------------- --- Free-monad action AST --- --------------------------------------------------------------------------- - -data Action - = APure T - | ABind T T - | AAbort T - | APutStr T - | APutBytes T - | AGetLine - | AReadSourceFile T - | AEncodeSourceText T - | AReadFile T - | AReadIndexedTree T T - | AHashIndexedTree T - | AWriteIndexedTreeIfAbsent T T T - | AReadTreeTerm T T - | AHashTreeTerm T - | AWriteTreeTermIfAbsent T T T - | AWriteFile T T - | AWriteBytes T T - | AWriteBytesIfAbsent T T - | AAtomicWriteBytes T T - | AListDirectory T - | ARenameFile T T - | ACreateDirectory T - | ADeleteFile T - | AFileExists T - | ASha256Hex T - | ACurrentTime - | AAsk - | ALocal T T - | AGet - | APut T - | AFork T - | AAwait T - | AYield - | ASleep T - | ASocket - | ACloseSocket T - | ABindSocket T T T - | AListen T T - | AAccept T - | AConnect T T T - | ARecv T T - | ASend T T - | AGetSocketName T - deriving (Show) - --- --------------------------------------------------------------------------- --- Action tag constants --- --------------------------------------------------------------------------- - -tagPure, tagBind, tagAbort :: Integer -tagPure = 0 -tagBind = 1 -tagAbort = 2 - -tagPutStr, tagPutBytes, tagGetLine :: Integer -tagPutStr = 10 -tagPutBytes = 12 -tagGetLine = 11 - -tagReadFile, tagWriteFile, tagWriteBytes :: Integer -tagReadFile = 20 -tagWriteFile = 21 -tagWriteBytes = 22 - -tagListDirectory, tagRenameFile, tagCreateDirectory, tagDeleteFile, tagFileExists :: Integer -tagListDirectory = 23 -tagRenameFile = 24 -tagCreateDirectory = 25 -tagDeleteFile = 26 -tagFileExists = 27 - -tagSha256Hex, tagCurrentTime, tagWriteBytesIfAbsent, tagAtomicWriteBytes :: Integer -tagSha256Hex = 28 -tagCurrentTime = 29 -tagWriteBytesIfAbsent = 32 -tagAtomicWriteBytes = 33 - -tagReadTreeTerm :: Integer -tagReadTreeTerm = 34 - -tagHashTreeTerm, tagWriteTreeTermIfAbsent :: Integer -tagHashTreeTerm = 35 -tagWriteTreeTermIfAbsent = 36 - -tagReadSourceFile, tagEncodeSourceText :: Integer -tagReadSourceFile = 37 -tagEncodeSourceText = 38 --- 37/38 are source UTF-8 operations; 40/41 are state operations. -tagReadIndexedTree, tagHashIndexedTree, tagWriteIndexedTreeIfAbsent :: Integer -tagReadIndexedTree = 50 -tagHashIndexedTree = 51 -tagWriteIndexedTreeIfAbsent = 52 - -tagAsk, tagLocal :: Integer -tagAsk = 30 -tagLocal = 31 - -tagGet, tagPut :: Integer -tagGet = 40 -tagPut = 41 - -tagFork, tagAwait, tagYield, tagSleep :: Integer -tagFork = 60 -tagAwait = 61 -tagYield = 62 -tagSleep = 63 - -tagSocket, tagCloseSocket, tagBindSocket, tagListen, tagAccept :: Integer -tagSocket = 70 -tagCloseSocket = 71 -tagBindSocket = 72 -tagListen = 73 -tagAccept = 74 - -tagConnect, tagRecv, tagSend, tagGetSocketName :: Integer -tagConnect = 75 -tagRecv = 76 -tagSend = 77 -tagGetSocketName = 78 - -data Step - = Halt Runtime T - | ExecutionFailed String - | Continue Machine - | ForkRequested T Machine - | AwaitRequested TaskId Machine - | YieldRequested Machine - | SleepRequested Integer Machine - | AsyncAction (IO T) Machine - -instance Show Step where - show (Halt _ v) = "Halt _ (" ++ show v ++ ")" - show (ExecutionFailed message) = "ExecutionFailed " ++ show message - show (Continue m) = "Continue (" ++ show m ++ ")" - show (ForkRequested t m) = "ForkRequested (" ++ show t ++ ") (" ++ show m ++ ")" - show (AwaitRequested tid m) = "AwaitRequested " ++ show tid ++ " (" ++ show m ++ ")" - show (YieldRequested m) = "YieldRequested (" ++ show m ++ ")" - show (SleepRequested n m) = "SleepRequested " ++ show n ++ " (" ++ show m ++ ")" - show (AsyncAction _ m) = "AsyncAction <io> (" ++ show m ++ ")" - -decodeAction :: T -> Either String Action -decodeAction tree = - case tree of - Fork tag payload -> - case toNumber tag of - Right n | n == tagPure -> - Right (APure payload) - - Right n | n == tagAbort -> - Right (AAbort payload) - - Right n | n == tagBind -> - case payload of - Fork left k -> Right (ABind left k) - _ -> Left "Invalid Bind: expected pair action continuation" - - Right n | n == tagPutStr -> - Right (APutStr payload) - - Right n | n == tagPutBytes -> - Right (APutBytes payload) - - Right n | n == tagGetLine -> - Right AGetLine - - Right n | n == tagReadSourceFile -> - Right (AReadSourceFile payload) - - Right n | n == tagEncodeSourceText -> - Right (AEncodeSourceText payload) - - Right n | n == tagReadFile -> - Right (AReadFile payload) - - Right n | n == tagReadTreeTerm -> - case payload of - Fork path expected -> Right (AReadTreeTerm path expected) - _ -> Left "Invalid ReadTreeTerm: expected pair path digest" - - Right n | n == tagHashTreeTerm -> - Right (AHashTreeTerm payload) - - Right n | n == tagWriteTreeTermIfAbsent -> - case payload of - Fork path (Fork expected value) -> Right (AWriteTreeTermIfAbsent path expected value) - _ -> Left "Invalid WriteTreeTermIfAbsent: expected path digest tree" - - Right n | n == tagReadIndexedTree -> - case payload of - Fork path expected -> Right (AReadIndexedTree path expected) - _ -> Left "Invalid ReadIndexedTree: expected pair path digest" - - Right n | n == tagHashIndexedTree -> - Right (AHashIndexedTree payload) - - Right n | n == tagWriteIndexedTreeIfAbsent -> - case payload of - Fork path (Fork expected value) -> Right (AWriteIndexedTreeIfAbsent path expected value) - _ -> Left "Invalid WriteIndexedTreeIfAbsent: expected path digest tree" - - Right n | n == tagWriteFile -> - case payload of - Fork path contents -> Right (AWriteFile path contents) - _ -> Left "Invalid WriteFile: expected pair path contents" - - Right n | n == tagWriteBytes -> - case payload of - Fork path contents -> Right (AWriteBytes path contents) - _ -> Left "Invalid WriteBytes: expected pair path contents" - - Right n | n == tagWriteBytesIfAbsent -> - case payload of - Fork path contents -> Right (AWriteBytesIfAbsent path contents) - _ -> Left "Invalid WriteBytesIfAbsent: expected pair path contents" - - Right n | n == tagAtomicWriteBytes -> - case payload of - Fork path contents -> Right (AAtomicWriteBytes path contents) - _ -> Left "Invalid AtomicWriteBytes: expected pair path contents" - - Right n | n == tagListDirectory -> - Right (AListDirectory payload) - - Right n | n == tagRenameFile -> - case payload of - Fork old new -> Right (ARenameFile old new) - _ -> Left "Invalid RenameFile: expected pair oldPath newPath" - - Right n | n == tagCreateDirectory -> - Right (ACreateDirectory payload) - - Right n | n == tagDeleteFile -> - Right (ADeleteFile payload) - - Right n | n == tagFileExists -> - Right (AFileExists payload) - - Right n | n == tagSha256Hex -> - Right (ASha256Hex payload) - - Right n | n == tagCurrentTime -> - Right ACurrentTime - - Right n | n == tagAsk -> - Right AAsk - - Right n | n == tagLocal -> - case payload of - Fork f action -> Right (ALocal f action) - _ -> Left "Invalid Local: expected pair function action" - - Right n | n == tagGet -> - Right AGet - - Right n | n == tagPut -> - Right (APut payload) - - Right n | n == tagFork -> - Right (AFork payload) - - Right n | n == tagAwait -> - Right (AAwait payload) - - Right n | n == tagYield -> - Right AYield - - Right n | n == tagSleep -> - Right (ASleep payload) - - Right n | n == tagSocket -> - Right ASocket - - Right n | n == tagCloseSocket -> - Right (ACloseSocket payload) - - Right n | n == tagBindSocket -> - case payload of - Fork sock (Fork addr port) -> Right (ABindSocket sock addr port) - _ -> Left "Invalid BindSocket: expected pair sock (pair addr port)" - - Right n | n == tagListen -> - case payload of - Fork sock backlog -> Right (AListen sock backlog) - _ -> Left "Invalid Listen: expected pair sock backlog" - - Right n | n == tagAccept -> - Right (AAccept payload) - - Right n | n == tagConnect -> - case payload of - Fork sock (Fork addr port) -> Right (AConnect sock addr port) - _ -> Left "Invalid Connect: expected pair sock (pair addr port)" - - Right n | n == tagRecv -> - case payload of - Fork sock maxBytes -> Right (ARecv sock maxBytes) - _ -> Left "Invalid Recv: expected pair sock maxBytes" - - Right n | n == tagSend -> - case payload of - Fork sock bytes -> Right (ASend sock bytes) - _ -> Left "Invalid Send: expected pair sock bytes" - - Right n | n == tagGetSocketName -> - Right (AGetSocketName payload) - - Right n -> - Left $ "Unknown IO action tag: " ++ show n - - Left err -> - Left $ "Invalid action tag: " ++ err - - _ -> - Left $ "Invalid action tree: expected pair tag payload, got " ++ show tree - --- --------------------------------------------------------------------------- --- Small-step IO machine --- --------------------------------------------------------------------------- - -finishValue :: Machine -> T -> IO Step -finishValue machine value = - case machineFrames machine of - [] -> - pure (Halt (machineRuntime machine) value) - - BindFrame k : rest -> - pure (Continue machine - { machineCurrent = apply k value - , machineFrames = rest - }) - - LocalFrame oldEnv : rest -> - let runtime' = (machineRuntime machine) { rtEnv = oldEnv } - in pure (Continue machine - { machineRuntime = runtime' - , machineCurrent = pureAction value - , machineFrames = rest - }) - -stepMachine :: TVar SocketRegistry -> Machine -> IO Step -stepMachine sockVar machine = - case decodeAction (machineCurrent machine) of - Right action -> dispatch action - Left _ -> finishValue machine (errResult "invalid action") - where - dispatch action = case action of - APure val -> - finishValue machine val - - AAbort diagnostic -> - pure (ExecutionFailed - ("execution aborted: " ++ either (const $ formatT Decode diagnostic) id (toString diagnostic))) - - ABind left k -> - pure (Continue machine - { machineCurrent = left - , machineFrames = BindFrame k : machineFrames machine - }) - - APutStr str -> - case decodeString str "PutStr" of - Right s -> - pure (AsyncAction (putStr s >> pure Leaf) machine) - Left _ -> - finishValue machine (errResult "invalid string") - - APutBytes bs -> - case decodeBytes bs "PutBytes" of - Right b -> - pure (AsyncAction (BS.putStr b >> pure Leaf) machine) - Left _ -> - finishValue machine (errResult "invalid bytes") - - AGetLine -> - pure (AsyncAction (ofString <$> getLine) machine) - - AReadSourceFile path -> dispatchFileRead tryReadSourceFile path - - AEncodeSourceText text -> - case decodeString text "EncodeSourceText" of - Right contents | text == ofString contents -> finishValue machine - (okResult (ofBytes (encodeUtf8 (T.pack contents)))) - _ -> finishValue machine (errResult "invalid source scalar list") - - AReadFile path -> dispatchFileRead tryReadFile path - - AReadTreeTerm path expected -> - dispatchTreeRead tryReadTreeTerm path expected - - AHashTreeTerm tree -> - pure (AsyncAction (do - digest <- evaluate (hashObject treeTermDomain (encodeTreeTerm tree)) - return (okResult (ofString (T.unpack digest)))) machine) - - AWriteTreeTermIfAbsent path expected tree -> - dispatchTreeWrite tryWriteTreeTerm path expected tree - - AReadIndexedTree path expected -> - dispatchTreeRead tryReadIndexedTree path expected - - AHashIndexedTree tree -> - pure (AsyncAction (do - payload <- encodeIndexedTree tree - digest <- evaluate (hashObject indexedTreeDomain payload) - return (okResult (ofString (T.unpack digest)))) machine) - - AWriteIndexedTreeIfAbsent path expected tree -> - dispatchTreeWrite tryWriteIndexedTree path expected tree - - AWriteFile path contents -> - case decodeString path "WriteFile" of - Right p -> - case decodeString contents "WriteFile" of - Right c -> do - mDeny <- checkWritePerm p - case mDeny of - Just denied -> finishValue machine denied - Nothing -> pure (AsyncAction (tryWriteFile p c) machine) - Left _ -> finishValue machine (errResult "invalid string") - Left _ -> finishValue machine (errResult "invalid string") - - AWriteBytes path contents -> - case decodeString path "WriteBytes" of - Right p -> - case decodeBytes contents "WriteBytes" of - Right c -> do - mDeny <- checkWritePerm p - case mDeny of - Just denied -> finishValue machine denied - Nothing -> pure (AsyncAction (tryWriteFileBytes p c) machine) - Left _ -> finishValue machine (errResult "invalid bytes") - Left _ -> finishValue machine (errResult "invalid string") - - AWriteBytesIfAbsent path contents -> - dispatchAtomicBytes path contents True - - AAtomicWriteBytes path contents -> - dispatchAtomicBytes path contents False - - AListDirectory pathTree -> - case decodeString pathTree "ListDirectory" of - Right p -> do - mDeny <- checkReadPerm p - case mDeny of - Just denied -> finishValue machine denied - Nothing -> pure (AsyncAction (tryListDirectory p) machine) - Left _ -> finishValue machine (errResult "invalid string") - - ARenameFile oldTree newTree -> - case decodeString oldTree "RenameFile" of - Right old -> - case decodeString newTree "RenameFile" of - Right new -> do - mDenyOld <- checkWritePerm old - mDenyNew <- checkWritePerm new - case (mDenyOld, mDenyNew) of - (Just denied, _) -> finishValue machine denied - (_, Just denied) -> finishValue machine denied - (Nothing, Nothing) -> pure (AsyncAction (tryRenameFile old new) machine) - Left _ -> finishValue machine (errResult "invalid string") - Left _ -> finishValue machine (errResult "invalid string") - - ACreateDirectory pathTree -> - case decodeString pathTree "CreateDirectory" of - Right p -> do - mDeny <- checkWritePerm p - case mDeny of - Just denied -> finishValue machine denied - Nothing -> pure (AsyncAction (tryCreateDirectory p) machine) - Left _ -> finishValue machine (errResult "invalid string") - - ADeleteFile pathTree -> - case decodeString pathTree "DeleteFile" of - Right p -> do - mDeny <- checkWritePerm p - case mDeny of - Just denied -> finishValue machine denied - Nothing -> pure (AsyncAction (tryDeleteFile p) machine) - Left _ -> finishValue machine (errResult "invalid string") - - AFileExists pathTree -> - case decodeString pathTree "FileExists" of - Right p -> do - mDeny <- checkReadPerm p - case mDeny of - Just denied -> finishValue machine denied - Nothing -> pure (AsyncAction (tryFileExists p) machine) - Left _ -> finishValue machine (errResult "invalid string") - - ASha256Hex bytesTree -> - case decodeBytes bytesTree "Sha256Hex" of - Right bs -> pure (AsyncAction (pure $ trySha256Hex bs) machine) - Left _ -> finishValue machine (errResult "invalid bytes") - - ACurrentTime -> - pure (AsyncAction (tryCurrentTime) machine) - - AAsk -> - finishValue machine (rtEnv (machineRuntime machine)) - - ALocal f action' -> - let runtime = machineRuntime machine - oldEnv = rtEnv runtime - newEnv = apply f oldEnv - runtime' = runtime { rtEnv = newEnv } - in pure (Continue machine - { machineRuntime = runtime' - , machineCurrent = action' - , machineFrames = LocalFrame oldEnv : machineFrames machine - }) - - AGet -> - finishValue machine (rtState (machineRuntime machine)) - - APut newState -> - let runtime' = (machineRuntime machine) { rtState = newState } - in finishValue (machine { machineRuntime = runtime' }) Leaf - - AFork childAction -> - pure (ForkRequested childAction machine) - - AAwait handleTree -> - case decodeTaskHandle handleTree of - Right taskId -> - pure (AwaitRequested taskId machine) - Left _ -> - finishValue machine invalidAsyncHandleResult - - AYield -> - pure (YieldRequested machine) - - ASleep msTree -> - case toNumber msTree of - Right ms | ms >= 0 -> - pure (SleepRequested ms machine) - _ -> - finishValue machine invalidSleepResult - - ASocket -> mask_ $ do - -- Acquisition through registry insertion is one ownership transfer. - result <- try $ bracketOnError - (NS.socket NS.AF_INET NS.Stream NS.defaultProtocol) NS.close $ \sock -> do - NS.setSocketOption sock NS.ReuseAddr 1 - registerSocket sockVar sock - case result of - Left (e :: IOException) -> finishValue machine (errResult ("io error: " ++ show e)) - Right sid -> finishValue machine (okResult (sockHandle sid)) - - ACloseSocket sockTree -> mask_ $ - case decodeSockHandle sockTree of - Left _ -> finishValue machine invalidSocketHandleResult - Right sid -> do - mSock <- atomically $ do - SocketRegistry m next <- readTVar sockVar - case Map.lookup sid m of - Nothing -> return Nothing - Just sock -> do - writeTVar sockVar (SocketRegistry (Map.delete sid m) next) - return (Just sock) - case mSock of - Nothing -> finishValue machine invalidSocketHandleResult - Just sock -> do - NS.close sock - finishValue machine (okResult Leaf) - - ABindSocket sockTree addrTree portTree -> - case decodeSockHandle sockTree of - Left _ -> finishValue machine invalidSocketHandleResult - Right sid -> - case decodeString addrTree "BindSocket" of - Left _ -> finishValue machine (errResult "invalid address") - Right addrStr -> - case toNumber portTree of - Left _ -> finishValue machine (errResult "invalid port") - Right port -> do - mSock <- atomically $ do - SocketRegistry m _ <- readTVar sockVar - return (Map.lookup sid m) - case mSock of - Nothing -> finishValue machine invalidSocketHandleResult - Just sock -> do - result <- try (do - addrInfo <- NS.getAddrInfo (Just $ NS.defaultHints { NS.addrSocketType = NS.Stream }) - (Just addrStr) - (Just (show port)) - let serverAddr = head addrInfo - NS.bind sock (NS.addrAddress serverAddr) - ) :: IO (Either IOException ()) - case result of - Left e -> - finishValue machine (errResult ("io error: " ++ show e)) - Right () -> - finishValue machine (okResult Leaf) - - AListen sockTree backlogTree -> - case decodeSockHandle sockTree of - Left _ -> finishValue machine invalidSocketHandleResult - Right sid -> - case toNumber backlogTree of - Left _ -> finishValue machine (errResult "invalid backlog") - Right backlog -> do - mSock <- atomically $ do - SocketRegistry m _ <- readTVar sockVar - return (Map.lookup sid m) - case mSock of - Nothing -> finishValue machine invalidSocketHandleResult - Just sock -> do - result <- try (NS.listen sock (fromIntegral backlog)) :: IO (Either IOException ()) - case result of - Left e -> - finishValue machine (errResult ("io error: " ++ show e)) - Right () -> - finishValue machine (okResult Leaf) - - AAccept listenTree -> - case decodeSockHandle listenTree of - Left _ -> finishValue machine invalidSocketHandleResult - Right listenSid -> - pure (AsyncAction (mask_ $ do - mListenSock <- atomically $ do - SocketRegistry m _ <- readTVar sockVar - return (Map.lookup listenSid m) - case mListenSock of - Nothing -> return (errResult "invalid socket handle") - Just listenSock -> do - result <- try (NS.accept listenSock) :: IO (Either IOException (NS.Socket, NS.SockAddr)) - case result of - Left e -> - return (errResult ("io error: " ++ show e)) - Right (clientSock, addr) -> do - clientSid <- registerSocket sockVar clientSock - let addrStr = case addr of - NS.SockAddrInet p h -> - let (a,b,c,d) = NS.hostAddressToTuple h - in show a ++ "." ++ show b ++ "." ++ show c ++ "." ++ show d ++ ":" ++ show p - _ -> show addr - return (okResult (Fork (sockHandle clientSid) (ofString addrStr))) - ) machine) - - AConnect sockTree addrTree portTree -> - case decodeSockHandle sockTree of - Left _ -> finishValue machine invalidSocketHandleResult - Right sid -> - case decodeString addrTree "Connect" of - Left _ -> finishValue machine (errResult "invalid address") - Right addrStr -> - case toNumber portTree of - Left _ -> finishValue machine (errResult "invalid port") - Right port -> do - mSock <- atomically $ do - SocketRegistry m _ <- readTVar sockVar - return (Map.lookup sid m) - case mSock of - Nothing -> finishValue machine invalidSocketHandleResult - Just sock -> - pure (AsyncAction (do - result <- try (do - addrInfo <- NS.getAddrInfo (Just $ NS.defaultHints { NS.addrSocketType = NS.Stream }) - (Just addrStr) - (Just (show port)) - let serverAddr = head addrInfo - NS.connect sock (NS.addrAddress serverAddr) - ) :: IO (Either IOException ()) - case result of - Left e -> - return (errResult ("io error: " ++ show e)) - Right () -> - return (okResult Leaf) - ) machine) - - ARecv sockTree maxBytesTree -> - case decodeSockHandle sockTree of - Left _ -> finishValue machine invalidSocketHandleResult - Right sid -> - case toNumber maxBytesTree of - Left _ -> finishValue machine (errResult "invalid maxBytes") - Right maxBytes -> do - mSock <- atomically $ do - SocketRegistry m _ <- readTVar sockVar - return (Map.lookup sid m) - case mSock of - Nothing -> finishValue machine invalidSocketHandleResult - Just sock -> - pure (AsyncAction (do - result <- try (NSB.recv sock (fromIntegral maxBytes)) :: IO (Either IOException BS.ByteString) - case result of - Left e -> - return (errResult ("io error: " ++ show e)) - Right bs -> - if BS.null bs - then return (errResult "connection closed") - else return (okResult (ofBytes bs)) - ) machine) - - AGetSocketName sockTree -> - case decodeSockHandle sockTree of - Left _ -> finishValue machine invalidSocketHandleResult - Right sid -> do - mSock <- atomically $ do - SocketRegistry m _ <- readTVar sockVar - return (Map.lookup sid m) - case mSock of - Nothing -> finishValue machine invalidSocketHandleResult - Just sock -> do - mPort <- getSocketPort sock - case mPort of - Just port -> finishValue machine (okResult (ofNumber port)) - Nothing -> finishValue machine (errResult "io error: could not get socket name") - - ASend sockTree bytesTree -> - case decodeSockHandle sockTree of - Left _ -> finishValue machine invalidSocketHandleResult - Right sid -> - case decodeBytes bytesTree "Send" of - Left _ -> finishValue machine (errResult "invalid bytes") - Right bs -> do - mSock <- atomically $ do - SocketRegistry m _ <- readTVar sockVar - return (Map.lookup sid m) - case mSock of - Nothing -> finishValue machine invalidSocketHandleResult - Just sock -> - pure (AsyncAction (do - result <- try (NSB.send sock bs) :: IO (Either IOException Int) - case result of - Left e -> - return (errResult ("io error: " ++ show e)) - Right sent -> - return (okResult (ofNumber (fromIntegral sent))) - ) machine) - - dispatchFileRead reader path = - case toString path of - Left _ -> finishValue machine (errResult "invalid string") - Right p -> permittedRead reader p - - permittedRead reader path = do - denied <- checkReadPerm path - case denied of - Just result -> finishValue machine result - Nothing -> pure (AsyncAction (reader path) machine) - - -- Keep validation before permissions, and permissions before encoding or OS - -- work. In particular, denied tree publication must not force the tree. - withTreeIdentity path expected next = - case (toString path, toString expected) of - (Right p, Right digest) -> case validateObjectHash (T.pack digest) of - Left _ -> finishValue machine (errResult "invalid CAS digest") - Right () -> next p (T.pack digest) - _ -> finishValue machine (errResult "invalid string") - - dispatchTreeRead reader path expected = withTreeIdentity path expected $ \p digest -> - permittedRead (\file -> reader file digest) p - - dispatchTreeWrite writer path expected tree = withTreeIdentity path expected $ \p digest -> do - -- Verifying a pre-existing/racing winner requires read permission too. - writeDenied <- checkWritePerm p - readDenied <- checkReadPerm p - case (writeDenied, readDenied) of - (Just result, _) -> finishValue machine result - (_, Just result) -> finishValue machine result - _ -> pure (AsyncAction (writer p digest tree) machine) - - dispatchAtomicBytes pathTree contentsTree exclusive = - case decodeString pathTree "AtomicWriteBytes" of - Right p -> - case decodeBytes contentsTree "AtomicWriteBytes" of - Right bs -> do - mDeny <- checkWritePerm p - case mDeny of - Just denied -> finishValue machine denied - Nothing -> pure (AsyncAction - (tryAtomicBytes p bs exclusive) machine) - Left _ -> finishValue machine (errResult "invalid bytes") - Left _ -> finishValue machine (errResult "invalid string") - - -- Resolve existing symlinked ancestors even when the final name is new. - -- Canonicalization failures deny access; they must never fall back to a - -- syntactic prefix. This is a path policy, not a race-free filesystem sandbox. - checkReadPerm = checkPathPerm allowReadAll allowRead - checkWritePerm = checkPathPerm allowWriteAll allowWrite - - checkPathPerm unrestricted roots path - | unrestricted perms = pure Nothing - | otherwise = do - checked <- try $ do - canonical <- canonicalizePath path - prefixes <- mapM canonicalizePath (filter (not . null) (roots perms)) - pure (any (isPathPrefixOf canonical) prefixes) - pure $ case checked of - Right True -> Nothing - Right False -> Just policyErrResult - Left (_ :: IOException) -> Just policyErrResult - where perms = rtPerms (machineRuntime machine) - - policyErrResult = errResult "permission denied" - - isPathPrefixOf :: FilePath -> FilePath -> Bool - isPathPrefixOf path prefix = - let prefix' = addTrailingPathSeparator prefix - in path == prefix || prefix' `isPrefixOf` path - - -- Source-only boundary: preserve the exact octets alongside decoded scalars. - -- No BOM stripping, replacement decoding, newline folding or normalization. - tryReadSourceFile path = do - result <- try (BS.readFile path) :: IO (Either IOException BS.ByteString) - return $ case result of - Left e -> errResult (ioErrorString e) - Right bytes -> case decodeUtf8' bytes of - Left problem -> errResult (path ++ ": invalid UTF-8 source: " ++ show problem) - Right text - | T.isPrefixOf (T.singleton '\xfeff') text -> - errResult (path ++ ": UTF-8 source BOM is not permitted") - | otherwise -> okResult (Fork (ofBytes bytes) (ofString (T.unpack text))) - - tryReadFile path = do - result <- try (BS.readFile path) :: IO (Either IOException BS.ByteString) - case result of - Right content -> return $ okResult (ofBytes content) - Left e -> return $ errResult (ioErrorString e) - - -- Fixed canonical domain, no source evaluation and no byte-list roundtrip. - -- Verify before decoding so malformed bytes cannot bypass integrity checks. - tryReadTreeTerm = tryReadTreeObject treeTermDomain decodeTreeTermShared "invalid canonical tree-term" - tryReadIndexedTree = tryReadTreeObject indexedTreeDomain decodeIndexedTree "invalid indexed tree" - - tryReadTreeObject domain decode invalid path expected = do - result <- try (BS.readFile path) :: IO (Either IOException BS.ByteString) - case result of - Left e -> return $ errResult (ioErrorString e) - Right payload - | hashObject domain payload /= expected -> - return $ errResult "CAS digest mismatch" - | otherwise -> case decode payload of - Left _ -> return $ errResult invalid - Right tree -> return $ okResult tree - - tryWriteTreeTerm path expected tree = - tryWriteTreeObject treeTermDomain path expected (encodeTreeTerm tree) - tryWriteIndexedTree path expected tree = do - payload <- encodeIndexedTree tree - tryWriteTreeObject indexedTreeDomain path expected payload - - tryWriteTreeObject domain path expected payload = do - if hashObject domain payload /= expected - then return $ errResult "CAS digest mismatch" - else do - published <- tryAtomicBytes path payload True - case published of - Fork (Stem Leaf) (Fork _ Leaf) -> do - -- Preserve byte-for-byte winner checking, not merely hash checking. - winner <- try (BS.readFile path) :: IO (Either IOException BS.ByteString) - case winner of - Left e -> return $ errResult (ioErrorString e) - Right bytes - | bytes == payload -> return published - | otherwise -> return $ errResult "CAS object exists with different bytes" - _ -> return published - - tryWriteFile path contents = tryWriteUnit (IO.writeFile path contents) - tryWriteFileBytes path contents = tryWriteUnit (BS.writeFile path contents) - - tryWriteUnit action = do - result <- try action :: IO (Either IOException ()) - pure $ either (errResult . ioErrorString) (const (okResult Leaf)) result - - -- Sync file contents before exposing the name and its parent directory - -- after publication. A losing writer also syncs the winning object so a - -- following lock/alias cannot outlive the object on a power loss. - syncPath path = bracket - (openFd path ReadOnly defaultFileFlags) - closeFd fileSynchronise - - syncParent path = syncPath (takeDirectory path) - - tryAtomicBytes path contents exclusive = do - let tmpDir = takeDirectory path - cleanup tmp h = do - IO.hClose h `catch` (\(_ :: IOException) -> return ()) - removeFile tmp `catch` (\(_ :: IOException) -> return ()) - result <- try $ bracket - (IO.openBinaryTempFile tmpDir ".tricu-write-") - (\(tmp, h) -> cleanup tmp h) - (\(tmp, h) -> do - BS.hPut h contents - IO.hClose h - syncPath tmp - if exclusive - then do - linked <- try (createLink tmp path) :: IO (Either IOException ()) - case linked of - Right () -> syncParent path >> return True - Left e | isAlreadyExistsError e -> do - syncPath path - syncParent path - return False - | otherwise -> ioError e - else renameFile tmp path >> syncParent path >> return True) - case result of - Right newlyWritten -> return $ okResult - (if newlyWritten then Stem Leaf else Leaf) - Left (e :: IOException) -> return $ errResult (ioErrorString e) - - tryListDirectory path = do - exists <- doesPathExist path - if not exists - then return $ errResult "does not exist" - else do - isDir <- doesDirectoryExist path - if not isDir - then return $ errResult "not a directory" - else do - result <- try (listDirectory path) :: IO (Either IOException [FilePath]) - case result of - Right entries -> - let filtered = filter (`notElem` [".", ".."]) entries - in return $ okResult (ofList (map ofString filtered)) - Left e -> return $ errResult (ioErrorString e) - - tryRenameFile old new = do - oldExists <- doesPathExist old - if not oldExists - then return $ errResult "does not exist" - else do - result <- try (renameFile old new) :: IO (Either IOException ()) - case result of - Right () -> return $ okResult Leaf - Left e - | isDoesNotExistError e -> return $ errResult "does not exist" - | isPermissionError e -> return $ errResult "permission denied" - | "cross-device" `isInfixOf` map toLower (show e) || "exdev" `isInfixOf` map toLower (show e) -> - return $ errResult "cross-device rename" - | otherwise -> return $ errResult (ioErrorString e) - - tryCreateDirectory path = do - exists <- doesPathExist path - if exists - then do - isDir <- doesDirectoryExist path - if isDir - then durableDirectoryResult path - else return $ errResult "already exists" - else do - let parent = takeDirectory path - parentExists <- doesPathExist parent - if parentExists - then do - parentIsDir <- doesDirectoryExist parent - if parentIsDir - then do - result <- try (createDirectory path) :: IO (Either IOException ()) - case result of - Right () -> durableDirectoryResult path - Left e - | isDoesNotExistError e -> return $ errResult "does not exist" - | isPermissionError e -> return $ errResult "permission denied" - | isAlreadyExistsError e -> existingDirectoryResult path - | otherwise -> return $ errResult (ioErrorString e) - else return $ errResult "not a directory" - else do - result <- try (createDirectory path) :: IO (Either IOException ()) - case result of - Right () -> durableDirectoryResult path - Left e - | isDoesNotExistError e -> return $ errResult "does not exist" - | isPermissionError e -> return $ errResult "permission denied" - | isAlreadyExistsError e -> existingDirectoryResult path - | otherwise -> return $ errResult (ioErrorString e) - - durableDirectoryResult path = do - result <- try (syncPath path >> syncParent path) :: IO (Either IOException ()) - return $ either (errResult . ioErrorString) (const (okResult Leaf)) result - - existingDirectoryResult path = do - isDir <- doesDirectoryExist path - if isDir then durableDirectoryResult path - else return $ errResult "already exists" - - tryDeleteFile path = do - exists <- doesPathExist path - if not exists - then return $ okResult Leaf - else do - isDir <- doesDirectoryExist path - if isDir - then return $ errResult "is a directory" - else do - result <- try (removeFile path) :: IO (Either IOException ()) - case result of - Right () -> return $ okResult Leaf - Left e - | isDoesNotExistError e -> return $ okResult Leaf - | isPermissionError e -> return $ errResult "permission denied" - | otherwise -> return $ errResult (ioErrorString e) - - tryFileExists path = do - result <- try (doesPathExist path) :: IO (Either IOException Bool) - case result of - Right exists -> return $ okResult (if exists then Stem Leaf else Leaf) - Left e - | isPermissionError e -> return $ errResult "permission denied" - | otherwise -> return $ errResult (ioErrorString e) - - trySha256Hex bs = - let digest = hash bs :: Digest SHA256 - hexBs = encode (convert digest) - hexStr = T.unpack (decodeUtf8 hexBs) - in okResult (ofString hexStr) - - tryCurrentTime = do - now <- getPOSIXTime - return $ okResult (ofNumber (floor now)) - - decodeString t ctx = - case toString t of - Right s -> Right s - Left _ -> Left $ "Invalid " ++ ctx ++ " string" - - decodeBytes t ctx = - case toBytes t of - Right b -> Right b - Left _ -> Left $ "Invalid " ++ ctx ++ " bytes" - --- --------------------------------------------------------------------------- --- Scheduler --- --------------------------------------------------------------------------- - -data TaskStatus - = Runnable Machine - | BlockedOn TaskId Machine - | Sleeping UTCTime Machine - | AsyncWaiting Machine - deriving (Show) - -data Scheduler = Scheduler - { schedulerNextTaskId :: Integer - , schedulerRunnable :: Seq TaskId - , schedulerTasks :: Map TaskId TaskStatus - , schedulerWaiters :: Map TaskId (Seq TaskId) - , schedulerSleepQueue :: Map UTCTime (Set TaskId) - , schedulerAsyncCompleted :: TVar (Map TaskId T) - , schedulerCompleted :: Map TaskId (T, T) - , schedulerSockets :: TVar SocketRegistry - , schedulerWorkers :: Workers - , schedulerFailure :: Maybe String - , schedulerYieldHook :: YieldStats -> IO () - } - -instance Show Scheduler where - show s = "Scheduler { schedulerNextTaskId = " ++ show (schedulerNextTaskId s) - ++ ", schedulerRunnable = " ++ show (schedulerRunnable s) - ++ ", schedulerTasks = " ++ show (schedulerTasks s) - ++ ", schedulerWaiters = " ++ show (schedulerWaiters s) - ++ ", schedulerSleepQueue = " ++ show (schedulerSleepQueue s) - ++ ", schedulerAsyncCompleted = <tvar>" - ++ ", schedulerCompleted = " ++ show (schedulerCompleted s) - ++ ", schedulerSockets = <tvar>" - ++ ", schedulerFailure = " ++ show (schedulerFailure s) - ++ " }" - --- Counts describe the scheduler at a yield without exposing retained trees. -data YieldStats = YieldStats - { yieldBindFrames :: !Int - , yieldLocalFrames :: !Int - , yieldTasks :: !Int - } deriving (Eq, Show) - -initialScheduler :: (YieldStats -> IO ()) -> TVar (Map TaskId T) -> TVar SocketRegistry -> Workers -> Machine -> Scheduler -initialScheduler yieldHook asyncVar sockVar workers mainMachine = - Scheduler - { schedulerNextTaskId = 1 - , schedulerRunnable = Seq.singleton (TaskId 0) - , schedulerTasks = Map.singleton (TaskId 0) (Runnable mainMachine) - , schedulerWaiters = Map.empty - , schedulerSleepQueue = Map.empty - , schedulerAsyncCompleted = asyncVar - , schedulerCompleted = Map.empty - , schedulerSockets = sockVar - , schedulerWorkers = workers - , schedulerFailure = Nothing - , schedulerYieldHook = yieldHook - } - -runtimeOfStatus :: TaskStatus -> Maybe Runtime -runtimeOfStatus (Runnable machine) = Just (machineRuntime machine) -runtimeOfStatus (BlockedOn _ machine) = Just (machineRuntime machine) -runtimeOfStatus (Sleeping _ machine) = Just (machineRuntime machine) -runtimeOfStatus (AsyncWaiting machine) = Just (machineRuntime machine) - -wakeAwaiters :: TaskId -> T -> Scheduler -> Scheduler -wakeAwaiters targetId value scheduler = - case Map.lookup targetId (schedulerWaiters scheduler) of - Nothing -> scheduler - Just waiters -> - let (tasks', queue') = Fold.foldl' (wakeOne targetId value) - (schedulerTasks scheduler, schedulerRunnable scheduler) - waiters - in scheduler - { schedulerTasks = tasks' - , schedulerRunnable = queue' - , schedulerWaiters = Map.delete targetId (schedulerWaiters scheduler) - } - where - wakeOne _ _ (tasks, queue) waiterId = - case Map.lookup waiterId tasks of - Just (BlockedOn _ machine) -> - let machine' = machine { machineCurrent = pureAction value } - in (Map.insert waiterId (Runnable machine') tasks, queue |> waiterId) - _ -> (tasks, queue) - -wakeDueSleepers :: Scheduler -> IO Scheduler -wakeDueSleepers scheduler = do - now <- getCurrentTime - let go sq accTasks accQueue = - case Map.lookupMin sq of - Nothing -> (accTasks, accQueue, sq) - Just (t, taskSet) - | t <= now -> - let tasks' = Fold.foldl' (\m tid -> - case Map.lookup tid m of - Just (Sleeping _ machine) -> Map.insert tid (Runnable machine) m - _ -> m - ) accTasks (Set.toList taskSet) - queue' = Fold.foldl' (|>) accQueue (Set.toList taskSet) - in go (Map.deleteMin sq) tasks' queue' - | otherwise -> (accTasks, accQueue, sq) - (tasks', queue', sq') = go (schedulerSleepQueue scheduler) - (schedulerTasks scheduler) - (schedulerRunnable scheduler) - pure scheduler - { schedulerTasks = tasks' - , schedulerRunnable = queue' - , schedulerSleepQueue = sq' - } - -nearestSleepTime :: Scheduler -> Maybe UTCTime -nearestSleepTime = fmap fst . Map.lookupMin . schedulerSleepQueue - -hasAsyncWaiters :: Scheduler -> Bool -hasAsyncWaiters = any isAsync . Map.elems . schedulerTasks - where - isAsync (AsyncWaiting _) = True - isAsync _ = False - -resumeCurrentWith :: TaskId -> T -> Machine -> Scheduler -> IO Scheduler -resumeCurrentWith taskId value machine scheduler = - let machine' = machine { machineCurrent = pureAction value } - in pure scheduler - { schedulerTasks = Map.insert taskId (Runnable machine') (schedulerTasks scheduler) - , schedulerRunnable = schedulerRunnable scheduler |> taskId - } - -wouldCycle :: TaskId -> TaskId -> Map TaskId TaskStatus -> Bool -wouldCycle target current tasks = - case Map.lookup target tasks of - Just (BlockedOn next _) -> - next == current || wouldCycle next current tasks - _ -> False - -handleStep :: TaskId -> Step -> Scheduler -> IO Scheduler -handleStep taskId (Continue machine) scheduler = - pure scheduler - { schedulerTasks = Map.insert taskId (Runnable machine) (schedulerTasks scheduler) - , schedulerRunnable = schedulerRunnable scheduler |> taskId - } - -handleStep taskId (Halt runtime value) scheduler = - let scheduler' = wakeAwaiters taskId value scheduler - in pure scheduler' - { schedulerTasks = Map.delete taskId (schedulerTasks scheduler') - , schedulerCompleted = Map.insert taskId (value, rtState runtime) (schedulerCompleted scheduler') - } - -handleStep parentId (ForkRequested childAction parentMachine) scheduler = - let childId = TaskId (schedulerNextTaskId scheduler) - handle = taskHandle childId - - parentMachine' = - parentMachine { machineCurrent = pureAction handle } - - childMachine = - Machine - { machineRuntime = machineRuntime parentMachine - , machineCurrent = childAction - , machineFrames = [] - } - - tasks' = - Map.insert parentId (Runnable parentMachine') $ - Map.insert childId (Runnable childMachine) $ - schedulerTasks scheduler - - queue' = - schedulerRunnable scheduler |> parentId |> childId - - in pure scheduler - { schedulerNextTaskId = schedulerNextTaskId scheduler + 1 - , schedulerTasks = tasks' - , schedulerRunnable = queue' - } - -handleStep currentId (AwaitRequested targetId machine) scheduler - | currentId == targetId = - resumeCurrentWith currentId selfAwaitResult machine scheduler - - | otherwise = - case Map.lookup targetId (schedulerTasks scheduler) of - Nothing -> - case Map.lookup targetId (schedulerCompleted scheduler) of - Just (value, _) -> resumeCurrentWith currentId value machine scheduler - Nothing -> resumeCurrentWith currentId invalidAsyncHandleResult machine scheduler - - Just (BlockedOn nextId _) -> - if wouldCycle targetId currentId (schedulerTasks scheduler) - then resumeCurrentWith currentId (errResult "cyclic await") machine scheduler - else block - - Just _ -> block - where - block = pure scheduler - { schedulerTasks = Map.insert currentId (BlockedOn targetId machine) (schedulerTasks scheduler) - , schedulerWaiters = Map.alter addWaiter targetId (schedulerWaiters scheduler) - } - addWaiter Nothing = Just (Seq.singleton currentId) - addWaiter (Just sq) = Just (sq |> currentId) - -handleStep taskId (YieldRequested machine) scheduler = do - let binds = length [() | BindFrame _ <- machineFrames machine] - locals = length [() | LocalFrame _ <- machineFrames machine] - schedulerYieldHook scheduler (YieldStats binds locals (Map.size (schedulerTasks scheduler))) - resumeCurrentWith taskId Leaf machine scheduler - -handleStep taskId (SleepRequested ms machine) scheduler = do - now <- getCurrentTime - let seconds = fromIntegral ms / 1000 - wakeTime = addUTCTime seconds now - machine' = machine { machineCurrent = pureAction Leaf } - pure scheduler - { schedulerTasks = Map.insert taskId (Sleeping wakeTime machine') (schedulerTasks scheduler) - , schedulerSleepQueue = Map.alter (Just . maybe (Set.singleton taskId) (Set.insert taskId)) wakeTime (schedulerSleepQueue scheduler) - } - -handleStep _ (ExecutionFailed message) scheduler = - pure scheduler { schedulerFailure = Just message } - -handleStep taskId (AsyncAction ioAction machine) scheduler = do - startWorker (schedulerWorkers scheduler) $ do - result <- (Right <$> ioAction) `catch` \(e :: SomeException) -> - case fromException e :: Maybe SomeAsyncException of - Just _ -> throwIO e - Nothing -> pure (Left (show e)) - atomically $ modifyTVar' (schedulerAsyncCompleted scheduler) (Map.insert taskId $ - case result of - Right val -> val - Left msg -> errResult msg) - pure scheduler - { schedulerTasks = Map.insert taskId (AsyncWaiting machine) (schedulerTasks scheduler) - } - -handleNoRunnable :: Scheduler -> IO Scheduler -handleNoRunnable scheduler = - case nearestSleepTime scheduler of - Just wakeTime -> do - now <- getCurrentTime - let micros = max 0 (floor (diffUTCTime wakeTime now * 1000000)) - threadDelay micros - wakeDueSleepers scheduler - - Nothing -> - if hasAsyncWaiters scheduler - then do - -- Block efficiently until at least one async operation completes. - atomically $ do - m <- readTVar (schedulerAsyncCompleted scheduler) - if Map.null m then retry else return () - pure scheduler - else - case Map.lookup (TaskId 0) (schedulerTasks scheduler) of - Just status -> - case runtimeOfStatus status of - Just runtime -> - let scheduler' = wakeAwaiters (TaskId 0) deadlockResult scheduler - in pure scheduler' - { schedulerTasks = Map.delete (TaskId 0) (schedulerTasks scheduler') - , schedulerCompleted = Map.insert (TaskId 0) (deadlockResult, rtState runtime) (schedulerCompleted scheduler') - } - Nothing -> pure scheduler - Nothing -> pure scheduler - -schedulerStep :: Scheduler -> IO Scheduler -schedulerStep scheduler = do - -- Poll completed async operations and resume their tasks. - completed <- atomically $ do - m <- readTVar (schedulerAsyncCompleted scheduler) - writeTVar (schedulerAsyncCompleted scheduler) Map.empty - return m - schedulerAfterAsync <- Fold.foldlM - (\s (tid, val) -> - case Map.lookup tid (schedulerTasks s) of - Just (AsyncWaiting machine) -> resumeCurrentWith tid val machine s - _ -> pure s) - scheduler - (Map.toList completed) - - scheduler1 <- wakeDueSleepers schedulerAfterAsync - case Seq.viewl (schedulerRunnable scheduler1) of - EmptyL -> - handleNoRunnable scheduler1 - - taskId :< restQueue -> - case Map.lookup taskId (schedulerTasks scheduler1) of - Just (Runnable machine) -> do - step <- stepMachine (schedulerSockets scheduler1) machine - handleStep taskId step scheduler1 { schedulerRunnable = restQueue } - - _ -> - pure scheduler1 { schedulerRunnable = restQueue } - -runScheduler :: Scheduler -> IO (Either String (T, T)) -runScheduler scheduler = - case schedulerFailure scheduler of - Just message -> pure (Left message) - Nothing -> case Map.lookup (TaskId 0) (schedulerCompleted scheduler) of - Just (value, finalState) -> pure (Right (value, finalState)) - Nothing -> schedulerStep scheduler >>= runScheduler - --- --------------------------------------------------------------------------- --- Public API --- --------------------------------------------------------------------------- - -runIOWith :: IOPermissions -> T -> T -> T -> IO (Either String (T, T)) -runIOWith = runIOWithYieldHook (pure ()) - --- Profiling observes a yield while its pending continuations remain live. -runIOWithYieldHook :: IO () -> IOPermissions -> T -> T -> T -> IO (Either String (T, T)) -runIOWithYieldHook yieldHook = runIOWithYieldStatsHook (\_ -> yieldHook) - -runIOWithYieldStatsHook :: (YieldStats -> IO ()) -> IOPermissions -> T -> T -> T -> IO (Either String (T, T)) -runIOWithYieldStatsHook yieldHook perms env initialState action = - case checkIOSentinel action of - Left err -> pure (Left err) - Right (_, action') -> bracket acquire cleanupRun $ \(asyncVar, sockVar, workers) -> do - let initialMachine = Machine - { machineRuntime = Runtime - { rtPerms = perms - , rtEnv = env - , rtState = initialState - } - , machineCurrent = action' - , machineFrames = [] - } - runScheduler (initialScheduler yieldHook asyncVar sockVar workers initialMachine) - - where - acquire = do - asyncVar <- newTVarIO Map.empty - sockVar <- newTVarIO (SocketRegistry Map.empty 0) - workers <- newTVarIO Map.empty - pure (asyncVar, sockVar, workers) - -runIOWithEnv :: IOPermissions -> T -> T -> IO (Either String T) -runIOWithEnv perms env action = do - result <- runIOWith perms env Leaf action - pure (fmap fst result) - -runIO :: IOPermissions -> T -> IO (Either String T) -runIO perms action = do - result <- runIOWith perms Leaf Leaf action - pure (fmap fst result) +checkIOSentinel tree = case tree of + Fork sentinel (Fork version action) + | sentinel /= ofString "tricuIO" -> Left "sentinel mismatch (expected canonical tricuIO)" + | otherwise -> case V2.canonicalNat 16 version of + Just v -> Right (v, action) + Nothing -> Left "invalid IO envelope version" + _ -> Left "root is not an IO sentinel pair" + +runIO :: T -> IO (Either String T) +runIO tree = case checkIOSentinel tree of + Left problem -> pure (Left problem) + Right (2, action) -> V2.run action + Right _ -> pure (Left "Unsupported tricuIO version") diff --git a/src/IOV2/Clock.hs b/src/IOV2/Clock.hs @@ -0,0 +1,28 @@ +-- Native monotonic observations and interruptible relative waits. No wall-clock +-- epoch, deadline scheduler, race combinator or ordering guarantee is implied. +module IOV2.Clock (monotonicNanoseconds, sleepMilliseconds, sleepWith, sleepLimit) where + +import Control.Concurrent (threadDelay) +import GHC.Clock (getMonotonicTimeNSec) + +sleepLimit :: Integer +sleepLimit = 9223372036854775807 + +monotonicNanoseconds :: IO Integer +monotonicNanoseconds = toInteger <$> getMonotonicTimeNSec + +-- Driver validates canonical [0,sleepLimit] milliseconds before entry. Keep each +-- host delay <= one second: no Int overflow even on a narrower host, and no giant +-- conversion to microseconds. Delays may run late; cancellation interrupts them. +-- Injectable waiter supports deterministic chunk/overflow tests, not fault flags. +sleepWith :: (Int -> IO ()) -> Integer -> IO () +sleepWith wait = go + where + go 0 = pure () + go remaining = do + let chunk = min remaining 1000 + wait (fromInteger (chunk * 1000)) + go (remaining - chunk) + +sleepMilliseconds :: Integer -> IO () +sleepMilliseconds = sleepWith threadDelay diff --git a/src/IOV2/Codec.hs b/src/IOV2/Codec.hs @@ -0,0 +1,112 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Format-specific authenticated transport, independent of store paths/publication. +module IOV2.Codec (hashIndexed, writeIndexed, verifyIndexed, readIndexed, readIndexedWith, sectionLimit, manifestLimit) where + +import qualified IOV2.Resources as R +import ContentStore.Snapshot +import ContentStore.Indexed (indexedTreeDomain, decodeIndexedSnapshot) +import ContentStore.Object (Domain(..), hashToText, validateObjectHash) +import Wire (Bundle, buildBundleShared, bundleEncodedLength, emitBundle) +import Crypto.Hash (Context, SHA256, hashInit, hashUpdate, hashFinalize) +import qualified Data.ByteString as BS +import Data.Text.Encoding (encodeUtf8) +import Control.Monad (unless) +import Runtime (T) +import Control.Exception +import Data.IORef +import qualified Data.Text as Text +import System.IO (Handle, hClose) + +sectionLimit, manifestLimit :: Int +sectionLimit = 64 +manifestLimit = 65536 + +readIndexed :: R.Runtime -> R.Scope -> R.Token -> FilePath -> Integer -> Integer -> String + -> IO (Either R.Problem T) +readIndexed = readIndexedWith hClose + +-- Injectable release for lifecycle/fault controls, not a production fault flag. +readIndexedWith :: (Handle -> IO ()) -> R.Runtime -> R.Scope -> R.Token -> FilePath + -> Integer -> Integer -> String -> IO (Either R.Problem T) +readIndexedWith release runtime scope reader directory limit size digest + | limit < 0 || size < 0 || size > limit || limit > R.positionLimit = + pure (Left (R.Problem "invalid-bound" "read" "invalid snapshot size limit" "none")) + | Left _ <- validateObjectHash expected = + pure (Left (R.Problem "invalid-argument" "read" "expected 64 lowercase hexadecimal digits" "none")) + | null directory = pure (Left (R.Problem "invalid-argument" "read" "empty snapshot directory" "none")) + | otherwise = R.withReaderLease runtime scope reader $ \receive -> + withAuthenticatedSnapshotUsing lifetime directory limit size indexedTreeDomain expected receive $ \snapshot -> do + decoded <- decodeIndexedSnapshot sectionLimit manifestLimit snapshot + either (R.resourceFault . malformed) pure decoded + where + expected = Text.pack digest + malformed message = R.Problem "invalid-data" "read" (take 512 message) "unknown" + -- Register the anonymous spool before consuming input. A terminal exception + -- leaves its entry for structured scope settlement, preserving the primary + -- fault/cancellation and collecting any cleanup diagnostics there. + lifetime allocate consume = R.withTemporary runtime scope "read" + (do handle <- allocate; pure (handle, release handle)) + (\handle -> consume handle `catch` (R.resourceFault . snapshotProblem)) + >>= either R.resourceFault pure + snapshotProblem e = case e of + SnapshotDigestMismatch -> R.Problem "integrity-failed" "read" "CAS digest mismatch" "unknown" + InvalidSnapshotBound -> R.Problem "invalid-bound" "read" "invalid snapshot size limit" "none" + InvalidSnapshotDigest -> R.Problem "invalid-argument" "read" "invalid digest" "none" + SnapshotTruncated -> malformed "incomplete indexed frame" + SnapshotReaderExceededBound -> malformed "reader exceeded frame bound" + InvalidSnapshotRange -> malformed "invalid snapshot range" + +-- Prepare/hash a resident DAG, not an encoded byte list. Encoding is regenerated +-- during writing: sharing a lazy byte-chunk list across passes would retain the +-- entire encoding. Application policy still selects sharding and publication. +prepareIndexed :: Integer -> T -> IO (Either R.Problem (Bundle, Integer, String)) +prepareIndexed limit tree + | limit < 0 || limit > R.positionLimit = pure (Left (R.Problem "invalid-bound" "hash" "invalid encoded size limit" "none")) + | otherwise = do + bundle <- buildBundleShared [("root", tree)] + let size = bundleEncodedLength bundle + if size > limit then pure (Left (R.Problem "invalid-bound" "hash" "encoded tree exceeds limit" "none")) + else do + let Domain domain = indexedTreeDomain + context <- newIORef (hashUpdate (hashInit :: Context SHA256) (encodeUtf8 domain <> BS.singleton 0)) + emitBundle R.chunkLimit bundle $ \bytes -> do + previous <- readIORef context + next <- evaluate (hashUpdate previous bytes) + writeIORef context next + finished <- readIORef context + digest <- evaluate (hashToText (hashFinalize finished)) + pure (Right (bundle, size, Text.unpack digest)) + +hashIndexed :: Integer -> T -> IO (Either R.Problem (Integer, String)) +hashIndexed limit tree = fmap (fmap (\(_,size,digest) -> (size,digest))) (prepareIndexed limit tree) + +writeIndexed :: R.Runtime -> R.Scope -> R.Token -> Integer -> String -> T + -> IO (Either R.Problem (Integer, String)) +writeIndexed runtime scope writer limit expected tree + | limit < 0 || limit > R.positionLimit = pure (Left (R.Problem "invalid-bound" "write" "invalid encoded size limit" "none")) + | Left _ <- validateObjectHash (Text.pack expected) = pure (Left (R.Problem "invalid-argument" "write" "expected 64 lowercase hexadecimal digits" "none")) + | otherwise = R.withWriterLease runtime scope writer $ \send -> do + (bundle,size,digest) <- prepareIndexed limit tree >>= either R.resourceFault pure + unless (digest == expected) (R.resourceFault (R.Problem "integrity-failed" "write" "CAS digest mismatch" "none")) + let allBytes bytes = unless (BS.null bytes) $ do + count <- send bytes + allBytes (BS.drop count bytes) + emitBundle R.chunkLimit bundle allBytes + pure (size,digest) + +-- Compare the exact canonical bytes, not merely their digest. This consumes one +-- known-size frame without an EOF probe; source policy must check whole-file EOF +-- when verifying an immutable store winner. No untrusted bytes are decoded. +verifyIndexed :: R.Runtime -> R.Scope -> R.Token -> Integer -> T + -> IO (Either R.Problem (Integer, String)) +verifyIndexed runtime scope reader limit tree + | limit < 0 || limit > R.positionLimit = pure (Left (R.Problem "invalid-bound" "verify" "invalid encoded size limit" "none")) + | otherwise = R.withReaderLease runtime scope reader $ \receive -> do + (bundle,size,digest) <- prepareIndexed limit tree >>= either R.resourceFault pure + let compareBytes expected = unless (BS.null expected) $ do + actual <- receive (BS.length expected) + unless (not (BS.null actual) && actual == BS.take (BS.length actual) expected) + (R.resourceFault (R.Problem "integrity-failed" "verify" "indexed frame differs from canonical tree" "unknown")) + compareBytes (BS.drop (BS.length actual) expected) + emitBundle R.chunkLimit bundle compareBytes + pure (size,digest) diff --git a/src/IOV2/Context.hs b/src/IOV2/Context.hs @@ -0,0 +1,26 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Read-only host context; no global cwd/environment mutation or implicit state. +module IOV2.Context (workingDirectory, environmentVariable, boundedText) where +import qualified IOV2.Resources as R +import Control.Exception +import qualified System.Directory as D +import qualified System.Environment as E + +-- Bound the returned text; the host library may materialize it before this check. +boundedText :: String -> String -> Either R.Problem String +boundedText operation value + | length (take 4097 value) > 4096 = Left (R.Problem "invalid-bound" operation "host context text exceeds scalar limit" "none") + | any (\c -> c == '\0' || (c >= '\xD800' && c <= '\xDFFF')) value = + Left (R.Problem "invalid-data" operation "host context is not NUL-free Unicode scalar text" "none") + | otherwise = Right value + +workingDirectory :: IO (Either R.Problem FilePath) +workingDirectory = (boundedText "working-directory" <$> D.getCurrentDirectory) + `catch` (\(e :: IOException) -> pure (Left (R.ioProblem "working-directory" e))) + +environmentVariable :: String -> IO (Either R.Problem (Maybe String)) +environmentVariable name = case boundedText "environment-variable" name of + Left problem -> pure (Left problem) + Right checked | null checked || '=' `elem` checked -> pure (Left (R.Problem "invalid-argument" "environment-variable" "" "none")) + Right checked -> (E.lookupEnv checked >>= pure . traverse (boundedText "environment-variable")) + `catch` (\(e :: IOException) -> pure (Left (R.ioProblem "environment-variable" e))) diff --git a/src/IOV2/Descriptor.hs b/src/IOV2/Descriptor.hs @@ -0,0 +1,29 @@ +-- Owned raw descriptors: a failed close must never be retried against a number +-- the OS may already have recycled. No public descriptors or unrestricted FFI. +module IOV2.Descriptor (closeOnce, releaseOnce, duplicateSync) where + +import Control.Exception (bracketOnError, mask_) +import Control.Monad (when) +import Data.IORef +import GHC.IO.Handle (hDuplicate) +import System.IO (Handle, hClose) +import System.Posix.IO (closeFd, handleToFd) +import System.Posix.Types (Fd) +import System.Posix.Unistd (fileSynchronise) + +closeOnce :: Fd -> IO (IO ()) +closeOnce fd = releaseOnce (closeFd fd) + +-- Injectable action for close-failure/cancellation controls. +releaseOnce :: IO () -> IO (IO ()) +releaseOnce action = do + owned <- newIORef True + pure $ mask_ $ do + close <- atomicModifyIORef' owned (\old -> (False,old)) + when close action + +duplicateSync :: Handle -> IO (IO (), IO ()) +duplicateSync handle = mask_ $ do + fd <- bracketOnError (hDuplicate handle) hClose handleToFd + release <- closeOnce fd + pure (fileSynchronise fd, release) diff --git a/src/IOV2/Digest.hs b/src/IOV2/Digest.hs @@ -0,0 +1,30 @@ +-- Scoped SHA-256 state, deliberately independent of writers and publication. +module IOV2.Digest (openDigest) where + +import qualified IOV2.Resources as R +import Crypto.Hash (Context, SHA256, Digest, hashInit, hashUpdate, hashFinalize) +import Data.ByteArray (convert) +import qualified Data.ByteString.Base16 as Hex +import qualified Data.ByteString.Char8 as BC +import Data.IORef +import Control.Exception (evaluate, mask_) + +openDigest :: R.Runtime -> R.Scope -> String -> IO (Either R.Problem R.Token) +openDigest runtime scope algorithm + | algorithm /= "sha256" = pure (Left (R.Problem "unsupported" "digest-open" (take 128 algorithm) "none")) + | otherwise = R.acquire runtime scope $ do + state <- newIORef (Just (hashInit :: Context SHA256)) + let active op = readIORef state >>= maybe + (R.resourceFault (R.Problem "invalid-state" op "digest already finished" "none")) pure + update bytes = mask_ $ do + context <- active "digest-update" + -- Force the new context before storing it: no chain of old chunks. + next <- evaluate (hashUpdate context bytes) + writeIORef state (Just next) + finish = mask_ $ do + context <- active "digest-finish" + let encoded = Hex.encode (convert (hashFinalize context :: Digest SHA256)) + _ <- evaluate (BC.length encoded) + writeIORef state Nothing + pure (BC.unpack encoded) + pure (R.Digest update finish (writeIORef state Nothing)) diff --git a/src/IOV2/Driver.hs b/src/IOV2/Driver.hs @@ -0,0 +1,451 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Native v2 structured tasks, resource ownership and scoped operations. +module IOV2.Driver (Outcome(..), run, runDetailed, runInRuntime, canonicalNat, decodeChunk) where + +import Runtime (T(..), apply, ofNumber, ofString, ofList, ofBytes, toString) +import qualified IOV2.Resources as R +import qualified IOV2.Protocol as P +import IOV2.Protocol (canonicalNat, problemTree) +import qualified IOV2.Tasks as S +import qualified IOV2.Staging as Stage +import qualified IOV2.Digest as Digest +import qualified IOV2.Filesystem as FS +import qualified IOV2.Network as TCP +import qualified IOV2.Codec as Codec +import qualified IOV2.UTF8 as UTF8 +import qualified IOV2.Resolver as Resolver +import qualified IOV2.Clock as Clock +import qualified IOV2.Process as Process +import qualified IOV2.Context as Context +import qualified Control.Concurrent as Concurrent +import IOV2.Tasks (Outcome(..)) +import qualified Data.ByteString as BS + + +decodeChunk :: T -> Either String BS.ByteString +decodeChunk = fmap BS.pack . walk R.chunkLimit + where + walk _ Leaf = Right [] + walk 0 _ = Left "chunk exceeds bound" + walk n (Fork value rest) = case canonicalNat 8 value of + Just byte -> (fromIntegral byte :) <$> walk (n-1) rest + _ -> Left "noncanonical octet" + walk _ _ = Left "improper chunk" + +ok, err :: T -> T +ok value = Fork (Stem Leaf) (Fork value Leaf) +err value = Fork Leaf (Fork value Leaf) +reply :: (a -> T) -> Either R.Problem a -> T +reply encode = either (err . problemTree) (ok . encode) +bad :: String -> T +bad op = err (problemTree (R.Problem "invalid-argument" op "" "none")) +-- Only host-generated operation Results pass here. Successful payloads and +-- arbitrary user failure diagnostics remain opaque; no error is inferred from +-- a pure computation's value. Keep category/detail/progress exactly intact. +qualifyError :: String -> T -> T +qualifyError operation result = case result of + Fork Leaf (Fork (Fork category (Fork _ rest)) Leaf) -> + err (Fork category (Fork (ofString operation) rest)) + _ -> result + +protocol :: Outcome +protocol = protocolFailure "invalid v2 computation" +protocolFailure :: String -> Outcome +protocolFailure detail = Failed (problemTree (R.Problem "protocol-error" "core/decode" detail "none")) [] + +tokenTree :: R.Token -> T +tokenTree (R.Token identity key) = ofList [ofString "io.v2.resource", ofNumber identity, ofNumber key] +decodeToken :: T -> Maybe R.Token +decodeToken (Fork tag (Fork runId (Fork key Leaf))) + | tag == ofString "io.v2.resource" = R.Token <$> canonicalNat 64 runId <*> canonicalNat 64 key +decodeToken _ = Nothing + +-- Scope references also carry a run identity, supplied by a private root token. +-- Use a separate marker so a resource cannot be passed as a scope. +scopeTree :: R.Token -> R.Scope -> T +scopeTree (R.Token identity _) scope = ofList [ofString "io.v2.scope", ofNumber identity, ofNumber scope] +decodeScope :: R.Token -> T -> Maybe R.Scope +decodeScope (R.Token identity _) (Fork tag (Fork runId (Fork key Leaf))) + | tag == ofString "io.v2.scope", Just identity == canonicalNat 64 runId = canonicalNat 64 key +decodeScope _ _ = Nothing + +runDetailed :: T -> IO Outcome +runDetailed action = R.newRuntime >>= \runtime -> runInRuntime runtime action + +-- One execution owns this runtime's root and always closes it. Exposed for +-- embedding/backend tests; a closed runtime cannot be reused for acquisitions. +runInRuntime :: R.Runtime -> T -> IO Outcome +runInRuntime runtime action = do + identity <- R.runtimeIdentity runtime + scheduler <- S.newScheduler runtime + group <- S.rootGroup scheduler + S.runGroup group (loop runtime (R.Token identity 0) group action []) + +run :: T -> IO (Either String T) +run action = do + outcome <- runDetailed action + pure $ case outcome of + Completed value -> Right value + Cancelled _ cleanup -> Left ("IO v2 task cancelled (reason not forced; " ++ show (length cleanup) ++ " cleanup diagnostics)") + Failed _ cleanup -> Left ("IO v2 task failed (diagnostic payload not forced; " ++ show (length cleanup) ++ " cleanup diagnostics)") + +loop :: R.Runtime -> R.Token -> S.Group -> T -> [T] -> IO Outcome +loop runtime marker group action frames = S.checkpoint group >> step + where + scope = S.scopeOf group + step = case P.decodeAction action of + Left detail -> pure (protocolFailure detail) + Right (P.Core code payload) -> dispatch code payload $ case lookup code + [(5,"spawn"),(6,"join"),(7,"discard"),(8,"cancel")] of + Just name -> done . qualifyError ("core/" ++ name) + Nothing -> done + Right (P.Request facility revision operation arguments) -> + case P.resolve facility revision operation of + Just code -> dispatch code arguments (done . qualifyError (facility ++ "/" ++ operation)) + Nothing -> done (reply id (Left (R.Problem "unsupported" + (facility ++ "/" ++ operation) ("group revision " ++ show revision) "none"))) + dispatch code payload finish = case code of + 0 -> finish payload + 1 -> case payload of + Fork first next -> loop runtime marker group first (next:frames) + _ -> pure protocol + 2 -> pure (Failed payload []) + 3 -> do + result <- S.nested group (\child -> loop runtime marker child + (apply payload (scopeTree marker (S.scopeOf child))) []) + case result of Completed value -> finish value; failure -> pure failure + 4 -> finish (scopeTree marker scope) + 5 -> case payload of + Fork supervised (Fork keep (Fork handles callback)) -> + case (canonicalNat 1 supervised, canonicalNat 1 keep, decodeHandles handles) of + (Just contain, Just retain, Just tokens) | contain <= 1 && retain <= 1 -> do + result <- S.spawn group (contain == 1) (retain == 1) tokens (\child fresh -> + loop runtime marker child (apply callback (ofList (map tokenTree fresh))) []) + finish (reply (spawnReply (retain == 1)) result) + _ -> finish (bad "spawn") + _ -> finish (bad "spawn") + 6 -> case decodeTask "io.v2.join" payload of + Just ref -> S.join group ref >>= finish . reply outcomeTree + _ -> finish (bad "join") + 7 -> case decodeTask "io.v2.join" payload of + Just ref -> S.discard group ref >>= finish . reply (const Leaf) + _ -> finish (bad "discard") + 8 -> case payload of + Fork handle reason -> case decodeTask "io.v2.task" handle of + Just ref -> S.cancel group ref reason >>= finish . reply (const Leaf) + _ -> finish (bad "cancel") + _ -> finish (bad "cancel") + 9 -> pure (Cancelled payload []) + 20 -> open finish R.openReader "open-reader" payload + 21 -> open finish R.openWriter "open-writer" payload + 22 -> case payload of + Fork handle count -> case (decodeToken handle, canonicalNat 16 count) of + (Just token, Just n) -> R.readSome runtime scope token (fromInteger n) >>= finish . reply readValue + _ -> finish (bad "read") + _ -> finish (bad "read") + 23 -> case payload of + Fork handle bytes -> case (decodeToken handle, decodeChunk bytes) of + (Just token, Right chunk) -> R.writeSome runtime scope token chunk >>= finish . reply (ofNumber . toInteger) + _ -> finish (bad "write") + _ -> finish (bad "write") + 24 -> case decodeToken payload of + Just token -> R.closeResource runtime scope token >>= finish . reply (const Leaf) + _ -> finish (bad "close") + 25 -> standard finish R.StandardInput payload + 26 -> standard finish R.StandardOutput payload + 27 -> standard finish R.StandardError payload + 50 -> open finish R.openAppender "open-appender" payload + 51 -> open finish R.openUpdate "open-update" payload + 52 -> positionOperation finish R.seekFile "seek" payload + 53 -> unary finish "position" R.filePosition ofNumber payload + 54 -> unary finish "size" R.fileSize ofNumber payload + 55 -> positionOperation finish R.resizeFile "resize" payload + 30 -> open finish Stage.openStage "stage-open" payload + 31 -> unary finish "seal" R.sealStage (const Leaf) payload + 32 -> case payload of + Fork handle (Fork mode path) -> case (decodeToken handle, canonicalNat 1 mode, canonicalText path) of + (Just token, Just replace, Just destination) | replace <= 1 -> + R.publishStage runtime scope token (replace == 1) destination >>= finish . reply ofString + _ -> finish (bad "publish") + _ -> finish (bad "publish") + 40 -> open finish Digest.openDigest "digest-open" payload + 41 -> case payload of + Fork handle bytes -> case (decodeToken handle, decodeChunk bytes) of + (Just token, Right chunk) -> R.updateDigest runtime scope token chunk >>= finish . reply (const Leaf) + _ -> finish (bad "digest-update") + _ -> finish (bad "digest-update") + 42 -> unary finish "digest-finish" R.finishDigest ofString payload + 60 -> pathOperation finish "stat" (FS.statPath True) statusTree payload + 61 -> pathOperation finish "lstat" (FS.statPath False) statusTree payload + 62 -> pathOperation finish "create" FS.createDirectory (const Leaf) payload + 63 -> pathOperation finish "remove" FS.removeDirectory (const Leaf) payload + 64 -> pathOperation finish "remove-file" FS.removeFile (const Leaf) payload + 65 -> case payload of + Fork old new -> case (canonicalText old, canonicalText new) of + (Just source, Just target) -> FS.renamePath source target >>= finish . reply (const Leaf) + _ -> finish (bad "rename") + _ -> finish (bad "rename") + 66 -> open finish FS.openDirectory "open" payload + 67 -> unary finish "next" R.nextDirectory + (maybe (Fork (ofString "eof") Leaf) (Fork (ofString "entry") . ofString)) payload + 80 -> case payload of + Fork ownerRef (Fork endpoint backlog) -> case + (decodeScope marker ownerRef, decodeEndpoint endpoint, canonicalNat 16 backlog) of + (Just owner, Just target, Just limit) -> owned finish "listen" owner + (TCP.listenTCP runtime owner target (fromInteger limit)) + _ -> finish (bad "listen") + _ -> finish (bad "listen") + 81 -> case payload of + Fork ownerRef endpoint -> case (decodeScope marker ownerRef, decodeEndpoint endpoint) of + (Just owner, Just target) -> owned finish "connect" owner (TCP.connectTCP runtime owner target) + _ -> finish (bad "connect") + _ -> finish (bad "connect") + 82 -> case payload of + Fork handle ownerRef -> case (decodeToken handle, decodeScope marker ownerRef) of + (Just listener, Just owner) -> owned finish "accept" owner + (R.acceptConnection runtime scope listener owner) + _ -> finish (bad "accept") + _ -> finish (bad "accept") + 83 -> unary finish "local-address" (\r s t -> R.socketAddress r s t False) endpointTree payload + 84 -> unary finish "peer-address" (\r s t -> R.socketAddress r s t True) endpointTree payload + 85 -> unary finish "shutdown-write" R.shutdownWrite (const Leaf) payload + 86 -> case payload of + Fork host (Fork family (Fork port (Fork limit Leaf))) -> case + (boundedHost host, resolveFamily family, canonicalNat 16 port, canonicalNat 8 limit) of + (Just name, Just selection, Just service, Just count) -> + Resolver.resolveTCP name selection service (fromInteger count) >>= finish . reply + (\(Resolver.Resolution endpoints truncated) -> ofList + [ofList (map endpointTree endpoints),ofNumber (if truncated then 1 else 0)]) + _ -> finish (bad "resolve") + _ -> finish (bad "resolve") + 87 -> case payload of + Fork reader (Fork directory (Fork limit (Fork size (Fork digest Leaf)))) -> case + (decodeToken reader, canonicalText directory, canonicalNat 63 limit, + canonicalNat 63 size, boundedDigest digest) of + (Just token, Just path, Just maximum, Just count, Just expected) -> + Codec.readIndexed runtime scope token path maximum count expected >>= finish . reply id + _ -> finish (bad "read") + _ -> finish (bad "read") + 88 -> case payload of + Fork limit tree -> case canonicalNat 63 limit of + Just maximum -> Codec.hashIndexed maximum tree >>= finish . reply encodedInfo + _ -> finish (bad "hash") + _ -> finish (bad "hash") + 89 -> case payload of + Fork writer (Fork limit (Fork digest (Fork tree Leaf))) -> case + (decodeToken writer, canonicalNat 63 limit, boundedDigest digest) of + (Just token, Just maximum, Just expected) -> + Codec.writeIndexed runtime scope token maximum expected tree >>= finish . reply encodedInfo + _ -> finish (bad "write") + _ -> finish (bad "write") + 95 -> case payload of + Fork reader (Fork limit (Fork tree Leaf)) -> case (decodeToken reader, canonicalNat 63 limit) of + (Just token, Just maximum) -> Codec.verifyIndexed runtime scope token maximum tree >>= finish . reply encodedInfo + _ -> finish (bad "verify") + _ -> finish (bad "verify") + 56 -> unary finish "sync" R.syncFile (const Leaf) payload + 68 -> pathOperation finish "sync" (FS.syncDirectory runtime scope) (const Leaf) payload + 96 -> case unicodeChunk UTF8.scalarLimit payload of + Just text -> finish (reply ofBytes (UTF8.encode text)) + _ -> finish (bad "encode") + 97 -> case payload of + Fork final (Fork bytes Leaf) -> case (canonicalNat 1 final, decodeChunk bytes) of + (Just end, Right chunk) -> finish (reply (\(text,carry) -> ofList [ofString text,ofBytes carry]) + (UTF8.decode (end == 1) chunk)) + _ -> finish (bad "decode") + _ -> finish (bad "decode") + 102 -> case payload of + Leaf -> Clock.monotonicNanoseconds >>= finish . ok . ofNumber + _ -> finish (bad "monotonic") + 103 -> case canonicalNat 63 payload of + Just milliseconds -> Clock.sleepMilliseconds milliseconds >> finish (ok Leaf) + _ -> finish (bad "sleep") + 104 -> case payload of + Leaf -> Concurrent.yield >> finish (ok Leaf) + _ -> finish (bad "yield") + 106 -> case payload of + Leaf -> Context.workingDirectory >>= finish . reply ofString + _ -> finish (bad "working-directory") + 107 -> case processText payload of + Just name -> Context.environmentVariable name >>= finish . reply + (maybe (ofList [ofString "missing"]) (\value -> ofList [ofString "value", ofString value])) + _ -> finish (bad "environment-variable") + 105 -> case decodeProcess payload of + Just spec -> Process.launch runtime group spec >>= finish . reply + (\(task,pipes) -> ofList ([taskTree "io.v2.task" task, taskTree "io.v2.join" task] ++ map tokenTree pipes)) + Nothing -> finish (bad "spawn") + 100 -> case payload of + Leaf -> finish (ok P.capabilities) + _ -> finish (bad "capabilities") + 101 -> finish (reply id (P.inspectAction payload)) + _ -> pure protocol + encodedInfo (size,digest) = ofList [ofNumber size, ofString digest] + done value = case frames of + [] -> pure (Completed value) + next:rest -> loop runtime marker group (apply next value) rest + owned finish name owner action = do + allowed <- R.isAncestor runtime scope owner + if allowed then action >>= finish . reply tokenTree else finish (bad name) + pathOperation finish name operation encode payload = case canonicalText payload of + Just path -> operation path >>= finish . reply encode + _ -> finish (bad name) + statusTree status = case status of + FS.Regular size -> ofList [ofString "file",ofNumber size] + FS.Directory -> ofList [ofString "directory"] + FS.Symlink -> ofList [ofString "symlink"] + FS.Other -> ofList [ofString "other"] + positionOperation finish operation name payload = case payload of + Fork handle value -> case (decodeToken handle, canonicalNat 63 value) of + (Just token, Just position) -> operation runtime scope token position >>= finish . reply (const Leaf) + _ -> finish (bad name) + _ -> finish (bad name) + standard finish stream payload = case payload of + Leaf -> R.standardStream runtime scope stream >>= finish . reply tokenTree + _ -> finish (bad "standard-stream") + readValue bytes | BS.null bytes = Fork (ofString "eof") Leaf + | otherwise = Fork (ofString "data") (ofBytes bytes) + unary finish name operation encode value = case decodeToken value of + Just token -> operation runtime scope token >>= finish . reply encode + _ -> finish (bad name) + open finish opener name value = case value of + Fork scopeRef path -> case (decodeScope marker scopeRef, canonicalText path) of + (Just owner, Just filename) -> do + allowed <- R.isAncestor runtime scope owner + if allowed then opener runtime owner filename >>= finish . reply tokenTree else finish (bad name) + _ -> finish (bad name) + _ -> finish (bad name) + +-- Transfer lists are bounded separately from binary chunks. Excess tails are +-- not forced; the bound is shared with capability reporting. +decodeHandles :: T -> Maybe [R.Token] +decodeHandles = walk P.transferLimit + where + walk _ Leaf = Just [] + walk 0 _ = Nothing + walk n (Fork h rest) = (:) <$> decodeToken h <*> walk (n-1) rest + walk _ _ = Nothing + +taskTree :: String -> S.TaskRef -> T +taskTree tag (S.TaskRef identity key) = ofList [ofString tag, ofNumber identity, ofNumber key] +decodeTask :: String -> T -> Maybe S.TaskRef +decodeTask expected (Fork tag (Fork identity (Fork key Leaf))) + | tag == ofString expected = S.TaskRef <$> canonicalNat 64 identity <*> canonicalNat 64 key +decodeTask _ _ = Nothing +spawnReply :: Bool -> S.TaskRef -> T +spawnReply keep ref = ofList [taskTree "io.v2.task" ref, if keep then taskTree "io.v2.join" ref else Leaf] +outcomeTree :: Outcome -> T +outcomeTree (Completed value) = ofList [ofString "completed", value] +outcomeTree (Failed diagnostic cleanup) = ofList [ofString "failed", diagnostic, ofList (map problemTree cleanup)] +outcomeTree (Cancelled reason cleanup) = ofList [ofString "cancelled", reason, ofList (map problemTree cleanup)] + +canonicalText :: T -> Maybe String +canonicalText value = case toString value of + Right text | '\0' `notElem` text && value == ofString text -> Just text + _ -> Nothing + +-- Endpoint components have fixed lengths; an excess element is not inspected. +decodeEndpoint :: T -> Maybe R.Endpoint +decodeEndpoint (Fork tag (Fork bytes (Fork port Leaf))) + | tag == ofString "ipv4" = do + values <- components 4 8 bytes + p <- canonicalNat 16 port + case values of + [a,b,c,d] -> Just (R.IPv4 (fromInteger a,fromInteger b,fromInteger c,fromInteger d) (fromInteger p)) + _ -> Nothing +decodeEndpoint (Fork tag (Fork words (Fork scope (Fork port Leaf)))) + | tag == ofString "ipv6" = do + values <- components 8 16 words + s <- canonicalNat 32 scope + p <- canonicalNat 16 port + case values of + [a,b,c,d,e,f,g,h] -> Just (R.IPv6 + (fromInteger a,fromInteger b,fromInteger c,fromInteger d,fromInteger e,fromInteger f,fromInteger g,fromInteger h) + (fromInteger s) (fromInteger p)) + _ -> Nothing +decodeEndpoint _ = Nothing +components :: Int -> Int -> T -> Maybe [Integer] +components 0 _ Leaf = Just [] +components 0 _ _ = Nothing +components n bits (Fork first rest) = (:) <$> canonicalNat bits first <*> components (n-1) bits rest +components _ _ _ = Nothing +endpointTree :: R.Endpoint -> T +endpointTree (R.IPv4 (a,b,c,d) p) = ofList [ofString "ipv4",numbers [a,b,c,d],ofNumber (toInteger p)] +endpointTree (R.IPv6 (a,b,c,d,e,f,g,h) s p) = ofList + [ofString "ipv6",numbers [a,b,c,d,e,f,g,h],ofNumber (toInteger s),ofNumber (toInteger p)] +numbers :: Integral a => [a] -> T +numbers = ofList . map (ofNumber . toInteger) + +-- Exactly 64 lowercase hex digits; reject an excess spine without forcing it. +boundedDigest :: T -> Maybe String +boundedDigest = walk (64 :: Int) + where + walk 0 Leaf = Just [] + walk 0 _ = Nothing + walk n (Fork c rest) = do + code <- canonicalNat 7 c + if (code >= 48 && code <= 57) || (code >= 97 && code <= 102) + then (toEnum (fromInteger code) :) <$> walk (n-1) rest + else Nothing + walk _ _ = Nothing + +-- Text codecs accept NUL, unlike paths. Unicode surrogate code points and +-- noncanonical scalar naturals are rejected before conversion to host Char. +unicodeChunk :: Int -> T -> Maybe String +unicodeChunk _ Leaf = Just [] +unicodeChunk 0 _ = Nothing +unicodeChunk n (Fork c rest) = do + scalar <- canonicalNat 21 c + if scalar <= 0x10ffff && not (scalar >= 0xd800 && scalar <= 0xdfff) + then (toEnum (fromInteger scalar) :) <$> unicodeChunk (n-1) rest + else Nothing +unicodeChunk _ _ = Nothing + +resolveFamily :: T -> Maybe Resolver.Family +resolveFamily value + | value == ofString "ipv4" = Just Resolver.IPv4Only + | value == ofString "ipv6" = Just Resolver.IPv6Only + | value == ofString "any" = Just Resolver.AnyFamily + | otherwise = Nothing +boundedHost :: T -> Maybe String +boundedHost = walk Resolver.hostLimit + where walk _ Leaf = Just [] + walk 0 _ = Nothing + walk n (Fork first rest) = do + code <- canonicalNat 7 first + (toEnum (fromInteger code):) <$> walk (n-1) rest + walk _ _ = Nothing + +-- Bounded before constructing host strings/lists; an excess element is inert. +decodeProcess :: T -> Maybe Process.Spec +decodeProcess (Fork executable (Fork arguments (Fork directory (Fork environment (Fork grace Leaf))))) = do + exe <- processText executable + args <- boundedList 256 processText arguments + cwd <- case directory of + Fork tag Leaf | tag == ofString "inherit" -> Just Nothing + Fork tag (Fork path Leaf) | tag == ofString "at" -> Just <$> processText path + _ -> Nothing + env <- case environment of + Fork tag Leaf | tag == ofString "inherit" -> Just Nothing + Fork tag (Fork entries Leaf) | tag == ofString "replace" -> Just <$> boundedList 256 entry entries + _ -> Nothing + duration <- canonicalNat 63 grace + let spec = Process.Spec exe args cwd env duration + if Process.validSpec spec then Just spec else Nothing + where + entry (Fork key (Fork value Leaf)) = (,) <$> processText key <*> processText value + entry _ = Nothing +decodeProcess _ = Nothing + +boundedList :: Int -> (T -> Maybe a) -> T -> Maybe [a] +boundedList _ _ Leaf = Just [] +boundedList 0 _ _ = Nothing +boundedList n decode (Fork value rest) = (:) <$> decode value <*> boundedList (n-1) decode rest +boundedList _ _ _ = Nothing + +processText :: T -> Maybe String +processText = boundedList 4096 scalar + where + scalar value = do + n <- canonicalNat 21 value + if n > 0 && n <= 1114111 && not (n >= 55296 && n <= 57343) + then Just (toEnum (fromInteger n)) else Nothing diff --git a/src/IOV2/Filesystem.hs b/src/IOV2/Filesystem.hs @@ -0,0 +1,109 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Path operations are observations/mutations, not authorization or transactions. +-- Directory cursors never materialize a listing or retain previously returned names. +module IOV2.Filesystem + ( Status(..), statPath, createDirectory, removeDirectory, removeFile, renamePath + , syncDirectory + , openDirectory, directoryBackend, nameLimit + ) where +import qualified IOV2.Descriptor as Descriptor +import System.Posix.IO (openFd, OpenMode(ReadOnly), defaultFileFlags, nonBlock) +import System.Posix.Unistd (fileSynchronise) +import qualified IOV2.Resources as R +import qualified System.Posix.Files as F +import qualified System.Posix.Directory as D +import Control.Exception +import Control.Monad (when, unless) +import Data.IORef +import Data.Char (ord) + +data Status = Regular Integer | Directory | Symlink | Other deriving (Eq, Show) +nameLimit :: Int +nameLimit = 4096 + +pathOperation :: String -> Bool -> [FilePath] -> IO a -> IO (Either R.Problem a) +pathOperation op mutating paths action + | any (\p -> null p || '\0' `elem` p) paths = + pure (Left (R.Problem "invalid-argument" op "empty or NUL-containing path" "none")) + | otherwise = (Right <$> action) `catch` \(e :: IOException) -> do + let problem = R.ioProblem op e + pure (Left (problem {R.progress=if mutating then "unknown" else "none"})) + +-- Following the final symlink is an explicit choice; intermediate components +-- always follow the host path resolver. Neither form pins later path operations. +statPath :: Bool -> FilePath -> IO (Either R.Problem Status) +statPath follow path = pathOperation op False [path] $ do + status <- (if follow then F.getFileStatus else F.getSymbolicLinkStatus) path + if F.isRegularFile status then do + let size = toInteger (F.fileSize status) + if size >= 0 && size <= R.positionLimit then pure (Regular size) + else ioError (userError "file size outside supported range") + else pure (if F.isDirectory status then Directory + else if F.isSymbolicLink status then Symlink else Other) + where op = if follow then "stat" else "lstat" + +createDirectory, removeDirectory, removeFile :: FilePath -> IO (Either R.Problem ()) +createDirectory path = pathOperation "create" True [path] (D.createDirectory path 0o777) +removeDirectory path = pathOperation "remove" True [path] (D.removeDirectory path) +removeFile path = pathOperation "remove-file" True [path] (F.removeLink path) +renamePath :: FilePath -> FilePath -> IO (Either R.Problem ()) +renamePath old new = pathOperation "rename" True [old,new] (F.rename old new) + +-- Synchronize an opened directory, not an unchecked path after verification. +-- Nonblocking open avoids hanging on a FIFO supplied where a directory belongs. +syncDirectory :: R.Runtime -> R.Scope -> FilePath -> IO (Either R.Problem ()) +syncDirectory runtime scope path + | null path || '\0' `elem` path = pure (Left (R.Problem "invalid-argument" "sync" "empty or NUL-containing path" "none")) + | otherwise = R.withTemporary runtime scope "sync" (do + fd <- openFd path ReadOnly (defaultFileFlags {nonBlock=True}) + release <- Descriptor.closeOnce fd + pure (fd,release)) $ \fd -> do + status <- F.getFdStatus fd + unless (F.isDirectory status) (R.resourceFault (R.Problem "not-directory" "sync" "path is not a directory" "none")) + fileSynchronise fd + +openDirectory :: R.Runtime -> R.Scope -> FilePath -> IO (Either R.Problem R.Token) +openDirectory runtime scope path + | null path || '\0' `elem` path = pure (Left (R.Problem "invalid-argument" "open" "empty or NUL-containing path" "none")) + | otherwise = R.acquire runtime scope $ + bracketOnError (D.openDirStream path) D.closeDirStream $ \stream -> + directoryBackend (D.readDirStream stream) (D.closeDirStream stream) + +data Cursor = Active | Ended | Poisoned +-- Injectable backend callbacks exercise failure/cancellation at the actual cursor +-- boundary. The registry supplies the lease; these callbacks are not standalone +-- concurrent iterators. An uncertain advance cannot be implicitly retried. +directoryBackend :: IO FilePath -> IO () -> IO R.Backend +directoryBackend readName close = do + state <- newIORef Active + owned <- newIORef True + let fault category detail = R.resourceFault (R.Problem category "next" detail "unknown") + validName = walk nameLimit + where + walk _ [] = pure () + walk 0 _ = fault "invalid-name" "directory name exceeds scalar limit" + walk n (c:rest) + | c == '/' || c == '\0' = fault "invalid-name" "invalid directory component" + | ord c > 0x10ffff || (ord c >= 0xd800 && ord c <= 0xdfff) = fault "invalid-encoding" "directory name is not Unicode scalar text" + | otherwise = walk (n-1) rest + advance budget = do + name <- readName + case name of + "" -> writeIORef state Ended >> pure Nothing + "." -> skip budget + ".." -> skip budget + _ -> validName name >> pure (Just name) + skip 0 = fault "invalid-state" "directory dot-entry scan exceeded bound" + skip n = advance (n-1) + next = mask $ \restore -> do + phase <- readIORef state + case phase of + Ended -> pure Nothing + Poisoned -> fault "invalid-state" "directory cursor failed; reopen explicitly" + Active -> restore (advance (2 :: Int)) `onException` writeIORef state Poisoned + release = mask_ $ do + -- closedir owns a raw pointer: never retry an uncertain close against a + -- potentially freed DIR*. Registry teardown can revisit async failures. + wasOwned <- atomicModifyIORef' owned (\old -> (False,old)) + when wasOwned close + pure (R.Directory next release) diff --git a/src/IOV2/Network.hs b/src/IOV2/Network.hs @@ -0,0 +1,97 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- TCP transports: numeric IPv4/IPv6 endpoints, no implicit resolver/retry policy. +module IOV2.Network (listenTCP, connectTCP, duplexBackend, backlogLimit) where +import qualified IOV2.Resources as R +import qualified Network.Socket as N +import qualified Network.Socket.ByteString as B +import qualified Data.ByteString as BS +import Control.Exception +import Control.Monad (when, unless) +import Data.IORef +import GHC.IO.Exception (ioe_errno) +import Foreign.C.Error + +backlogLimit :: Int +backlogLimit = 4096 +address (R.IPv4 host port) = N.SockAddrInet (fromIntegral port) (N.tupleToHostAddress host) +address (R.IPv6 host scope port) = N.SockAddrInet6 (fromIntegral port) 0 (N.tupleToHostAddress6 host) scope +family (R.IPv4 _ _) = N.AF_INET +family (R.IPv6 _ _ _) = N.AF_INET6 +port (R.IPv4 _ p) = p +port (R.IPv6 _ _ p) = p +endpoint (N.SockAddrInet p h) = pure (R.IPv4 (N.hostAddressToTuple h) (fromIntegral p)) +endpoint (N.SockAddrInet6 p _ h scope) = pure (R.IPv6 (N.hostAddress6ToTuple h) scope (fromIntegral p)) +endpoint _ = ioError (userError "unexpected TCP address family") + +networkIO :: String -> IO a -> IO a +networkIO op action = action `catch` \(e :: IOException) -> do + let generic = R.ioProblem op e + category = case ioe_errno e of + Just code -> maybe (R.category generic) id (lookup (Errno code) + [(eCONNREFUSED,"connection-refused"),(eCONNRESET,"connection-reset"), + (eCONNABORTED,"connection-aborted"),(eADDRINUSE,"address-in-use"), + (eADDRNOTAVAIL,"address-unavailable"),(eNETUNREACH,"network-unreachable"), + (eHOSTUNREACH,"host-unreachable"),(ePIPE,"broken-pipe"),(eTIMEDOUT,"timed-out")]) + _ -> R.category generic + R.resourceFault (generic {R.category=category, + R.progress=if op `elem` ["local-address","peer-address"] then "none" else "unknown"}) + +listenTCP :: R.Runtime -> R.Scope -> R.Endpoint -> Int -> IO (Either R.Problem R.Token) +listenTCP runtime owner target backlog + | backlog <= 0 || backlog > backlogLimit = pure (Left (R.Problem "invalid-bound" "listen" "" "none")) + | otherwise = R.acquire runtime owner $ networkIO "listen" $ + bracketOnError (N.socket (family target) N.Stream N.defaultProtocol) N.close $ \socket -> do + N.setSocketOption socket N.ReuseAddr 1 + when (family target == N.AF_INET6) (N.setSocketOption socket N.IPv6Only 1) + N.bind socket (address target) + N.listen socket backlog + let accept = networkIO "accept" $ + bracketOnError (fst <$> N.accept socket) N.close duplexBackend + pure (R.Listener (R.ListenerOps accept + (networkIO "local-address" (N.getSocketName socket >>= endpoint)) (N.close socket))) + +connectTCP :: R.Runtime -> R.Scope -> R.Endpoint -> IO (Either R.Problem R.Token) +connectTCP runtime owner target + | port target == 0 = pure (Left (R.Problem "invalid-bound" "connect" "zero peer port" "none")) + | otherwise = R.acquire runtime owner $ networkIO "connect" $ + bracketOnError (N.socket (family target) N.Stream N.defaultProtocol) N.close $ \socket -> do + N.connect socket (address target) + duplexBackend socket + +data Direction = Open | Ended | Poisoned +-- The registry supplies separate read/write leases; direction state is not +-- synchronized across unrelated sockets, aliases or host IO outside this runtime. +duplexBackend :: N.Socket -> IO R.Backend +duplexBackend socket = do + reading <- newIORef Open + writing <- newIORef Open + let invalid op = R.resourceFault (R.Problem "invalid-state" op "socket direction failed; close explicitly" "unknown") + receive count = mask $ \restore -> do + phase <- readIORef reading + case phase of + Ended -> pure BS.empty + Poisoned -> invalid "read" + Open -> (do + bytes <- restore (networkIO "read" (B.recv socket count)) + when (BS.null bytes) (writeIORef reading Ended) + pure bytes) `onException` writeIORef reading Poisoned + send bytes = mask $ \restore -> do + phase <- readIORef writing + case phase of + Ended -> R.resourceFault (R.Problem "invalid-state" "write" "write direction shut down" "none") + Poisoned -> invalid "write" + Open -> (do + count <- restore (networkIO "write" (B.send socket bytes)) + unless (count > 0 && count <= BS.length bytes) + (ioError (userError "invalid socket write progress")) + pure count) `onException` writeIORef writing Poisoned + shutdown = mask $ \restore -> do + phase <- readIORef writing + case phase of + Ended -> pure () + Poisoned -> invalid "shutdown-write" + Open -> (restore (networkIO "shutdown-write" (N.shutdown socket N.ShutdownSend)) >> + writeIORef writing Ended) `onException` writeIORef writing Poisoned + pure (R.Duplex (R.DuplexOps receive send shutdown + (networkIO "local-address" (N.getSocketName socket >>= endpoint)) + (networkIO "peer-address" (N.getPeerName socket >>= endpoint)) (N.close socket))) diff --git a/src/IOV2/Process.hs b/src/IOV2/Process.hs @@ -0,0 +1,173 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Direct-child processes, supervised by the existing structured task machinery. +-- No shell expansion, detached waiter, implicit output collection or job control. +module IOV2.Process (Spec(..), launch, validSpec, TerminationOps(..), terminateWith, cleanupAll) where + +import qualified IOV2.Resources as R +import qualified IOV2.Tasks as S +import qualified IOV2.Clock as Clock +import Runtime (T(..), ofList, ofString, ofNumber) +import Control.Concurrent (threadDelay) +import Control.Concurrent.STM +import Control.Exception +import Control.Monad +import Data.IORef +import Data.List (nub) +import qualified Data.ByteString as BS +import System.Exit +import System.IO +import qualified System.Process as P +import System.Posix.Signals (signalProcess, sigKILL) + +-- Nothing explicitly selects inherited cwd/environment. Just [] is an empty env. +-- Limits count Unicode scalars, not OS-encoded bytes; exec may impose smaller limits. +data Spec = Spec FilePath [String] (Maybe FilePath) (Maybe [(String,String)]) Integer + deriving Show + +validSpec :: Spec -> Bool +validSpec (Spec executable arguments directory environment grace) = + not (null executable) && maybe True (not . null) directory && + length arguments <= 256 && length variables <= 256 && + all validText strings && sum (map length strings) <= 65536 && + all (\(key,_) -> not (null key) && '=' `notElem` key) variables && + length (nub (map fst variables)) == length variables && + grace >= 0 && grace <= 9223372036854775807 + where + variables = maybe [] id environment + strings = executable : arguments ++ maybe [] (:[]) directory ++ concatMap (\(k,v) -> [k,v]) variables + validText s = length s <= 4096 && all (\c -> c /= '\0' && not (c >= '\xD800' && c <= '\xDFFF')) s + +-- Returns one ordinary task/join reference and parent-owned stdin/stdout/stderr. +-- The private process handle is owned by the monitor child, never source-visible. +-- Launch failures consume the internal join ticket before returning an error. +launch :: R.Runtime -> S.Group -> Spec -> IO (Either R.Problem (S.TaskRef, [R.Token])) +launch runtime parent spec + | not (validSpec spec) = pure (Left (R.Problem "invalid-argument" "process/spawn" "" "none")) + | otherwise = mask $ \restore -> do + ready <- newEmptyTMVarIO + spawned <- S.spawn parent False True [] (\child _ -> monitor ready child) + case spawned of + Left problem -> pure (Left problem) + Right task -> do + result <- restore (atomically (takeTMVar ready)) + case result of + Right pipes -> pure (Right (task,pipes)) + Left problem -> do + -- Failure is not acknowledged until the launched process is reaped. + settled <- restore (S.join parent task) + case settled of + Right (S.Completed _) -> pure (Left problem) + _ -> S.checkpoint parent >> R.resourceFault problem + where + Spec executable arguments directory environment grace = spec + monitor ready child = do + slot <- newIORef Nothing + acquired <- R.acquire runtime (S.scopeOf child) $ do + (input,output,errors,process) <- P.createProcess (P.proc executable arguments) + { P.cwd = directory, P.env = environment, P.std_in = P.CreatePipe + , P.std_out = P.CreatePipe, P.std_err = P.CreatePipe, P.close_fds = True } + let handles = [h | Just h <- [input,output,errors]] + raw <- newIORef handles + let release = cleanupAll [releaseRaw raw, terminateAndReap process grace] + writeIORef slot (Just (process,raw,handles)) + pure (R.Internal release) + case acquired of + Left problem -> atomically (putTMVar ready (Left problem)) >> pure (S.Completed Leaf) + Right _ -> do + Just (process,raw,handles) <- readIORef slot + case handles of + [input,output,errors] -> do + pipes <- install raw [] [(True,input),(False,output),(False,errors)] + case pipes of + Left problem -> atomically (putTMVar ready (Left problem)) >> pure (S.Completed Leaf) + Right tokens -> do + atomically (putTMVar ready (Right tokens)) + -- This wait is body work, not a protected release callback. + status <- awaitExit process + pure (S.Completed (exitTree status)) + _ -> ioError (userError "process backend omitted requested pipes") + install _ installed [] = pure (Right (reverse installed)) + install raw installed ((writing,h):rest) = do + result <- R.acquire runtime (S.scopeOf parent) $ do + hSetBinaryMode h True + hSetBuffering h NoBuffering + poisoned <- newIORef False + let guarded action = mask $ \restore -> do + failed <- readIORef poisoned + if failed then R.resourceFault (R.Problem "invalid-state" "process/pipe" "prior operation failed; progress is uncertain" "unknown") + else restore action `onException` writeIORef poisoned True + -- Publication is masked by acquire; thereafter registry release owns h. + modifyIORef' raw (filter (/= h)) + pure $ if writing + then R.Writer (\bytes -> guarded (BS.hPut h bytes >> pure (BS.length bytes))) (hClose h) + else R.Reader (\count -> guarded (BS.hGetSome h count)) (hClose h) + case result of + Right token -> install raw (token:installed) rest + Left problem -> do + cleanupAll [R.closeResource runtime (S.scopeOf parent) token >>= either R.resourceFault pure | token <- installed] + pure (Left problem) + +exitTree :: ExitCode -> T +exitTree ExitSuccess = ofList [ofString "exit", ofNumber 0] +exitTree (ExitFailure code) + | code < 0 = ofList [ofString "signal", ofNumber (negate (toInteger code))] + | otherwise = ofList [ofString "exit", ofNumber (toInteger code)] + +releaseRaw :: IORef [Handle] -> IO () +releaseRaw raw = atomicModifyIORef' raw (\handles -> ([],handles)) >>= cleanupAll . map hClose + +-- Attempt all releases, preserving the first exception. Resource settlement +-- reports failure rather than silently acknowledging a partial cleanup. +cleanupAll :: [IO ()] -> IO () +cleanupAll actions = do + outcomes <- mapM try actions + case [e | Left (e :: SomeException) <- outcomes] of + [] -> pure () + [e] -> throwIO e + e:rest -> R.resourceFault (R.Problem "cleanup-failed" "process/release" + (take 240 (displayException e) ++ "; additional cleanup errors omitted: " ++ show (length rest)) "unknown") + +-- Injectable boundary for independent escalation/error-order tests. The owner +-- must settle the sole wait body before entering this release protocol. +data TerminationOps = TerminationOps + { checkReaped :: IO Bool, requestTerminate :: IO (), forceTerminate :: IO () + , reap :: IO (), clock :: IO Integer, pause :: IO () } + +terminateWith :: TerminationOps -> Integer -> IO () +terminateWith ops grace = do + exited <- checkReaped ops + unless exited $ do + start <- clock ops + let wait remaining previous = do + done <- checkReaped ops + unless done $ do + now <- clock ops + -- Host samples are Word64. Account for wrap between nearby polls, + -- rather than constructing an unreachable absolute deadline. + let elapsed = (now - previous) `mod` 18446744073709551616 + if elapsed < remaining then pause ops >> wait (remaining-elapsed) now + else cleanupAll [forceTerminate ops, reap ops] + cleanupAll [requestTerminate ops, wait (grace*1000000) start] + +terminateAndReap :: P.ProcessHandle -> Integer -> IO () +terminateAndReap process = terminateWith TerminationOps + { checkReaped = maybe False (const True) <$> P.getProcessExitCode process + , requestTerminate = P.terminateProcess process + , forceTerminate = do + -- No other reaper remains: an unreaped child retains its PID. Never + -- expose this handle for concurrent public wait/getPid operations. + pid <- P.getPid process + case pid of Just identity -> signalProcess sigKILL identity; Nothing -> pure () + , reap = void (awaitExit process) + , clock = Clock.monotonicNanoseconds + , pause = threadDelay 10000 } + +-- waitForProcess may block an RTS capability in a foreign wait (and cannot be +-- relied on as a cancellation checkpoint). WNOHANG polling keeps the sole reaper +-- in the task body and is interruptible without a detached host waiter. +awaitExit :: P.ProcessHandle -> IO ExitCode +awaitExit process = do + status <- P.getProcessExitCode process + case status of + Just result -> pure result + Nothing -> threadDelay 10000 >> awaitExit process diff --git a/src/IOV2/Protocol.hs b/src/IOV2/Protocol.hs @@ -0,0 +1,82 @@ +-- Shared native v2 request catalogue and shallow, non-executing inspection. +-- Dispatch numbers below are PRIVATE implementation selectors. +module IOV2.Protocol + ( Action(..), decodeAction, canonicalNat, resolve, capabilities, inspectAction + , problemTree, transferLimit + ) where + +import Runtime (T(..), ofList, ofString, ofNumber) +import qualified IOV2.Resources as R +import qualified IOV2.Filesystem as FS +import qualified IOV2.Network as TCP +import qualified IOV2.Codec as Codec +import qualified IOV2.UTF8 as UTF8 +import qualified IOV2.Resolver as Resolver +import qualified IOV2.Clock as Clock +import Data.List (find) +import IOV2.Wire (Action(..), canonicalNat, decodeAction) + +-- Only implemented revisions/operations are advertised. Unsupported names are +-- not promises about a future group's argument schema or API. +catalogue :: [(String, [(String, Integer)])] +catalogue = + [ ("runtime", [("capabilities",100), ("inspect",101)]) + , ("standard", [("input",25), ("output",26), ("error",27)]) + , ("file", [("open-reader",20), ("open-writer",21), ("open-appender",50), ("open-update",51), ("seek",52), ("position",53), ("size",54), ("resize",55), ("sync",56)]) + , ("path", [("stat",60), ("lstat",61), ("remove-file",64), ("rename",65)]) + , ("directory", [("create",62), ("remove",63), ("open",66), ("next",67), ("sync",68)]) + , ("tcp", [("listen",80), ("connect",81), ("accept",82), ("local-address",83), ("peer-address",84), ("shutdown-write",85)]) + , ("resolver", [("resolve",86)]) + , ("indexed-tree", [("read",87), ("hash",88), ("write",89), ("verify",95)]) + , ("utf8", [("encode",96), ("decode",97)]) + , ("stream", [("read",22), ("write",23)]) + , ("resource", [("close",24)]) + , ("stage", [("open",30), ("seal",31), ("publish",32)]) + , ("digest", [("open",40), ("update",41), ("finish",42)]) + , ("clock", [("monotonic",102), ("sleep",103)]) + , ("scheduler", [("yield",104)]) + , ("process", [("spawn",105), ("working-directory",106), ("environment-variable",107)]) + ] + +resolve :: String -> Integer -> String -> Maybe Integer +resolve group 1 operation = lookup group catalogue >>= lookup operation +resolve _ _ _ = Nothing + +core :: [(Integer, String)] +core = zip [0..9] ["pure", "bind", "fail", "scope", "current-scope", "spawn", "join", "discard", "cancel", "cancelled"] + +transferLimit :: Int +transferLimit = 256 + +problemTree :: R.Problem -> T +problemTree p = ofList (map ofString [R.category p, R.operation p, R.detail p, R.progress p]) + +-- Association rows keep metadata extensible without making it authority. +capabilities :: T +capabilities = ofList + [ row "schema" (ofNumber 1) + , row "io-version" (ofNumber 2) + , row "backend" (ofString "haskell-native") + , row "authority" (ofString "os-process") + , row "core" (strings (map snd core)) + , row "groups" (ofList [ofList [ofString g, ofNumber 1, strings (map fst ops)] | (g,ops) <- catalogue]) + , row "limits" (ofList [row "chunk-bytes" (ofNumber (toInteger R.chunkLimit)), row "transfer-handles" (ofNumber (toInteger transferLimit)), row "file-offset-max" (ofNumber R.positionLimit), row "directory-name-scalars" (ofNumber (toInteger FS.nameLimit)), row "tcp-backlog" (ofNumber (toInteger TCP.backlogLimit)), row "resolver-host-characters" (ofNumber (toInteger Resolver.hostLimit)), row "resolver-addresses" (ofNumber (toInteger Resolver.addressLimit)), row "indexed-sections" (ofNumber (toInteger Codec.sectionLimit)), row "indexed-manifest-bytes" (ofNumber (toInteger Codec.manifestLimit)), row "utf8-encode-scalars" (ofNumber (toInteger UTF8.scalarLimit)), row "sleep-milliseconds-max" (ofNumber Clock.sleepLimit), row "process-arguments" (ofNumber 256), row "process-environment-entries" (ofNumber 256), row "process-text-scalars" (ofNumber 4096), row "process-total-scalars" (ofNumber 65536), row "process-grace-milliseconds-max" (ofNumber 9223372036854775807)]) + , row "tcp-address-families" (strings ["ipv4","ipv6"]) + , row "digest-algorithms" (strings ["sha256"]) + , row "restrictions" (strings ["action-boundary-cancellation", "no-hard-deadlines", "stage-same-filesystem", "stage-no-copy-fallback", "stage-private-namespace", "publish-new-requires-hard-links", "no-crash-recovery", "borrowed-standard-streams", "standard-stream-leases-per-run", "file-single-cursor-lease", "append-no-position-or-resize","file-host-handle-locking", "directory-unsorted-non-snapshot", "filesystem-host-encoding", "directory-failure-poisons-cursor", "tcp-numeric-addresses", "tcp-ipv6-only-listeners", "tcp-direction-leases", "tcp-failure-poisons-direction", "resolver-host-order-and-buffering", "resolver-may-delay-cancellation", "indexed-authenticated-snapshot", "indexed-caller-frame-and-disk-limit", "indexed-dag-resident", "indexed-writer-preflight", "indexed-budget-not-heap-quota", "sync-host-filesystem-guarantees", "utf8-strict-scalars", "clock-host-monotonic-word64", "sleep-may-run-late", "yield-no-ordering-guarantee", "process-direct-child-only", "process-normal-exit-joins", "process-explicit-pipe-draining"]) + ] + where row key value = ofList [ofString key, value] + strings = ofList . map ofString + +-- This is classification of ONE header, not executable validation, recursive +-- traversal, argument validation, handle lookup, or prediction of success. +-- Payloads, callbacks and continuations are never included in the result. +inspectAction :: T -> Either R.Problem T +inspectAction value = case decodeAction value of + Left detail -> Left (R.Problem "invalid-argument" "inspect" detail "none") + Right (Core code _) -> case find ((== code) . fst) core of + Just (_,label) -> Right (ofList [ofString "core", ofString label]) + Nothing -> error "decodeAction returned unknown core" + Right (Request group revision operation _) -> Right (ofList + [ofString "facility", ofString group, ofNumber revision, ofString operation, + ofString (case resolve group revision operation of Just _ -> "available"; _ -> "unsupported")]) diff --git a/src/IOV2/Resolver.hs b/src/IOV2/Resolver.hs @@ -0,0 +1,58 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Explicit host name resolution, not an implicit part of connection acquisition. +-- getaddrinfo may buffer more results and delay cancellation; only the published +-- endpoint prefix is bounded here. No worker is abandoned on interruption. +module IOV2.Resolver (Family(..), Resolution(..), resolveTCP, resolveWith, hostLimit, addressLimit, validHost) where +import qualified IOV2.Resources as R +import qualified Network.Socket as N +import Control.Exception + +hostLimit, addressLimit :: Int +hostLimit = 253 +addressLimit = 64 +data Family = IPv4Only | IPv6Only | AnyFamily deriving (Eq, Show) +data Resolution = Resolution [R.Endpoint] Bool deriving (Eq, Show) +validHost :: String -> Bool +validHost [] = False +validHost host = walk hostLimit host + where walk _ [] = True + walk 0 _ = False + walk n (c:rest) = ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '-' || c == '.') && walk (n-1) rest + +resolveTCP :: String -> Family -> Integer -> Int -> IO (Either R.Problem Resolution) +resolveTCP host family port limit = resolveWith host port limit $ do + let af = case family of IPv4Only -> N.AF_INET; IPv6Only -> N.AF_INET6; AnyFamily -> N.AF_UNSPEC + hints = N.defaultHints {N.addrFamily=af, N.addrSocketType=N.Stream, + N.addrFlags=[N.AI_NUMERICSERV]} + infos <- N.getAddrInfo (Just hints) (Just host) (Just (show port)) + pure [endpoint | info <- infos, Just endpoint <- [convert (N.addrAddress info)]] + where convert (N.SockAddrInet p h) = Just (R.IPv4 (N.hostAddressToTuple h) (fromIntegral p)) + convert (N.SockAddrInet6 p _ h scope) = Just (R.IPv6 (N.hostAddress6ToTuple h) scope (fromIntegral p)) + convert _ = Nothing + +-- Injected query supports deterministic failure/bound tests without public DNS. +resolveWith :: String -> Integer -> Int -> IO [R.Endpoint] -> IO (Either R.Problem Resolution) +resolveWith host port limit query + | not (validHost host) = pure (Left (R.Problem "invalid-argument" "resolve" "expected bounded ASCII host name" "none")) + | port < 0 || port > 65535 || limit <= 0 || limit > addressLimit = + pure (Left (R.Problem "invalid-bound" "resolve" "" "none")) + | otherwise = (query >>= bounded limit [] >>= pure . Right) `catch` \(e :: IOException) -> + pure (Left (R.Problem "resolution-failed" "resolve" (take 512 (displayException e)) "none")) + where + bounded 0 acc rest = do + truncated <- evaluate (not (null rest)) + finish acc truncated + bounded _ acc [] = finish acc False + bounded n acc (first:rest) = forceEndpoint first `seq` bounded (n-1) (first:acc) rest + finish acc truncated = do + let prefix = reverse acc + -- Neither a lazy split nor endpoint projections may retain the discarded + -- host-result tail/AddrInfo records after returning the bounded reply. + _ <- evaluate (foldr seq () prefix) + pure (Resolution prefix truncated) + +forceEndpoint :: R.Endpoint -> () +forceEndpoint (R.IPv4 (a,b,c,d) p) = a `seq` b `seq` c `seq` d `seq` p `seq` () +forceEndpoint (R.IPv6 (a,b,c,d,e,f,g,h) scope p) = + a `seq` b `seq` c `seq` d `seq` e `seq` f `seq` g `seq` h `seq` scope `seq` p `seq` () diff --git a/src/IOV2/Resources.hs b/src/IOV2/Resources.hs @@ -0,0 +1,612 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Scoped native resources. No Tree interpretation or application policy here. +-- Resource kinds share lifetime/lease rules; task supervision is in IOV2.Tasks. +module IOV2.Resources + ( Runtime, Scope, Token(..), Backend(..), FileOps(..), DuplexOps(..), ListenerOps(..), Endpoint(..), Problem(..), newRuntime, rootScope + , childScope, closeScope, acquire, openReader, openWriter, readSome, writeSome + , withTemporary, syncFile + , withReaderLease, withWriterLease, withInternalResource, closeInternalResource + , closeResource, liveCounts, chunkLimit, runtimeIdentity, isAncestor, combineErrors + , beginCloseScopeSTM, reserveChildSTM + , openAppender, openUpdate, seekFile, filePosition, fileSize, resizeFile, positionLimit + , nextDirectory, acceptConnection, shutdownWrite, socketAddress + , StandardStream(..), newRuntimeWithStreams, standardStream + , sealStage, publishStage, updateDigest, finishDigest, resourceFault, ioProblem + ) where + +import qualified IOV2.Descriptor as Descriptor +import Control.Concurrent.MVar +import Control.Concurrent.STM +import Control.Exception +import Control.Monad (foldM, unless, when) +import qualified Data.ByteString as BS +import qualified Data.Map.Strict as M +import qualified Data.Set as S +import Data.List (foldl') +import Data.IORef +import Data.Bits ((.&.), (.|.), complement) +import Data.Word (Word8, Word16, Word32) +import Text.Read (readMaybe) +import Data.Unique (newUnique, hashUnique) +import System.IO +import System.IO.Error + +-- IDs never recur within a run; the run identity rejects cross-run handles. +type Scope = Integer +data Token = Token !Integer !Integer deriving (Eq, Show) +data Problem = Problem { category :: String, operation :: String, detail :: String + , progress :: String } deriving (Eq, Show) +data Backend = Reader (Int -> IO BS.ByteString) (IO ()) + | Writer (BS.ByteString -> IO Int) (IO ()) + | Staged (BS.ByteString -> IO Int) (IO ()) (Bool -> FilePath -> IO String) (IO ()) + | Digest (BS.ByteString -> IO ()) (IO String) (IO ()) + | Inherited Backend + | File FileOps + | Directory (IO (Maybe String)) (IO ()) + | Duplex DuplexOps + | Listener ListenerOps + | Internal (IO ()) + +data Endpoint = IPv4 (Word8,Word8,Word8,Word8) Word16 + | IPv6 (Word16,Word16,Word16,Word16,Word16,Word16,Word16,Word16) Word32 Word16 + deriving (Eq, Show) +data DuplexOps = DuplexOps + { duplexRead :: Int -> IO BS.ByteString + , duplexWrite :: BS.ByteString -> IO Int + , duplexShutdownWrite :: IO () + , duplexLocal :: IO Endpoint + , duplexPeer :: IO Endpoint + , duplexRelease :: IO () + } +data ListenerOps = ListenerOps + { listenerAccept :: IO Backend + , listenerLocal :: IO Endpoint + , listenerRelease :: IO () + } +-- Bit 0 leases the read direction, bit 1 the write direction. Non-duplex +-- operations always lease both. Close/handoff require BOTH directions idle. +data Access = Exclusive | ReadDirection | WriteDirection +leaseBits :: Access -> Backend -> Word8 +leaseBits ReadDirection (Duplex _) = 1 +leaseBits WriteDirection (Duplex _) = 2 +leaseBits _ _ = 3 +-- File cursor operations and byte IO share ONE registry lease. Optional methods +-- express access mode, not separate handles/cursors or independent lifetimes. +data FileOps = FileOps + { fileReadAction :: Maybe (Int -> IO BS.ByteString) + , fileWriteAction :: Maybe (BS.ByteString -> IO Int) + , fileSeekAction :: Maybe (Integer -> IO ()) + , filePositionAction :: Maybe (IO Integer) + , fileSizeAction :: IO Integer + , fileResizeAction :: Maybe (Integer -> IO ()) + -- Acquire (synchronization action, once-only release); registered before use. + , fileSyncActions :: Maybe (IO (IO (), IO ())) + , fileReleaseAction :: IO () + } +positionLimit :: Integer +positionLimit = 2^(63 :: Int)-1 +-- Backend state errors are ordinary operation failures; unrelated synchronous +-- faults remain terminal task faults, not a catch-all recoverable Result. +newtype ResourceFault = ResourceFault Problem deriving Show +instance Exception ResourceFault +resourceFault :: Problem -> IO a +resourceFault = throwIO . ResourceFault +data Resource = Resource !Scope Backend (TVar Word8) (TVar Bool) (MVar ()) +data ScopeState = ScopeState (Maybe Scope) !Bool !Int +-- Scope Bool is admission, not permission for existing leases to complete. +data State = State !Integer (M.Map Scope ScopeState) (M.Map Integer Resource) +data Runtime = Runtime !Integer (TVar State) + +chunkLimit :: Int +chunkLimit = 4096 +rootScope :: Scope +rootScope = 0 +-- Three fixed root entries exist for the run, including during parent joining. +-- No lazy acquisition into a closing root and no per-lookup aliases/history. +-- These are borrowed process handles: no mode/buffering changes or OS close. +data StandardStream = StandardInput | StandardOutput | StandardError deriving (Enum) +newRuntime :: IO Runtime +newRuntime = newRuntimeWithStreams stdin stdout stderr +newRuntimeWithStreams :: Handle -> Handle -> Handle -> IO Runtime +newRuntimeWithStreams input output diagnostic = do + identity <- toInteger . hashUnique <$> newUnique + let writer h = do + failed <- newIORef False + pure $ Writer (\bytes -> mask $ \restore -> do + poisoned <- readIORef failed + when poisoned (resourceFault (Problem "invalid-state" "write" + "inherited writer failed; progress is uncertain" "unknown")) + -- A failed flush can leave host-buffered bytes. Never implicitly retry + -- them with the next v2 write. The embedding still owns that buffer. + restore (BS.hPut h bytes >> hFlush h) `onException` writeIORef failed True + pure (BS.length bytes)) (pure ()) + out <- writer output + err <- writer diagnostic + let backends = [Reader (BS.hGetSome input) (pure ()), out, err] + entries <- mapM (\backend -> Resource rootScope (Inherited backend) + <$> newTVarIO 0 <*> newTVarIO False <*> newMVar ()) backends + Runtime identity <$> newTVarIO (State 4 (M.singleton 0 (ScopeState Nothing True 0)) + (M.fromList (zip [1..3] entries))) + +standardStream :: Runtime -> Scope -> StandardStream -> IO (Either Problem Token) +standardStream (Runtime identity state) current stream = atomically $ do + snapshot <- readTVar state + let token = Token identity (1 + toInteger (fromEnum stream)) + -- Resolve validation here, not in a lazy result retaining the registry snapshot. + case lookupResource identity current token snapshot of + Left fault -> pure (Left fault) + Right _ -> token `seq` pure (Right token) + +internal :: Backend -> Bool +internal (Internal _) = True +internal _ = False + +inherited :: Backend -> Bool +inherited (Inherited _) = True +inherited _ = False +usable :: Backend -> Backend +usable (Inherited backend) = backend +usable backend = backend + +problem :: String -> String -> Problem +problem cat op = Problem cat op "" "none" +ioProblem :: String -> IOException -> Problem +ioProblem op e = Problem cat op (take 512 (show e)) (if op `elem` ["write", "read", "seek", "resize", "next", "close", "sync"] then "unknown" else "none") + where cat | isDoesNotExistError e = "not-found" + | isPermissionError e = "os-permission-denied" + | isAlreadyExistsError e = "already-exists" + | otherwise = "io-error" + +-- Scheduler transactions can close admission before joining children, without +-- revoking their ancestor borrowing or closing their own open scopes. Resource +-- release is a separate step, performed only after task settlement. +beginCloseScopeSTM :: Runtime -> Scope -> STM () +beginCloseScopeSTM (Runtime _ state) scope = modifyTVar' state $ \(State n scopes resources) -> + State n (M.adjust (\(ScopeState p _ count) -> ScopeState p False count) scope scopes) resources + +childScope :: Runtime -> Scope -> IO (Either Problem Scope) +childScope runtime parent = atomically $ fmap (fmap fst) (reserveChildSTM runtime parent []) + +-- Low-level reservation, NOT a public spawn operation. A scheduler must combine +-- this transaction with task registration and opening a prestarted worker's gate. +-- Thread creation must precede commitment: a failed fork must not move resources. +-- STM rollback then covers the resource registry AND task publication together. +-- +-- Only resources owned by the attaching parent move; borrowed ancestor resources +-- stay borrowed. Every token is checked before any mutation. Rekeying invalidates +-- all old aliases, with no tombstones, and preserves original acquisition order +-- independently of the caller's handle-list order. Returned tokens follow input +-- order. No pending lease or explicit close may cross the handoff boundary. +reserveChildSTM :: Runtime -> Scope -> [Token] -> STM (Either Problem (Scope, [Token])) +reserveChildSTM (Runtime identity state) parent tokens = do + snapshot@(State next scopes resources) <- readTVar state + case M.lookup parent scopes of + Just (ScopeState _ True _) -> + if S.size (S.fromList [(run, key) | Token run key <- tokens]) /= length tokens + then pure (Left (problem "duplicate-handle" "transfer")) + else do + checked <- mapM (validate snapshot) tokens + case sequence checked of + Left e -> pure (Left e) + Right entries -> do + let ordered = M.toAscList (M.fromList entries) + moved = zip ordered [next+1..] + replacements = M.fromList [(old, new) | ((old, _), new) <- moved] + removeOld = foldr (M.delete . fst) resources entries + insertMoved acc ((_, Resource _ backend busy closing gate), key) = + M.insert key (Resource next backend busy closing gate) acc + finalResources = foldl' insertMoved removeOld moved + fresh (Token _ old) = Token identity (replacements M.! old) + freshTokens = map fresh tokens + -- Do not let lazy output tokens retain the moved backend records + -- through the replacement-map construction after the scope closes. + foldr (\token rest -> token `seq` rest) () freshTokens `seq` pure () + writeTVar state (State (next+1+toInteger (length entries)) + (M.insert next (ScopeState (Just parent) True 0) scopes) finalResources) + pure (Right (next, freshTokens)) + _ -> pure (Left (problem "closed-scope" "scope")) + where + validate snapshot token@(Token _ key) = case lookupResource identity parent token snapshot of + Left e -> pure (Left e) + Right resource@(Resource owner backend busy closing _) -> do + occupied <- readTVar busy + dead <- readTVar closing + pure $ if owner /= parent then Left (problem "not-owner" "transfer") + else if inherited backend then Left (problem "borrowed-resource" "transfer") + else if internal backend then Left (problem "internal-resource" "transfer") + else if dead then Left (problem "closed-handle" "transfer") + else if occupied /= 0 then Left (problem "busy" "transfer") + else Right (key, resource) + +release :: Backend -> IO () +release (Internal done) = done +release (Duplex methods) = duplexRelease methods +release (Listener methods) = listenerRelease methods +release (Directory _ done) = done +release (File methods) = fileReleaseAction methods +release (Inherited _) = pure () +release (Reader _ done) = done +release (Writer _ done) = done +release (Staged _ _ _ done) = done +release (Digest _ _ done) = done + +-- Acquisition is masked through registry publication, like bracket acquisition. +-- Backend acquisition must be exception-safe internally. Admission remains counted +-- until publication/failure, so scope closure cannot miss an in-flight acquisition. +acquire :: Runtime -> Scope -> IO Backend -> IO (Either Problem Token) +acquire (Runtime identity state) owner create = mask_ $ do + admitted <- atomically $ do + State next scopes resources <- readTVar state + case M.lookup owner scopes of + Just (ScopeState parent True pending) -> do + writeTVar state (State next (M.insert owner (ScopeState parent True (pending+1)) scopes) resources) + pure True + _ -> pure False + if not admitted then pure (Left (problem "closed-scope" "open")) else do + let leave = atomically $ modifyTVar' state $ \(State n scopes resources) -> + State n (M.adjust (\(ScopeState p a count) -> ScopeState p a (count-1)) owner scopes) resources + result <- try (bracketOnError create release $ \backend -> do + busy <- newTVarIO 0 + closing <- newTVarIO False + gate <- newMVar () + atomically $ do + State next scopes resources <- readTVar state + writeTVar state (State (next+1) scopes (M.insert next (Resource owner backend busy closing gate) resources)) + pure (Token identity next)) `finally` leave + case result of + Right value -> pure (Right value) + Left (exception :: SomeException) + | Just (ResourceFault fault) <- fromException exception -> pure (Left fault) + | Just e <- fromException exception -> pure (Left (ioProblem "open" e)) + | otherwise -> throwIO exception + +openReader, openWriter, openAppender, openUpdate :: Runtime -> Scope -> FilePath -> IO (Either Problem Token) +openReader = openFileResource ReadMode +openWriter = openFileResource WriteMode +openAppender = openFileResource AppendMode +openUpdate = openFileResource ReadWriteMode + +openFileResource :: IOMode -> Runtime -> Scope -> FilePath -> IO (Either Problem Token) +openFileResource mode runtime scope path = acquire runtime scope $ + bracketOnError (openBinaryFile path mode) hClose $ \h -> do + hSetBuffering h NoBuffering + let readable = mode == ReadMode || mode == ReadWriteMode + writable = mode /= ReadMode + positioned = mode /= AppendMode + optional allowed action = if allowed then Just action else Nothing + seekable action = do + supported <- hIsSeekable h + unless supported (resourceFault (Problem "unsupported" "file" "file is not seekable" "none")) + action + resize size = seekable $ do + -- Synchronize/discard any read-ahead before changing length. Resizing + -- preserves the logical cursor, including a position beyond new EOF. + position <- hTell h + hSeek h AbsoluteSeek position + hSetFileSize h size + pure (File (FileOps + (optional readable (BS.hGetSome h)) + (optional writable (\bytes -> BS.hPut h bytes >> pure (BS.length bytes))) + (optional positioned (\position -> seekable (hSeek h AbsoluteSeek position))) + (optional positioned (seekable (hTell h))) + (hFileSize h) + (optional (writable && positioned) resize) + (Just $ do + -- Synchronize any read-ahead while preserving the logical cursor. Append + -- needs only flushing; the duplicate is used for fsync, never seeking. + when readable (seekable (hTell h >>= hSeek h AbsoluteSeek)) + when writable (hFlush h) + Descriptor.duplicateSync h) + (hClose h))) + +-- Validating bounds precedes backend IO. Query replies are checked too, so a +-- backend cannot inject a negative/out-of-range natural into the tree protocol. +fileOperation :: Runtime -> Scope -> Token -> String -> (FileOps -> Maybe (IO a)) -> IO (Either Problem a) +fileOperation runtime scope token op select = withLease runtime scope token op isFile $ \backend -> case backend of + File methods -> case select methods of + Just action -> action + Nothing -> resourceFault (Problem "unsupported" op "operation unavailable in this file mode" "none") + _ -> error "validated file kind" + where isFile (File _) = True + isFile _ = False + +syncFile :: Runtime -> Scope -> Token -> IO (Either Problem ()) +syncFile runtime scope token = fileOperation runtime scope token "sync" $ \methods -> + fmap (\allocate -> withTemporary runtime scope "sync" allocate id >>= either resourceFault pure) + (fileSyncActions methods) + +seekFile, resizeFile :: Runtime -> Scope -> Token -> Integer -> IO (Either Problem ()) +seekFile runtime scope token position + | position < 0 || position > positionLimit = pure (Left (problem "invalid-bound" "seek")) + | otherwise = fileOperation runtime scope token "seek" (fmap ($ position) . fileSeekAction) +resizeFile runtime scope token size + | size < 0 || size > positionLimit = pure (Left (problem "invalid-bound" "resize")) + | otherwise = fileOperation runtime scope token "resize" (fmap ($ size) . fileResizeAction) + +filePosition, fileSize :: Runtime -> Scope -> Token -> IO (Either Problem Integer) +filePosition runtime scope token = fileOperation runtime scope token "position" + (fmap checkedPosition . filePositionAction) +fileSize runtime scope token = fileOperation runtime scope token "size" + (Just . checkedPosition . fileSizeAction) +checkedPosition :: IO Integer -> IO Integer +checkedPosition action = do + value <- action + unless (value >= 0 && value <= positionLimit) + (resourceFault (Problem "unsupported" "file" "position outside supported range" "none")) + pure value + +inside :: Scope -> Scope -> M.Map Scope ScopeState -> Bool +inside current owner scopes = case M.lookup current scopes of + Nothing -> False + Just (ScopeState parent _ _) -> current == owner || maybe False (\p -> inside p owner scopes) parent + +lookupResource :: Integer -> Scope -> Token -> State -> Either Problem Resource +lookupResource identity current (Token run key) (State _ scopes resources) + | run /= identity = Left (problem "stale-handle" "resource") + | otherwise = case M.lookup key resources of + Nothing -> Left (problem "stale-handle" "resource") + Just resource@(Resource owner _ _ _ _) + | inside current owner scopes -> Right resource + | otherwise -> Left (problem "out-of-scope" "resource") + +withLease :: Runtime -> Scope -> Token -> String -> (Backend -> Bool) -> (Backend -> IO a) -> IO (Either Problem a) +withLease = withAccess Exclusive + +-- Host-owned temporary resources are registered for lexical cleanup but are not +-- public handles. Even a guessed registry key cannot read, close or transfer one. +-- Their active operation still has a lease, so scope release waits for it. +withInternalResource :: Runtime -> Scope -> Token -> String -> IO a -> IO (Either Problem a) +withInternalResource runtime scope token op action = withLease runtime scope token op internal (const action) + +closeInternalResource :: Runtime -> Scope -> Token -> IO (Either Problem ()) +closeInternalResource runtime@(Runtime identity state) scope token = do + found <- atomically (lookupResource identity scope token <$> readTVar state) + case found of + Left p -> pure (Left p) + Right (Resource _ backend _ _ _) | internal backend -> closeResourceWith True runtime scope token + _ -> pure (Left (problem "wrong-kind" "close")) + +-- Register transient host-owned resources before use. Ordinary failures retain +-- primary diagnostics plus bounded cleanup detail; terminal faults leave cleanup +-- to lexical settlement. Allocation must itself be exception-safe until return. +withTemporary :: Runtime -> Scope -> String -> IO (a, IO ()) -> (a -> IO b) -> IO (Either Problem b) +withTemporary runtime scope op allocate use = mask $ \restore -> do + slot <- newIORef Nothing + acquired <- acquire runtime scope $ do + (value, done) <- allocate + writeIORef slot (Just value) + pure (Internal done) + case acquired of + Left p -> pure (Left p) + Right token -> do + Just value <- readIORef slot + result <- restore (withInternalResource runtime scope token op (use value)) + closed <- closeInternalResource runtime scope token + pure $ case (result,closed) of + (Right value, Right ()) -> Right value + (Right _, Left cleanup) -> Left cleanup + (Left primary, Right ()) -> Left primary + (Left primary, Left cleanup) -> Left primary { detail = take 240 (detail primary) ++ + "; cleanup: " ++ take 240 (category cleanup ++ ": " ++ detail cleanup) } + +withAccess :: Access -> Runtime -> Scope -> Token -> String -> (Backend -> Bool) -> (Backend -> IO a) -> IO (Either Problem a) +withAccess access (Runtime identity state) current token op rightKind use = mask $ \restore -> do + lease <- atomically $ do + snapshot <- readTVar state + case lookupResource identity current token snapshot of + Left e -> pure (Left e) + Right resource@(Resource _ backend busy closing _) -> do + dead <- readTVar closing + occupied <- readTVar busy + if dead then pure (Left (problem "closed-handle" op)) + else if not (rightKind (usable backend)) then pure (Left (problem "wrong-kind" op)) + else if occupied .&. leaseBits access (usable backend) /= 0 then pure (Left (problem "busy" op)) + else writeTVar busy (occupied .|. leaseBits access (usable backend)) >> pure (Right resource) + case lease of + Left e -> pure (Left e) + Right (Resource _ backend busy _ _) -> do + result <- try (restore (use (usable backend))) `finally` atomically (modifyTVar' busy (.&. complement (leaseBits access (usable backend)))) + case result of + Right value -> pure (Right value) + Left (exception :: SomeException) + | Just (ResourceFault p) <- fromException exception -> pure (Left p) + | Just e <- fromException exception -> pure (Left (ioProblem op e)) + | otherwise -> throwIO exception + +readSome :: Runtime -> Scope -> Token -> Int -> IO (Either Problem BS.ByteString) +readSome runtime scope token count + | count <= 0 || count > chunkLimit = pure (Left (problem "invalid-bound" "read")) + | otherwise = withReaderLease runtime scope token (\readBytes -> readBytes count) + +-- Internal codec transport: retain one directional lease across an entire frame, +-- not one lease per chunk. Internal clients must not retain the supplied reader +-- beyond the callback; no such function is exposed through the source protocol. +withReaderLease :: Runtime -> Scope -> Token -> ((Int -> IO BS.ByteString) -> IO a) -> IO (Either Problem a) +withReaderLease runtime scope token use = withAccess ReadDirection runtime scope token "read" isReader $ \backend -> + let checked readBytes count = do + unless (count > 0 && count <= chunkLimit) (resourceFault (problem "invalid-bound" "read")) + bytes <- readBytes count + unless (BS.length bytes <= count) (ioError (userError "backend exceeded read bound")) + pure bytes + in case backend of + Duplex methods -> use (checked (duplexRead methods)) + Reader readBytes _ -> use (checked readBytes) + File methods | Just readBytes <- fileReadAction methods -> use (checked readBytes) + _ -> error "validated reader kind" + +writeSome :: Runtime -> Scope -> Token -> BS.ByteString -> IO (Either Problem Int) +writeSome runtime scope token bytes + | BS.null bytes || BS.length bytes > chunkLimit = pure (Left (problem "invalid-bound" "write")) + | otherwise = withWriterLease runtime scope token (\writeBytes -> writeBytes bytes) + +-- As for the reader hook, internal clients must not retain the supplied writer. +withWriterLease :: Runtime -> Scope -> Token -> ((BS.ByteString -> IO Int) -> IO a) -> IO (Either Problem a) +withWriterLease runtime scope token use = withAccess WriteDirection runtime scope token "write" isWriter $ \backend -> + let checked writeBytes bytes = do + unless (not (BS.null bytes) && BS.length bytes <= chunkLimit) + (resourceFault (problem "invalid-bound" "write")) + count <- writeBytes bytes + unless (count > 0 && count <= BS.length bytes) (ioError (userError "invalid backend write progress")) + pure count + in case backend of + Duplex methods -> use (checked (duplexWrite methods)) + File methods | Just writeBytes <- fileWriteAction methods -> use (checked writeBytes) + Writer writeBytes _ -> use (checked writeBytes) + Staged writeBytes _ _ _ -> use (checked writeBytes) + _ -> error "validated writer kind" + +isReader, isWriter, isStage, isDigest :: Backend -> Bool +isReader (Duplex _) = True +isReader (File methods) = maybe False (const True) (fileReadAction methods) +isReader (Reader _ _) = True +isReader _ = False +isWriter (Duplex _) = True +isWriter (File methods) = maybe False (const True) (fileWriteAction methods) +isWriter (Writer _ _) = True +isWriter (Staged _ _ _ _) = True +isWriter _ = False +isStage (Staged _ _ _ _) = True +isStage _ = False +isDigest (Digest _ _ _) = True +isDigest _ = False + +acceptConnection :: Runtime -> Scope -> Token -> Scope -> IO (Either Problem Token) +acceptConnection runtime current token owner = do + allowed <- isAncestor runtime current owner + if not allowed then pure (Left (problem "out-of-scope" "accept")) else do + result <- withLease runtime current token "accept" isListener $ \backend -> case backend of + Listener methods -> acquire runtime owner (listenerAccept methods) + _ -> error "validated listener kind" + pure (either Left id result) + where isListener (Listener _) = True + isListener _ = False + +shutdownWrite :: Runtime -> Scope -> Token -> IO (Either Problem ()) +shutdownWrite runtime current token = withAccess WriteDirection runtime current token "shutdown-write" isDuplex $ \backend -> case backend of + Duplex methods -> duplexShutdownWrite methods + _ -> error "validated duplex kind" + where isDuplex (Duplex _) = True + isDuplex _ = False + +socketAddress :: Runtime -> Scope -> Token -> Bool -> IO (Either Problem Endpoint) +socketAddress runtime current token peer = withLease runtime current token op supported $ \backend -> case backend of + Duplex methods -> (if peer then duplexPeer else duplexLocal) methods + Listener methods -> listenerLocal methods + _ -> error "validated socket kind" + where op = if peer then "peer-address" else "local-address" + supported (Duplex _) = True + supported (Listener _) = not peer + supported _ = False + +nextDirectory :: Runtime -> Scope -> Token -> IO (Either Problem (Maybe String)) +nextDirectory runtime scope token = withLease runtime scope token "next" isDirectory $ \backend -> case backend of + Directory next _ -> next + _ -> error "validated directory kind" + where isDirectory (Directory _ _) = True + isDirectory _ = False + +sealStage :: Runtime -> Scope -> Token -> IO (Either Problem ()) +sealStage runtime scope token = withLease runtime scope token "seal" isStage $ \backend -> case backend of + Staged _ seal _ _ -> seal + _ -> error "validated stage kind" +publishStage :: Runtime -> Scope -> Token -> Bool -> FilePath -> IO (Either Problem String) +publishStage runtime scope token replace destination = withLease runtime scope token "publish" isStage $ \backend -> case backend of + Staged _ _ publish _ -> publish replace destination + _ -> error "validated stage kind" +updateDigest :: Runtime -> Scope -> Token -> BS.ByteString -> IO (Either Problem ()) +updateDigest runtime scope token bytes + | BS.null bytes || BS.length bytes > chunkLimit = pure (Left (problem "invalid-bound" "digest-update")) + | otherwise = withLease runtime scope token "digest-update" isDigest $ \backend -> case backend of + Digest update _ _ -> update bytes + _ -> error "validated digest kind" +finishDigest :: Runtime -> Scope -> Token -> IO (Either Problem String) +finishDigest runtime scope token = withLease runtime scope token "digest-finish" isDigest $ \backend -> case backend of + Digest _ finish _ -> finish + _ -> error "validated digest kind" + +-- The close gate serializes explicit close and teardown without holding the +-- registry lock during IO. Mark closing before waiting for leases. If interrupted +-- while waiting, a subsequent scope teardown can finish the close. +closeResource :: Runtime -> Scope -> Token -> IO (Either Problem ()) +closeResource = closeResourceWith False + +-- Only lexical settlement may revoke inherited references, never the OS handles. +closeResourceWith :: Bool -> Runtime -> Scope -> Token -> IO (Either Problem ()) +closeResourceWith settling runtime@(Runtime identity state) current token = mask_ $ do + found <- atomically $ lookupResource identity current token <$> readTVar state + case found of + Left e -> pure (Left e) + Right (Resource _ backend _ _ _) | internal backend && not settling -> + pure (Left (problem "internal-resource" "close")) + Right (Resource _ backend _ _ _) | inherited backend && not settling -> + pure (Left (problem "borrowed-resource" "close")) + Right (Resource _ _ _ _ gate) -> withMVar gate $ \_ -> do + -- Claim closing in the SAME transaction as token validation. Otherwise a + -- transfer can rekey the entry between lookup and closeEntry, allowing an + -- old alias to close the new owner's backend and leave a dead live entry. + fresh <- atomically $ do + result <- lookupResource identity current token <$> readTVar state + case result of + Right (Resource _ _ _ closing _) -> writeTVar closing True + Left _ -> pure () + pure result + case fresh of + Left e -> pure (Left e) + Right resource -> closeEntry runtime token resource + +closeEntry :: Runtime -> Token -> Resource -> IO (Either Problem ()) +closeEntry (Runtime _ state) (Token _ key) (Resource _ backend busy closing _) = do + atomically (writeTVar closing True) + atomically (readTVar busy >>= check . (== 0)) + result <- try (release backend) + case result of + Left (exception :: SomeException) | Just (_ :: SomeAsyncException) <- fromException exception -> throwIO exception + _ -> do + atomically $ modifyTVar' state $ \(State n scopes resources) -> State n scopes (M.delete key resources) + pure $ case result of + Right () -> Right () + Left exception -> Left $ case fromException exception of + Just e -> ioProblem "close" e + Nothing -> Problem "backend-fault" "close" (take 512 (displayException exception)) "unknown" + +-- IOV2.Tasks must settle children before entering resource teardown. Close any +-- remaining nested resource scopes before this scope, in reverse acquisition order. +closeScope :: Runtime -> Scope -> IO [Problem] +closeScope runtime@(Runtime identity state) scope = mask_ $ do + atomically (beginCloseScopeSTM runtime scope) + children <- atomically $ do + State _ scopes _ <- readTVar state + case M.lookup scope scopes of + Just (ScopeState _ _ pending) -> check (pending == 0) + Nothing -> pure () + pure [key | (key, ScopeState parent _ _) <- M.toDescList scopes, parent == Just scope] + childErrors <- foldM (\errors child -> combineErrors errors <$> closeScope runtime child) [] children + keys <- atomically $ do + State _ _ resources <- readTVar state + pure [key | (key, Resource owner _ _ _ _) <- M.toDescList resources, owner == scope] + errors <- foldM (\previous key -> do + result <- closeResourceWith True runtime scope (Token identity key) + pure (combineErrors previous (either (:[]) (const []) result))) childErrors keys + atomically $ modifyTVar' state $ \(State n scopes resources) -> State n (M.delete scope scopes) resources + pure errors + +-- Keep bounded diagnostics while preserving an explicit count of omissions. +combineErrors :: [Problem] -> [Problem] -> [Problem] +combineErrors left right = + let allErrors = left ++ right + actual = filter ((/= "cleanup-omitted") . category) allErrors + prior = sum [maybe 0 id (readMaybe (detail e) :: Maybe Integer) | e <- allErrors, category e == "cleanup-omitted"] + omitted = prior + toInteger (max 0 (length actual - 16)) + in take 16 actual ++ [Problem "cleanup-omitted" "close" (show omitted) "none" | omitted > 0] + +liveCounts :: Runtime -> IO (Int, Int) +liveCounts (Runtime _ state) = atomically $ do + State _ scopes resources <- readTVar state + pure (M.size scopes, M.size resources) + +runtimeIdentity :: Runtime -> IO Integer +runtimeIdentity (Runtime identity _) = pure identity +isAncestor :: Runtime -> Scope -> Scope -> IO Bool +isAncestor (Runtime _ state) current owner = atomically $ do + State _ scopes _ <- readTVar state + pure (inside current owner scopes) diff --git a/src/IOV2/Staging.hs b/src/IOV2/Staging.hs @@ -0,0 +1,149 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Same-filesystem staged publication, including cross-directory destinations. No digest, length, HTTP or artifact policy. +module IOV2.Staging (openStage, openStageWith, StageOps(..), nativeOps) where + +import qualified IOV2.Resources as R +import qualified Data.ByteString as BS +import Control.Exception +import Control.Monad (unless, when) +import Data.List (isInfixOf) +import Data.IORef +import System.Directory (canonicalizePath, removeFile, renameFile) +import System.FilePath (takeDirectory, takeFileName, (</>)) +import System.IO +import System.IO.Error +import System.Posix.Files (createLink) +import System.Posix.IO (openFd, closeFd, handleToFd, defaultFileFlags, OpenMode(ReadOnly)) +import System.Posix.Types (Fd) +import System.Posix.Unistd (fileSynchronise) + +-- Injectable OS dependencies, not production fault flags. The native backend +-- has exactly the same transitions as deterministic failure/cancellation tests. +data StageOps = StageOps + { putBytes :: Handle -> BS.ByteString -> IO () + , syncFile :: Fd -> IO () + , closeDescriptor :: Fd -> IO () + , replaceName :: FilePath -> FilePath -> IO () + , linkName :: FilePath -> FilePath -> IO () + , removeName :: FilePath -> IO () + , openDirectory :: FilePath -> IO Fd + , syncDirectory :: Fd -> IO () + } +nativeOps :: StageOps +nativeOps = StageOps BS.hPut fileSynchronise closeFd renameFile createLink removeFile + (\path -> openFd path ReadOnly defaultFileFlags) fileSynchronise + +temporaryMarker :: String +temporaryMarker = ".tricu-stage-" + +data Phase = Writing | Sealed | Failed | Published deriving Eq +data Owned = OwnHandle Handle | OwnDescriptor Fd | Released +newtype CleanupFault = CleanupFault String deriving Show +instance Exception CleanupFault + +openStage :: R.Runtime -> R.Scope -> FilePath -> IO (Either R.Problem R.Token) +openStage = openStageWith nativeOps +openStageWith :: StageOps -> R.Runtime -> R.Scope -> FilePath -> IO (Either R.Problem R.Token) +openStageWith _ _ _ "" = pure (Left (R.Problem "invalid-argument" "stage-open" "empty directory" "none")) +openStageWith ops runtime scope directory = R.acquire runtime scope $ mask_ $ do + canonical <- canonicalizePath directory + (path, handle) <- openBinaryTempFile canonical temporaryMarker + -- No user evaluation between allocation and installing cleanup bookkeeping. + phase <- newIORef Writing + owned <- newIORef (OwnHandle handle) + privateName <- newIORef True + let removePrivate = do + exists <- readIORef privateName + when exists $ do + removeName ops path `catch` (\e -> unless (isDoesNotExistError e) (throwIO (e :: IOException))) + writeIORef privateName False + closeOwned = do + current <- readIORef owned + case current of + Released -> pure () + OwnHandle h -> hClose h >> writeIORef owned Released + OwnDescriptor fd -> do + -- A failed close can already have closed the descriptor. Never + -- retry a numeric descriptor that the OS may have reused. + writeIORef owned Released + closeDescriptor ops fd + cleanup = mask_ $ do + closed <- try closeOwned + removed <- try removePrivate + let failures = [e | Left e <- [closed, removed]] :: [SomeException] + asynchronous e = case fromException e :: Maybe SomeAsyncException of + Just _ -> True + Nothing -> False + -- Preserve interruption so the registry retains the closing entry for + -- later settlement, but still attempt private-name removal here. + case filter asynchronous failures of + first:_ -> throwIO first + [] -> case (closed, removed) of + (Left first, Left second) -> case fromException first of + Just ioe -> throwIO (ioeSetErrorString ioe + (take 240 (ioeGetErrorString ioe) ++ "; removal: " ++ take 240 (displayException second))) + Nothing -> throwIO (CleanupFault + (take 240 (displayException first) ++ "; removal: " ++ take 240 (displayException second))) + (Left first, _) -> throwIO first + (_, Left second) -> throwIO second + _ -> pure () + require expected operation = do + actual <- readIORef phase + unless (actual == expected) (R.resourceFault (R.Problem "invalid-state" operation "stage is not in the required phase" "none")) + poison = writeIORef phase Failed + write bytes = do + require Writing "write" + (putBytes ops handle bytes >> pure (BS.length bytes)) `onException` poison + seal = mask_ $ do + require Writing "seal" + (do + hFlush handle + fd <- handleToFd handle + writeIORef owned (OwnDescriptor fd) + -- On sync failure/cancellation the scope still owns this descriptor. + -- Its later release cannot mask the primary operation failure. + syncFile ops fd + closeOwned + writeIORef phase Sealed) `onException` poison + publish replace destination = mask_ $ do + require Sealed "publish" + progress <- newIORef "not-published" + let action = do + targetDirectory <- canonicalizePath (takeDirectory destination) + let name = takeFileName destination + unless (name `notElem` ["", ".", ".."] && not (temporaryMarker `isInfixOf` name)) $ + R.resourceFault (R.Problem "invalid-destination" "publish" "destination must name a file outside the private staging namespace" "not-published") + let target = targetDirectory </> name + -- Syscall failure can be ambiguous on remote filesystems. Never + -- turn it into permission to retry or delete the destination. + writeIORef progress "unknown" + linked <- if replace then replaceName ops path target >> pure True + else (linkName ops path target >> pure True) `catch` + (\e -> if isAlreadyExistsError e then pure False else throwIO (e :: IOException)) + if not linked then pure "exists" else do + writeIORef progress "published" + writeIORef phase Published + if replace then writeIORef privateName False else removePrivate + -- These are atomic namespace syscalls, never copy fallbacks. + -- Synchronize destination visibility before source-name removal durability; + -- distinct directories both need synchronization. Failure remains + -- published and cleanup cannot remove the destination. Do not open + -- a second descriptor until the first has been settled. + let syncParent directoryPath = do + fd <- openDirectory ops directoryPath + writeIORef owned (OwnDescriptor fd) + syncDirectory ops fd + closeOwned + syncParent targetDirectory + when (targetDirectory /= canonical) (syncParent canonical) + pure "published" + action `catch` (\(e :: IOException) -> do + poison + visibility <- readIORef progress + R.resourceFault ((R.ioProblem "publish" e) { R.progress = visibility })) + `onException` (do + state <- readIORef phase + -- In particular, cancellation after link/rename never rolls back + -- visibility. Cleanup only ever touches our private temporary name. + unless (state == Published) poison) + (hSetBuffering handle NoBuffering >> pure (R.Staged write seal publish cleanup)) `onException` cleanup diff --git a/src/IOV2/Tasks.hs b/src/IOV2/Tasks.hs @@ -0,0 +1,323 @@ +{-# LANGUAGE ScopedTypeVariables #-} +-- Structured native task groups. Registry transactions are the linearization +-- points; ThreadKilled only wakes a body, never carries the cancellation reason. +module IOV2.Tasks + ( Outcome(..), Scheduler, Group, TaskRef(..), newScheduler, rootGroup + , scopeOf, runGroup, nested, spawn, join, discard, cancel, checkpoint, liveCounts + ) where + +import Control.Concurrent hiding (yield) +import Control.Concurrent.STM +import Control.Exception +import Control.Monad (forM_, void) +import qualified Data.Map.Strict as M +import Runtime (T(..), ofString, ofList) +import qualified IOV2.Resources as R + +data Outcome = Completed T | Failed T [R.Problem] | Cancelled T [R.Problem] deriving Show +data Phase = Open | Joining | Releasing | Closed deriving Eq +data Scheduler = Scheduler R.Runtime (TVar (M.Map Integer Task)) + (TVar (M.Map Integer Task)) (TVar (M.Map Integer [Integer])) +data Group = Group + { groupScheduler :: Scheduler + , scopeOf :: R.Scope + , taskIdentity :: Integer + , groupParent :: Maybe (Group, Bool) + , groupPhase :: TVar Phase + , groupReason :: TVar (Maybe Outcome) + , groupChildren :: TVar (M.Map Integer Task) + , groupCleanup :: TVar [R.Problem] + , groupLexical :: TVar (Maybe Group) + } +data Task = Task Group Group (TVar (Maybe Outcome)) +data TaskRef = TaskRef !Integer !Integer deriving (Eq, Show) +data Interrupted = Interrupted deriving Show +instance Exception Interrupted + +schedulerOf :: Group -> Scheduler +schedulerOf = groupScheduler +runtimeOf :: Scheduler -> R.Runtime +runtimeOf (Scheduler runtime _ _ _) = runtime + +newScheduler :: R.Runtime -> IO Scheduler +newScheduler runtime = Scheduler runtime <$> newTVarIO M.empty <*> newTVarIO M.empty <*> newTVarIO M.empty +newGroup :: Scheduler -> R.Scope -> Integer -> Maybe (Group, Bool) -> STM Group +newGroup scheduler scope task parent = Group scheduler scope task parent + <$> newTVar Open <*> newTVar Nothing <*> newTVar M.empty <*> newTVar [] <*> newTVar Nothing +rootGroup :: Scheduler -> IO Group +rootGroup scheduler = atomically (newGroup scheduler R.rootScope 0 Nothing) + +problem :: String -> String -> R.Problem +problem category operation = R.Problem category operation "" "none" +failedException :: SomeException -> Outcome +failedException e = Failed (ofString (take 512 (displayException e))) [] + +-- First recorded stop wins. Failure propagates across lexical groups and +-- uncontained child boundaries, but never across preinstalled supervision. +-- All siblings receive sticky requests in this same transaction; a slow OS +-- interruption of one worker cannot prevent cancellation of the others. +stop :: Group -> Outcome -> STM () +stop Group{ groupScheduler = scheduler, scopeOf = scope, groupParent = parent, + groupPhase = phase, groupReason = reason, groupChildren = children, groupLexical = lexical } outcome = do + state <- readTVar phase + previous <- readTVar reason + case (state, previous) of + (Closed, _) -> pure () + (_, Just _) -> pure () + _ -> do + writeTVar reason (Just (withoutCleanup outcome)) + R.beginCloseScopeSTM (runtimeOf scheduler) scope + inner <- readTVar lexical + forM_ inner (\child -> stop child (Cancelled (ofString "owning group stopped") [])) + active <- readTVar children + forM_ (M.elems active) $ \(Task _ child _) -> + stop child (Cancelled (ofString "owning group stopped") []) + case (outcome, parent) of + (Failed _ _, Just (outer, True)) -> stop outer (withoutCleanup outcome) + _ -> pure () + +checkpoint :: Group -> IO () +checkpoint Group{ groupReason = reason } = atomically $ do + stopped <- readTVar reason + case stopped of Nothing -> pure (); Just _ -> throwSTM Interrupted + +-- Propagation carries only the primary. Cleanup travels once, at settlement, +-- rather than being duplicated along early fail-fast notifications. +withoutCleanup :: Outcome -> Outcome +withoutCleanup (Failed diagnostic _) = Failed diagnostic [] +withoutCleanup (Cancelled reason _) = Cancelled reason [] +withoutCleanup completed = completed +cleanupOf :: Outcome -> [R.Problem] +cleanupOf (Failed _ errors) = errors +cleanupOf (Cancelled _ errors) = errors +cleanupOf _ = [] + +attach :: Outcome -> [R.Problem] -> Outcome +attach (Failed diagnostic previous) errors = Failed diagnostic (R.combineErrors previous errors) +attach (Cancelled reason previous) errors = Cancelled reason (R.combineErrors previous errors) +attach completed [] = completed +attach (Completed _) (first:rest) = Failed (ofList (map ofString [R.category first, R.operation first, R.detail first, R.progress first])) rest + +-- The controller never executes user code. Its body runs in a separate worker, +-- so cancellation cannot land inside the resource-release protocol. Lexical +-- nesting may interrupt a controller from outside; the exception boundary then +-- cancels/joins its entire subtree before rethrowing that host interruption. +-- Repeated external host interruption/process death is not a cleanup guarantee. +runGroup :: Group -> IO Outcome -> IO Outcome +runGroup group@Group{ groupScheduler = scheduler, scopeOf = scope, taskIdentity = taskId, + groupPhase = phase, groupReason = reason, groupChildren = children, + groupCleanup = childErrors, groupLexical = lexical } body = mask $ \restore -> do + bodyDone <- newEmptyTMVarIO + launched <- try $ forkIOWithUnmask $ \unmask -> do + result <- try (unmask (checkpoint group >> body >>= evaluate)) + atomically (putTMVar bodyDone result) + worker <- case launched of + Left (exception :: SomeException) -> atomically (putTMVar bodyDone (Left exception)) >> pure Nothing + Right tid -> pure (Just tid) + let interruptBody = do + -- A lexical controller is host teardown, not user work. Its sticky stop + -- was set by stop; let it settle naturally rather than injecting an + -- exception into its release protocol. Only interrupt actual body work. + inner <- readTVarIO lexical + case inner of Nothing -> forM_ worker (\tid -> throwTo tid ThreadKilled); Just _ -> pure () + closeAdmission = atomically $ do + writeTVar phase Joining + R.beginCloseScopeSTM (runtimeOf scheduler) scope + joinChildren = do + let Scheduler _ _ _ waits = scheduler + atomically $ do + active <- readTVar children + graph <- readTVar waits + let targets = M.keys active + if any (reaches graph taskId []) targets + then stop group (Failed (ofString "task join cycle") []) + else modifyTVar' waits (M.insert taskId targets) + atomically (readTVar children >>= check . M.null) + `finally` atomically (modifyTVar' waits (M.delete taskId)) + release = do + atomically (writeTVar phase Releasing) + R.closeScope (runtimeOf scheduler) scope + finish outcome errors = atomically $ do + prior <- readTVar reason + descendants <- readTVar childErrors + let diagnostics = R.combineErrors (cleanupOf outcome) (R.combineErrors descendants errors) + final = attach (maybe (withoutCleanup outcome) id prior) diagnostics + case final of Failed _ _ -> stop group final; _ -> pure () + writeTVar childErrors (cleanupOf final) + writeTVar phase Closed + let Scheduler _ _ tickets _ = scheduler + modifyTVar' tickets (M.filter (\(Task owner _ _) -> scopeOf owner /= scope)) + pure final + execute = do + signal <- restore $ atomically $ do + requested <- readTVar reason + case requested of + Just _ -> pure Nothing + Nothing -> Just <$> readTMVar bodyDone + result <- case signal of + Just value -> pure value + Nothing -> interruptBody >> atomically (readTMVar bodyDone) + let outcome = either failedException id result + atomically $ case outcome of Completed _ -> pure (); _ -> stop group outcome + closeAdmission + joinChildren + errors <- release + finish outcome errors + abort (exception :: SomeException) = do + atomically (stop group (Cancelled (ofString "host interruption") [])) + interruptBody + void (atomically (readTMVar bodyDone)) + closeAdmission + joinChildren + errors <- release + void (finish (Cancelled (ofString "host interruption") []) errors) + throwIO exception + execute `catch` abort + +nested :: Group -> (Group -> IO Outcome) -> IO Outcome +nested parent@Group{ groupScheduler = scheduler, scopeOf = scope, taskIdentity = task, + groupCleanup = parentErrors, groupLexical = lexical } body = mask $ \restore -> do + allocated <- atomically $ do + checkpointSTM parent + reserved <- R.reserveChildSTM (runtimeOf scheduler) scope [] + case reserved of + Left e -> pure (Left e) + Right (childScope, _) -> do + child <- newGroup scheduler childScope task (Just (parent, True)) + writeTVar lexical (Just child) + pure (Right child) + case allocated of + Left e -> pure (Failed (ofString (R.category e)) []) + Right child@Group{ groupCleanup = diagnostics } -> do + result <- runGroup child (restore (body child)) `finally` atomically (do + errors <- readTVar diagnostics + modifyTVar' parentErrors (\previous -> R.combineErrors previous errors) + writeTVar lexical Nothing) + pure (withoutCleanup result) + +checkpointSTM :: Group -> STM () +checkpointSTM Group{ groupPhase = phase, groupReason = reason } = do + state <- readTVar phase + stopped <- readTVar reason + case (state, stopped) of + (Open, Nothing) -> pure () + _ -> throwSTM Interrupted + +-- A worker exists but cannot run before atomic resource transfer AND task +-- publication. Fork failure leaves the registry untouched. Failed validation +-- opens the gate with Nothing and joins that unused worker before returning. +spawn :: Group -> Bool -> Bool -> [R.Token] -> (Group -> [R.Token] -> IO Outcome) + -> IO (Either R.Problem TaskRef) +spawn owner@Group{ groupScheduler = scheduler, scopeOf = scope, + groupChildren = children, groupCleanup = ownerErrors } supervised keep tokens body = mask_ $ do + gate <- newEmptyTMVarIO + retired <- newEmptyMVar + let Scheduler runtime active tickets _ = scheduler + _ <- forkIOWithUnmask $ \unmask -> (do + entry <- atomically (takeTMVar gate) + case entry of + Nothing -> pure () + Just (task@(Task _ child completed), fresh) -> do + outcome <- unmask (runGroup child (body child fresh)) `catch` (pure . failedException) + atomically $ do + writeTVar completed (Just outcome) + if supervised then pure () else + modifyTVar' ownerErrors (\previous -> R.combineErrors previous (cleanupOf outcome)) + modifyTVar' children (M.delete (scopeOf child)) + modifyTVar' active (M.delete (scopeOf child)) + -- runGroup already propagated ordinary faults before release. This + -- also covers an unexpected controller fault at its outer boundary. + case outcome of Failed _ _ | not supervised -> stop owner outcome; _ -> pure () + task `seq` pure ()) `finally` putMVar retired () + let abandon = atomically (putTMVar gate Nothing) >> takeMVar retired + result <- (atomically $ do + checkpointSTM owner + reserved <- R.reserveChildSTM runtime scope tokens + case reserved of + Left e -> pure (Left e) + Right (childScope, fresh) -> do + child <- newGroup scheduler childScope childScope (Just (owner, not supervised)) + completed <- newTVar Nothing + let task = Task owner child completed + modifyTVar' children (M.insert childScope task) + modifyTVar' active (M.insert childScope task) + if keep then modifyTVar' tickets (M.insert childScope task) else pure () + putTMVar gate (Just (task, fresh)) + pure (Right childScope)) `onException` abandon + case result of + Left e -> abandon >> pure (Left e) + Right key -> do + identity <- R.runtimeIdentity runtime + pure (Right (TaskRef identity key)) + +within :: Group -> Group -> Bool +within current@Group{ groupParent = parent } owner = + scopeOf current == scopeOf owner || maybe False (\(outer, _) -> within outer owner) parent + +-- Registry lookup must remain inside the claiming transaction. Only the run +-- identity check is IO (it reads an immutable field). +checked :: Group -> TaskRef -> TVar (M.Map Integer Task) -> IO (STM (Either R.Problem Task)) +checked current (TaskRef run key) table = do + identity <- R.runtimeIdentity (runtimeOf (schedulerOf current)) + pure $ if identity /= run then pure (Left (problem "stale-task" "task")) else do + entries <- readTVar table + pure $ case M.lookup key entries of + Just task@(Task owner _ _) | within current owner -> Right task + Just _ -> Left (problem "out-of-scope" "task") + Nothing -> Left (problem "stale-task" "task") + +-- Includes implicit scope joining as well as explicit ticket waits. Lexical +-- controllers share their body's task identity, so nested scopes cannot hide a +-- cycle. Edges are removed when a wait settles, never retained as history. +reaches :: M.Map Integer [Integer] -> Integer -> [Integer] -> Integer -> Bool +reaches graph wanted seen target + | target == wanted = True + | target `elem` seen = False + | otherwise = any (reaches graph wanted (target:seen)) (M.findWithDefault [] target graph) + +join :: Group -> TaskRef -> IO (Either R.Problem Outcome) +join current@Group{ groupScheduler = Scheduler _ _ tickets waits, taskIdentity = taskId } ref@(TaskRef _ key) = mask $ \restore -> do + lookupEntry <- checked current ref tickets + claimed <- atomically $ do + checkpointSTM current + entry <- lookupEntry + graph <- readTVar waits + case entry of + Right task@(Task _ target _) | within current target || reaches graph taskId [] key -> pure (Left (problem "join-cycle" "join")) + | otherwise -> do + modifyTVar' tickets (M.delete key) + modifyTVar' waits (M.insert taskId [key]) + pure (Right task) + Left e -> pure (Left e) + case claimed of + Left e -> pure (Left e) + Right (Task _ _ completed) -> + (Right <$> restore (atomically (readTVar completed >>= maybe retry pure))) + `finally` atomically (modifyTVar' waits (M.delete taskId)) + +discard :: Group -> TaskRef -> IO (Either R.Problem ()) +discard current ref@(TaskRef _ key) = do + let Scheduler _ _ tickets _ = schedulerOf current + lookupEntry <- checked current ref tickets + atomically $ do + checkpointSTM current + entry <- lookupEntry + case entry of Left e -> pure (Left e); Right _ -> modifyTVar' tickets (M.delete key) >> pure (Right ()) + +cancel :: Group -> TaskRef -> T -> IO (Either R.Problem ()) +cancel current ref why = do + let Scheduler _ active _ _ = schedulerOf current + lookupEntry <- checked current ref active + atomically $ do + checkpointSTM current + entry <- lookupEntry + case entry of + Left e -> pure (Left e) + Right (Task _ child@Group{ groupPhase = phase } _) -> do + state <- readTVar phase + if state == Closed then pure (Left (problem "completed-task" "cancel")) + else stop child (Cancelled why []) >> pure (Right ()) + +liveCounts :: Scheduler -> IO (Int, Int, Int) +liveCounts (Scheduler _ active tickets waits) = atomically $ (,,) + <$> (M.size <$> readTVar active) <*> (M.size <$> readTVar tickets) <*> (M.size <$> readTVar waits) diff --git a/src/IOV2/UTF8.hs b/src/IOV2/UTF8.hs @@ -0,0 +1,63 @@ +{-# LANGUAGE BangPatterns #-} +-- Stateless bounded Unicode transport. Source owns the <=3-byte carry between +-- calls, text/source policy and sequencing; no shared mutable decoder context. +module IOV2.UTF8 (encode, decode, scalarLimit) where + +import qualified IOV2.Resources as R +import qualified Data.ByteString as BS +import qualified Data.Text as Text +import Data.Text.Encoding (encodeUtf8) +import Data.Char (chr, ord) +import Data.Bits ((.&.)) +import Data.List (foldl') + +scalarLimit :: Int +scalarLimit = R.chunkLimit `div` 4 + +fault op message = R.Problem "invalid-data" op message "none" + +encode :: String -> Either R.Problem BS.ByteString +encode text = encodeUtf8 . Text.pack <$> checked scalarLimit text + where + checked _ [] = Right [] + checked 0 _ = Left (R.Problem "invalid-bound" "encode" "Unicode chunk exceeds scalar limit" "none") + checked n (c:rest) + | ord c >= 0xd800 && ord c <= 0xdfff = Left (fault "encode" "surrogate is not a Unicode scalar") + | otherwise = (c:) <$> checked (n-1) rest + +-- Strict UTF-8, including overlong/surrogate/range controls. A nonfinal valid +-- incomplete suffix is copied (not a slice retaining the input buffer). Invalid +-- prefixes fail immediately, even when incomplete. No partial output on error. +decode :: Bool -> BS.ByteString -> Either R.Problem (String, BS.ByteString) +decode final bytes + | BS.length bytes > R.chunkLimit = Left (R.Problem "invalid-bound" "decode" "UTF-8 chunk exceeds byte limit" "none") + | otherwise = walk 0 [] + where + end = BS.length bytes + at i = fromIntegral (BS.index bytes i) :: Int + bad offset why = Left (fault "decode" (why ++ " at byte " ++ show offset)) + walk !offset chars + | offset == end = Right (reverse chars, BS.empty) + | otherwise = + let lead = at offset + emit code count = let !c = chr code in walk (offset + count) (c:chars) + sequenceOf count low high mask = + let valid i = let c = at (offset+i) + in c >= (if i == 1 then low else 0x80) + && c <= (if i == 1 then high else 0xbf) + available = min (count-1) (end-offset-1) + in if not (all valid [1..available]) then bad offset "invalid UTF-8 sequence" + else if end-offset < count then + if final then bad offset "incomplete UTF-8 sequence" + else Right (reverse chars, BS.copy (BS.drop offset bytes)) + else emit (foldl' (\code i -> code*64 + (at (offset+i) .&. 0x3f)) + (lead .&. mask) [1..count-1]) count + in if lead <= 0x7f then emit lead 1 + else if lead >= 0xc2 && lead <= 0xdf then sequenceOf 2 0x80 0xbf 0x1f + else if lead == 0xe0 then sequenceOf 3 0xa0 0xbf 0x0f + else if lead == 0xed then sequenceOf 3 0x80 0x9f 0x0f + else if lead >= 0xe1 && lead <= 0xef then sequenceOf 3 0x80 0xbf 0x0f + else if lead == 0xf0 then sequenceOf 4 0x90 0xbf 0x07 + else if lead == 0xf4 then sequenceOf 4 0x80 0x8f 0x07 + else if lead >= 0xf1 && lead <= 0xf3 then sequenceOf 4 0x80 0xbf 0x07 + else bad offset "invalid UTF-8 leading byte" diff --git a/src/IOV2/Wire.hs b/src/IOV2/Wire.hs @@ -0,0 +1,45 @@ +-- Pure v2 wire decoding, shared by native and browser embeddings. No resource, +-- scheduler, host effects or facility resolution imports. Payloads are borrowed +-- lazy trees: decoding never traverses arguments, callbacks or continuations. +module IOV2.Wire (Action(..), canonicalNat, decodeAction) where + +import Runtime.Tree (T(..)) + +canonicalNat :: Int -> T -> Maybe Integer +canonicalNat _ Leaf = Just 0 +canonicalNat 0 _ = Nothing +canonicalNat _ (Fork Leaf Leaf) = Nothing +canonicalNat depth (Fork Leaf rest) = (2 *) <$> canonicalNat (depth-1) rest +canonicalNat depth (Fork (Stem Leaf) rest) = (\n -> 2*n+1) <$> canonicalNat (depth-1) rest +canonicalNat _ _ = Nothing + +-- Names are nonempty ASCII identifiers, at most 64 characters. Do not inspect +-- an excess element or tail, or demand arguments while resolving a header. +name :: T -> Maybe String +name value = do + text <- walk (64 :: Int) value + case text of c:_ | c >= 'a' && c <= 'z' -> Just text; _ -> Nothing + where + walk _ Leaf = Just [] + walk 0 _ = Nothing + walk n (Fork c rest) = do + code <- canonicalNat 7 c + if (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code == 45 + then (toEnum (fromInteger code) :) <$> walk (n-1) rest + else Nothing + walk _ _ = Nothing + +data Action = Core Integer T | Request String Integer String T + +decodeAction :: T -> Either String Action +decodeAction (Fork tag payload) = case canonicalNat 8 tag of + Just n | n <= 9 -> Right (Core n payload) + Just 10 -> case payload of + Fork group (Fork revision (Fork operation arguments)) -> + case (name group, canonicalNat 16 revision, name operation) of + (Just g, Just v, Just op) | v > 0 -> Right (Request g v op arguments) + _ -> Left "invalid facility header" + _ -> Left "invalid facility header" + _ -> Left "unknown computation constructor" +decodeAction _ = Left "invalid computation shape" + diff --git a/src/InteractionInspection.hs b/src/InteractionInspection.hs @@ -43,7 +43,7 @@ data Report = Report , occurrenceLimitReached :: Bool } deriving (Eq, Show) -data View = Envelope | Action | Opaque Text | StringValue | BytesValue | Natural | Ignored +data View = Envelope | Action | ActionV2 | Opaque Text | StringValue | BytesValue | Natural | Ignored deriving (Eq, Ord, Show) data Decoded = Decoded Text Text [(Text, View, Word32)] @@ -74,7 +74,7 @@ inspectBundle limits name bundle = do base description state ref = Occurrence path depth idx (take (previewItems limits) names) (max 0 (length names - previewItems limits)) role description state ref Decoded summary status children = decode nodes limits view idx - shared = if view == Action then M.lookup idx seen else Nothing + shared = if view `elem` [Action, ActionV2] then M.lookup (view,idx) seen else Nothing in case shared of Just first -> walk seen (count + 1) pending (base "shared action structure (not execution)" "reference" (Just first) : acc) Nothing -> @@ -84,7 +84,7 @@ inspectBundle limits name bundle = do else base summary status Nothing next = if depthCut then pending else [(path <> "." <> field, depth + 1, field, childView, child) | (field,childView,child) <- children] ++ pending - seen' = if view == Action then M.insert idx path seen else seen + seen' = if view `elem` [Action, ActionV2] then M.insert (view,idx) path seen else seen in walk seen' (count + 1) next (row : acc) node :: Nodes -> Word32 -> W.BundleNode @@ -113,6 +113,46 @@ natural nodes = go 0 1 0 W.BNFork _ _ -> Left "number exceeds 64-bit inspection limit" _ -> Left "invalid number shape" +-- V2 and envelope numbers are canonical and independently bounded. +canonicalNatural :: Nodes -> Int -> Word32 -> Either Text Integer +canonicalNatural nodes = go + where + go _ i | W.BNLeaf <- node nodes i = Right 0 + go 0 _ = Left "canonical number exceeds bit bound" + go depth i = case node nodes i of + W.BNFork bit rest -> do + b <- case node nodes bit of + W.BNLeaf -> Right 0 + W.BNStem l | W.BNLeaf <- node nodes l -> Right 1 + _ -> Left "invalid canonical number bit" + n <- go (depth-1) rest + if b == 0 && n == 0 then Left "noncanonical number" else Right (b + 2*n) + _ -> Left "invalid canonical number shape" + +facilityName :: Nodes -> Word32 -> Either Text Text +facilityName nodes root = do + chars <- go (64 :: Int) root + case chars of c:_ | c >= 'a' && c <= 'z' -> Right (T.pack chars); _ -> Left "invalid facility name" + where + go _ i | W.BNLeaf <- node nodes i = Right [] + go 0 _ = Left "facility name exceeds 64 characters" + go left i = do + (h,r) <- pair nodes i + n <- canonicalNatural nodes 7 h + if (n >= 97 && n <= 122) || (n >= 48 && n <= 57) || n == 45 + then (chr (fromInteger n) :) <$> go (left-1) r + else Left "invalid facility name character" + +sentinelName :: Nodes -> Word32 -> Either Text () +sentinelName nodes = go "tricuIO" + where + go [] i | W.BNLeaf <- node nodes i = Right () + go (c:cs) i = do + (h,r) <- pair nodes i + n <- canonicalNatural nodes 7 h + if n == toInteger (fromEnum c) then go cs r else Left "expected canonical tricuIO envelope" + go _ _ = Left "expected canonical tricuIO envelope" + -- Preview only; an unvisited suffix is not certified valid. The final tail is -- inspected to distinguish an exact-length list from a truncated prefix. listPreview :: Nodes -> Int -> (Integer -> Bool) -> Word32 -> Either Text ([Integer], Bool) @@ -140,14 +180,14 @@ decode :: Nodes -> Limits -> View -> Word32 -> Decoded decode nodes limits view idx = case view of Envelope -> case do (sentinel, body) <- pair nodes idx - (chars, cut) <- listPreview nodes 7 scalar sentinel - when (cut || map (chr . fromInteger) chars /= "tricuIO") $ Left "expected tricuIO envelope; export is not decoded as IO (no application attempted)" + sentinelName nodes sentinel (version, action) <- pair nodes body - n <- natural nodes version + n <- canonicalNatural nodes 16 version pure (n,action) of Left err -> boundedFailure err Right (1, action) -> Decoded "tricuIO v1" "decoded" [("action",Action,action)] + Right (2, action) -> Decoded "tricuIO v2" "decoded" [("action",ActionV2,action)] Right (n, _) -> Decoded ("unsupported tricuIO version " <> tshow n) "unknown" [] Action -> case pair nodes idx of Left _ -> failure "expected action pair (tag, payload)" @@ -158,6 +198,28 @@ decode nodes limits view idx = case view of Just (label, fields) -> case unpackFields payload fields of Left err -> failure (label <> ": " <> err) Right children -> Decoded label "decoded" children + ActionV2 -> case pair nodes idx of + Left err -> failure err + Right (tag,payload) -> case canonicalNatural nodes 8 tag of + Left err -> boundedFailure err + Right 10 -> case do + (g,rest) <- pair nodes payload + (v,body) <- pair nodes rest + (op,args) <- pair nodes body + group <- facilityName nodes g + revision <- canonicalNatural nodes 16 v + when (revision == 0) (Left "zero facility revision") + operationName <- facilityName nodes op + pure (group,revision,operationName,args) + of + Left err -> boundedFailure err + Right (g,v,op,args) -> Decoded ("Facility " <> g <> "@" <> tshow v <> "/" <> op) + "decoded" [("arguments",Opaque "arguments (not validated; backend availability not inferred)",args)] + Right n -> case lookup n coreV2 of + Nothing -> Decoded ("unknown IO v2 constructor " <> tshow n) "unknown" [] + Just (label,fields) -> case unpackFields payload fields of + Left err -> failure (label <> ": " <> err) + Right children -> Decoded label "decoded" children Opaque label -> Decoded label "opaque" [] Ignored -> Decoded "ignored by host" "opaque" [] Natural -> case natural nodes idx of @@ -178,6 +240,22 @@ decode nodes limits view idx = case view of (format values <> if cut then " ... (suffix not inspected)" else "") (if cut then "truncated" else "decoded") [] +-- Scope/spawn callbacks and cancellation reasons stay opaque. Only bind's +-- explicit first action is traversed; this never applies a continuation. +coreV2 :: [(Integer, (Text, [(Text, View)]))] +coreV2 = + [ (0,("Pure",[("value",opaque)])) + , (1,("Bind",[("action",ActionV2),("resume",Opaque "continuation (not applied)")])) + , (2,("Fail",[("diagnostic",opaque)])) + , (3,("Scope",[("callback",Opaque "scope callback (not applied)")])) + , (4,("CurrentScope",[("payload",Ignored)])) + , (5,("Spawn",[("supervised",opaque),("keepResult",opaque),("handles",opaque),("callback",Opaque "child callback (not applied)")])) + , (6,("Join",[("ticket",opaque)])), (7,("Discard",[("ticket",opaque)])) + , (8,("Cancel",[("task",opaque),("reason",opaque)])) + , (9,("Cancelled",[("reason",opaque)])) + ] + where opaque = Opaque "tree value (not validated)" + -- The host IO v1 field layouts, not the incompatible pure contract-effect -- protocol. Keep independent fixture expectations in InteractionInspectionTests. -- Zero-argument operations deliberately expose their ignored payload: the host @@ -227,6 +305,13 @@ operation tag = lookup tag , (76, ("Recv", sock ++ [("maxBytes",Natural)])) , (77, ("Send", sock ++ [("bytes",BytesValue)])) , (78, ("GetSocketName", sock)) + -- Native experimental upload layouts. Previews do not validate session + -- ownership, state, exact length, or the complete bounded chunk contract. + , (90, ("UploadBegin", [("directory",StringValue),("expected",Natural)])) + , (91, ("UploadAppend", [("handle",value),("chunk",BytesValue)])) + , (92, ("UploadSeal", [("handle",value)])) + , (93, ("UploadCommit", [("handle",value),("destination",StringValue)])) + , (94, ("UploadAbort", [("handle",value)])) ] where value = Opaque "tree value" @@ -249,7 +334,7 @@ quoted = tshow . T.unpack renderOutline :: Report -> Text renderOutline report = T.unlines $ - ["export " <> quoted (selectedExport report) <> " [host IO v1; read-only]" + ["export " <> quoted (selectedExport report) <> " [host IO v1/v2; read-only]" ,"tree IDs are bundle-local; aliases are exact exported-tree matches"] ++ map row (occurrences report) ++ ["... occurrence limit reached; remaining traversal omitted" | occurrenceLimitReached report] @@ -262,9 +347,9 @@ renderOutline report = T.unlines $ reportJSON :: Text -> Report -> Value reportJSON bundleSHA256 report = object - [ "schema" .= ("tricu.interaction-inspection.v1" :: Text) + [ "schema" .= ("tricu.interaction-inspection.v2" :: Text) , "protocol" .= ("tricuIO" :: Text) - , "supportedVersion" .= (1 :: Int) + , "supportedVersions" .= ([1,2] :: [Int]) , "bundleSha256" .= bundleSHA256 , "identityScope" .= ("bundle-local node indices; not CAS addresses" :: Text) , "export" .= selectedExport report diff --git a/src/Main.hs b/src/Main.hs @@ -11,7 +11,7 @@ import ContentStore.Bundle import Module.Manifest import System.IO.Error (ioeGetErrorString) import System.Exit (die, exitFailure) -import IODriver (IOPermissions(..), runIO, YieldStats(..), runIOWithYieldStatsHook) +import IODriver (runIO) import REPL (portableRepl) import PortableCompiler import Runtime (T(..), EvaluatedForm(..), formatT, exportDag, apply, ofString, toString) @@ -43,7 +43,6 @@ import System.IO (openBinaryTempFile, hClose, hFlush, hPutStrLn, stdout, stderr, import qualified Data.Aeson as JSON import GHC.Clock (getMonotonicTimeNSec) import GHC.Stats -import Data.IORef (newIORef, atomicModifyIORef') import System.Mem (performGC) import System.Posix.Files (fileMode, getFileStatus, setFileMode, isRegularFile) @@ -247,9 +246,9 @@ runProject args = (case args of unless complete exitFailure data ProvenanceArgs - = CaptureInputs FilePath (Maybe FilePath) FilePath String (Maybe String) (Maybe (FilePath,Maybe FilePath)) FilePath + = CaptureInputs FilePath (Maybe FilePath) FilePath String Int (Maybe String) (Maybe (FilePath,Maybe FilePath)) FilePath | VerifyInputs FilePath String - | RebuildInputs FilePath String String FilePath Int + | RebuildInputs FilePath String String FilePath Int Int deriving (Show) provenanceParser :: Parser TricuArgs @@ -264,29 +263,37 @@ provenanceParser = ProvenanceCommand <$> subparser (mconcat pin = opt "pin" "INPUTS_HASH" capture = CaptureInputs <$> opt "file" "ENTRY.tri" <*> storePathParser <*> opt "tool-store" "TOOL_STORE" <*> opt "toolchain" "COMPILER_MANIFEST" + <*> ioVersion "toolchain-io-version" <*> optional (opt "receipt" "NATIVE_RECEIPT_HASH") <*> optional ((,) <$> opt "project" "DECLARATION" <*> optional (opt "lock" "PROJECT_LOCK")) <*> opt "output" "NEW_PACK_DIRECTORY" + ioVersion name = option (eitherReader (\raw -> case raw of + "1" -> Right 1 + "2" -> Right 2 + _ -> Left "IO version must be 1 or 2")) + (long name <> metavar "VERSION" <> value 1 + <> help "Explicit compiler IO protocol: 1 (default) or 2 (OS process authority)") verify = VerifyInputs <$> pack <*> pin rebuild = RebuildInputs <$> pack <*> pin <*> strOption (long "trust-compiler" <> metavar "COMPILER_MANIFEST" <> help "Explicitly authorize this compiler; a matching hash is not publisher trust") <*> opt "output" "NEW_REBUILD_DIRECTORY" <*> option auto (long "compile-timeout" <> value 120 <> showDefault <> help "Compiler deadline in seconds") + <*> ioVersion "trust-io-version" runProvenance :: ProvenanceArgs -> IO () runProvenance args = (case args of - CaptureInputs file store tools compiler receipt project output -> do + CaptureInputs file store tools compiler ioVersion receipt project output -> do app <- resolveStorePath store let context = fmap (\(p,l) -> (p, maybe (takeDirectory p </> "tricu.lock") id l)) project - pin <- Provenance.capture file app (StorePath tools) (T.pack compiler) (T.pack <$> receipt) context output + pin <- Provenance.capture file app (StorePath tools) (T.pack compiler) ioVersion (T.pack <$> receipt) context output putStrLn ("Retained build inputs: " ++ T.unpack pin ++ "\nProducer claims; use verify, then an explicitly trusted rebuild.") VerifyInputs pack pin -> Provenance.verifyPack pack (T.pack pin) >>= putStr . T.unpack - RebuildInputs pack pin trusted output seconds -> do + RebuildInputs pack pin trusted output seconds trustedIO -> do let tools = output </> "tools" app = output </> "application" - options = CompilerOptions (Just trusted) (Just tools) seconds False + options = CompilerOptions (Just trusted) (Just tools) seconds False trustedIO validateCompilerOptions options - recipe <- Provenance.prepareRebuild pack (T.pack pin) (T.pack trusted) output + recipe <- Provenance.prepareRebuild pack (T.pack pin) (T.pack trusted) trustedIO output (_,actual,receipt) <- buildPortableResult options (Just app) (output </> "workspace" </> T.unpack (Provenance.recipeEntry recipe) ++ ".tri") (report,exact) <- Provenance.finishRebuild pack (T.pack pin) output recipe actual receipt @@ -320,6 +327,12 @@ compilerOptionsParser = CompilerOptions <> help "Portable compiler load/build deadline (default 120 seconds)") <*> switch (long "compile-profile" <> help "Report portable load/build stages and RTS counters to stderr (requires +RTS -T)") + <*> option (eitherReader (\version -> case version of + "1" -> Right 1 + "2" -> Right 2 + _ -> Left "--toolchain-io-version must be 1 or 2")) + (long "toolchain-io-version" <> metavar "VERSION" <> value 2 + <> help "Selected compiler IO protocol: 2 (OS process authority, default) or 1 (explicit retained bridge); no fallback") evalParser :: Parser TricuArgs evalParser = Eval @@ -686,7 +699,7 @@ applyGlobalStore mGlobal args = case args of ProjectCommand (ProjectExplain file lock directory store) -> ProjectCommand (ProjectExplain file lock directory (preferLocal store)) ProjectCommand (ProjectDiff before after store) -> ProjectCommand (ProjectDiff before after (preferLocal store)) ProjectCommand _ -> args - ProvenanceCommand (CaptureInputs file store tools compiler receipt project output) -> ProvenanceCommand (CaptureInputs file (preferLocal store) tools compiler receipt project output) + ProvenanceCommand (CaptureInputs file store tools compiler ioVersion receipt project output) -> ProvenanceCommand (CaptureInputs file (preferLocal store) tools compiler ioVersion receipt project output) ProvenanceCommand _ -> args Repl options -> Repl options Eval {} -> args { evalStore = preferLocal (evalStore args) } @@ -708,7 +721,7 @@ runRepl :: IO () runRepl = runReplWithStore Nothing runReplWithStore :: Maybe FilePath -> IO () -runReplWithStore mStore = runReplWithOptions mStore (CompilerOptions Nothing Nothing 120 False) +runReplWithStore mStore = runReplWithOptions mStore (CompilerOptions Nothing Nothing 120 False 2) runReplWithOptions :: Maybe FilePath -> CompilerOptions -> IO () runReplWithOptions mStore options = do @@ -728,6 +741,9 @@ runReplWithOptions mStore options = do runEval :: TricuArgs -> IO () runEval opts = do + when (evalUnsafeIo opts || evalAllowReadAll opts || evalAllowWriteAll opts + || not (null (evalAllowRead opts)) || not (null (evalAllowWrite opts))) $ + die "IO v1 permission grants are not accepted; IO v2 uses OS process authority" let files = evalFiles opts form = evalFormat opts out = evalOutput opts @@ -743,13 +759,7 @@ runEval opts = do _ -> die "Portable eval requires one native module file exporting main (or stdin)" finalT <- if evalIo opts then do - let perms = IOPermissions - { allowRead = evalAllowRead opts - , allowWrite = evalAllowWrite opts - , allowReadAll = evalUnsafeIo opts || evalAllowReadAll opts - , allowWriteAll = evalUnsafeIo opts || evalAllowWriteAll opts - } - result <- runIO perms resultT + result <- runIO resultT case result of Left err -> die $ "IO error: " ++ err Right val -> pure val @@ -772,7 +782,7 @@ runTreeWrite opts = do when symbolic $ die "Tree output refuses symbolic links" exists <- doesPathExist path permissions <- if exists then Just . fileMode <$> getFileStatus path else pure Nothing - (store, manifest) <- buildPortableModule (CompilerOptions Nothing Nothing 120 False) + (store, manifest) <- buildPortableModule (CompilerOptions Nothing Nothing 120 False 2) (treeStore opts) (treeInput opts) (_, value) <- loadVerifiedExport store manifest (T.pack $ treeName opts) let payload = encodeTreeTerm value @@ -889,6 +899,9 @@ runImport opts = do -- No .tri frontend or source compilation occurs here. runStored :: TricuArgs -> IO () runStored opts = do + when (storedUnsafeIo opts || not (null (storedAllowRead opts)) + || not (null (storedAllowWrite opts))) $ + die "IO v1 permission grants are not accepted; IO v2 uses OS process authority" when (storedNoResult opts && (not (storedIo opts) || storedResult opts)) $ die "--no-result requires --io and cannot be combined with --result" unless (storedRepeat opts >= 1 && storedRepeat opts <= 1000) $ @@ -898,36 +911,20 @@ runStored opts = do when (storedProfile opts) $ do enabled <- getRTSStatsEnabled unless enabled $ die "--profile requires +RTS -T" - let sample label = when (storedProfile opts) (profileSample label Nothing) + let sample label = when (storedProfile opts) (profileSample label) sample "load-begin" store <- resolveStorePath (storedStore opts) (_, term) <- loadVerifiedExport store (T.pack (storedModule opts)) (T.pack (storedName opts)) arguments <- mapM (storedArgument store) (storedArgs opts) let applied = foldl apply term arguments - perms = IOPermissions - { allowRead = storedAllowRead opts - , allowWrite = storedAllowWrite opts - , allowReadAll = storedUnsafeIo opts - , allowWriteAll = storedUnsafeIo opts - } - unless (storedIo opts) $ - when (storedUnsafeIo opts || not (null $ storedAllowRead opts) - || not (null $ storedAllowWrite opts)) $ - die "IO permissions require --io" _ <- evaluate term sample "load-end" when (storedRepeat opts > 1) $ putStrLn "READY" forM_ [1..storedRepeat opts] $ \iteration -> do let label phase = phase ++ "-" ++ show iteration sample (label "invoke-begin") - yieldCount <- newIORef (0 :: Int) - let onYield counts = do - ordinal <- atomicModifyIORef' yieldCount (\count -> (count + 1, count + 1)) - profileSample (label "yield" ++ "-" ++ show ordinal) (Just counts) finalT <- if storedIo opts - then (if storedProfile opts - then fmap (fmap fst) (runIOWithYieldStatsHook onYield perms Leaf Leaf applied) - else runIO perms applied) >>= either (die . ("IO error: " ++)) pure + then runIO applied >>= either (die . ("IO error: " ++)) pure else pure applied sample (label "invoke-end") when (storedProfileFullResult opts) $ do @@ -950,22 +947,18 @@ runStored opts = do -- One digest/kind/ABI-verified boundary for runtime execution and tooling. -- Each sample forces a major collection. Deltas include sampling overhead; -- yielded samples retain the scheduler's pending work. Rendering follows timing. -profileSample :: String -> Maybe YieldStats -> IO () -profileSample label counts = do +profileSample :: String -> IO () +profileSample label = do performGC stats <- getRTSStats wall <- getMonotonicTimeNSec - BL.hPutStr stderr (JSON.encode (JSON.object ( + BL.hPutStr stderr (JSON.encode (JSON.object [ "profile" JSON..= label , "wall_ns" JSON..= wall , "allocated_bytes" JSON..= allocated_bytes stats , "live_bytes" JSON..= gcdetails_live_bytes (gc stats) , "gc_elapsed_ns" JSON..= gc_elapsed_ns stats - ] ++ maybe [] (\snapshot -> - [ "bind_frames" JSON..= yieldBindFrames snapshot - , "local_frames" JSON..= yieldLocalFrames snapshot - , "tasks" JSON..= yieldTasks snapshot - ]) counts))) + ])) BL.hPutStr stderr "\n" -- Presentation of the canonical Result envelope; the underlying tree survives. diff --git a/src/Module/Provenance.hs b/src/Module/Provenance.hs @@ -24,10 +24,9 @@ import System.FilePath ((</>), takeDirectory, takeBaseName, takeExtension) import System.IO (openBinaryFile, IOMode(ReadMode), hClose) import System.Posix.Files (getSymbolicLinkStatus, isRegularFile, isDirectory) -receiptDomain, sourceDomain, recipeDomain, optionsDomain :: Domain +receiptDomain, sourceDomain, optionsDomain :: Domain receiptDomain = Domain "arboricx.build-receipt.v1" sourceDomain = Domain "arboricx.source.v1" -recipeDomain = Domain "arboricx.build-inputs.v1" optionsDomain = Domain "arboricx.build-options.v1" profile :: BS.ByteString profile = "source-module.stable.v1" @@ -38,6 +37,7 @@ data Receipt = Receipt } deriving (Eq, Show) data Recipe = Recipe { recipeEntry :: Text, recipeReceipt :: Text, recipeCompiler :: Text + , recipeIOVersion :: Int , recipeUnits :: [(Text,Text)], recipeProject :: Maybe (Text,Text) } deriving (Eq, Show) data Closure = Closure @@ -80,18 +80,37 @@ parseReceipt bytes = do pure (Receipt s c o out ls ps) _ -> Left "invalid native build receipt" +-- The historical profile remains byte-for-byte v1. The v2 profile explicitly +-- binds OS-authority execution; neither schema permits guessing or fallback. +recipeHeader :: Int -> Text +recipeHeader 2 = "arboricx.build-inputs.v2" +recipeHeader _ = "arboricx.build-inputs.v1" +recipeDomain :: Int -> Domain +recipeDomain = Domain . recipeHeader + +recipeVersion :: BS.ByteString -> Either String Int +recipeVersion bytes = case BS.takeWhile (/= 10) bytes of + "arboricx.build-inputs.v1" -> Right 1 + "arboricx.build-inputs.v2" -> Right 2 + _ -> Left "unsupported build inputs schema" + encodeRecipe :: Recipe -> BS.ByteString encodeRecipe r = encodeUtf8 $ T.unlines $ - ["arboricx.build-inputs.v1", "entry\t" <> recipeEntry r, "receipt\t" <> recipeReceipt r, + [recipeHeader (recipeIOVersion r), "entry\t" <> recipeEntry r, "receipt\t" <> recipeReceipt r, "compiler\t" <> recipeCompiler r] ++ + ["compiler-io\t2" | recipeIOVersion r == 2] ++ ["unit\t" <> n <> "\t" <> p | (n,p) <- recipeUnits r] ++ maybe [] (\(p,l) -> ["project\t" <> p <> "\t" <> l]) (recipeProject r) decodeRecipe :: BS.ByteString -> Either String Recipe decodeRecipe bytes = do - rows <- textRows "arboricx.build-inputs.v1" bytes + ioVersion <- recipeVersion bytes + rows <- textRows (recipeHeader ioVersion) bytes case rows of - ["entry",entry]:["receipt",receipt]:["compiler",compiler]:rest | J.validBinding entry -> do + ["entry",entry]:["receipt",receipt]:["compiler",compiler]:tailRows | J.validBinding entry -> do + rest <- if ioVersion == 1 then Right tailRows else case tailRows of + ["compiler-io","2"]:remaining -> Right remaining + _ -> Left "v2 build inputs require compiler-io 2" let (units,context) = span (\row -> take 1 row == ["unit"]) rest us <- mapM (\row -> case row of ["unit",n,p] | J.validBinding n -> validateObjectHash p >> pure (n,p) @@ -104,7 +123,7 @@ decodeRecipe bytes = do [["project",p,l]] -> mapM_ validateObjectHash [p,l] >> pure (Just (p,l)) _ -> Left "invalid project context" unless (lookup entry us == Just receipt) $ Left "entry receipt not in source units" - let result = Recipe entry receipt compiler us ctx + let result = Recipe entry receipt compiler ioVersion us ctx unless (encodeRecipe result == bytes) $ Left "noncanonical build inputs" pure result _ -> Left "invalid build inputs" @@ -232,8 +251,9 @@ cacheReceipt (StorePath store) name = do pure receipt _ -> fail "invalid native build cache locator" -capture :: FilePath -> StorePath -> StorePath -> Text -> Maybe Text -> Maybe (FilePath,FilePath) -> FilePath -> IO Text -capture file app tools compiler selectedReceipt project output = do +capture :: FilePath -> StorePath -> StorePath -> Text -> Int -> Maybe Text -> Maybe (FilePath,FilePath) -> FilePath -> IO Text +capture file app tools compiler ioVersion selectedReceipt project output = do + unless (ioVersion `elem` [1,2]) $ fail "compiler IO version must be 1 or 2" unless (takeExtension file == ".tri" && J.validBinding (T.pack (takeBaseName file))) $ fail "capture requires a native .tri entry" let entry = T.pack (takeBaseName file) sources <- workspaceSources file @@ -255,7 +275,7 @@ capture file app tools compiler selectedReceipt project output = do toolManifest <- either fail pure (decodeManifest toolManifestBytes) let toolPins = S.fromList ((unDomain manifestDomain,compiler) : [(objectRefKind r,objectRefHash r) | ex <- moduleManifestExports toolManifest, let r = moduleExportObject ex]) combined d p = if S.member (unDomain d,p) toolPins then loadStore tools d p else load d p - c <- collect (Recipe entry receipt compiler [] ctx) combined (\name _ -> maybe (fail "receipt source absent from workspace") (bounded 1048576) (M.lookup (T.unpack name) sources)) + c <- collect (Recipe entry receipt compiler ioVersion [] ctx) combined (\name _ -> maybe (fail "receipt source absent from workspace") (bounded 1048576) (M.lookup (T.unpack name) sources)) -- Publication is create-only; a failed capture may leave an incomplete directory -- with no descriptor. Never replace or merge another pack. createDirectoryIfMissing True (takeDirectory output) @@ -266,12 +286,13 @@ capture file app tools compiler selectedReceipt project output = do let bytes = encodeRecipe (cRecipe c) _ <- either fail pure (decodeRecipe bytes) P.writeAtomic True (output </> "build-inputs") bytes - pure (hashObject recipeDomain bytes) + pure (hashObject (recipeDomain ioVersion) bytes) readPack :: FilePath -> Text -> IO Closure readPack directory pin = do bytes <- bounded 65536 (directory </> "build-inputs") - either fail pure (verifyObjectBytes recipeDomain pin bytes) + ioVersion <- either fail pure (recipeVersion bytes) + either fail pure (verifyObjectBytes (recipeDomain ioVersion) pin bytes) recipe <- either fail pure (decodeRecipe bytes) let store = StorePath (directory </> "store") c <- collect recipe (loadStore store) (\_ source -> loadStore store sourceDomain source) @@ -283,15 +304,17 @@ verifyPack directory pin = do c <- readPack directory pin pure ("Verified retained bytes and native receipt links (referenced closure only). Producer claims only; no build executed.\n" <> "Inputs " <> pin <> "\nNative receipt " <> recipeReceipt (cRecipe c) - <> "\nCompiler " <> recipeCompiler (cRecipe c) <> "\n") + <> "\nCompiler " <> recipeCompiler (cRecipe c) + <> "\nCompiler IO version " <> T.pack (show (recipeIOVersion (cRecipe c))) <> "\n") -- Prepare only explicitly needed executable inputs. Claimed outputs and receipts -- remain in the pack, not the fresh build store. No producer caches are copied. -prepareRebuild :: FilePath -> Text -> Text -> FilePath -> IO Recipe -prepareRebuild directory pin trusted output = do +prepareRebuild :: FilePath -> Text -> Text -> Int -> FilePath -> IO Recipe +prepareRebuild directory pin trusted trustedIO output = do c <- readPack directory pin let recipe = cRecipe c unless (trusted == recipeCompiler recipe) $ fail "--trust-compiler must equal the retained compiler manifest; verification alone does not authorize execution" + unless (trustedIO == recipeIOVersion recipe) $ fail "--trust-io-version must equal the retained compiler IO version; v2 uses OS process authority" createDirectoryIfMissing True (takeDirectory output) createDirectory output let workspace = output </> "workspace" @@ -361,7 +384,7 @@ finishRebuild directory pin output recipe actual actualReceipt = do _ -> "unknown" exact = actual == expected && actualReceipt == recipeReceipt recipe && I.inspectionStatus before == "verified" && I.inspectionStatus after == "verified" report = (if exact then "Reproduced native module and receipt exactly.\n" else "Rebuild mismatch; not reproduced exactly.\n") - <> "Inputs " <> pin <> "\nExpected module " <> expected <> "\nActual module " <> actual + <> "Inputs " <> pin <> "\nCompiler IO version " <> T.pack (show (recipeIOVersion recipe)) <> "\nExpected module " <> expected <> "\nActual module " <> actual <> "\nExpected receipt " <> recipeReceipt recipe <> "\nActual receipt " <> actualReceipt <> "\n" <> T.concat rows <> "Local observation, not publisher authentication or a signed attestation.\n" P.writeAtomic True (output </> "rebuild.txt") (encodeUtf8 report) diff --git a/src/PortableCompiler.hs b/src/PortableCompiler.hs @@ -11,7 +11,7 @@ import ContentStore.Bundle (unpackIndexedBundleToStore, indexedTreeDomain) import ContentStore.Executable import Module.Manifest import Runtime (T(..), EvaluatedForm(Decode), apply, ofString, ofNumber, toString, formatT) -import IODriver (IOPermissions(..), runIO) +import IODriver (runIO) import Paths_tricu (getDataFileName) import Crypto.Hash (Digest, SHA256, hash) import Control.Exception (IOException, catch) @@ -34,6 +34,7 @@ data CompilerOptions = CompilerOptions , compilerToolStore :: Maybe FilePath , compilerSeconds :: Int , compilerProfile :: Bool + , compilerIOVersion :: Int } deriving (Show) -- Startup and actual compilation use the same artifact selection, validation @@ -63,7 +64,7 @@ loadCompiler sample options store = do -- retain their data closure when invoked outside the checkout. An explicit -- tricu_datadir override is useful for relocatable distributions and diagnostics. packagedCompilerSha256 :: String -packagedCompilerSha256 = "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e" +packagedCompilerSha256 = "848c641ac8bbfd1ebe0ea72685c3c7b468aadf70f838f0370bcb48558582a0b4" packagedCompiler :: StorePath -> IO T.Text packagedCompiler store = do @@ -86,6 +87,8 @@ validateCompilerOptions :: CompilerOptions -> IO () validateCompilerOptions options = do when (compilerManifest options == Nothing && compilerToolStore options /= Nothing) $ fail "--tool-store requires --toolchain" + unless (compilerIOVersion options == 2) $ + fail "--toolchain-io-version must be 2; IO v1 compilers require a retained v1 runtime" unless (compilerSeconds options >= 1 && compilerSeconds options <= 86400) $ fail "--compile-timeout must be between 1 and 86400 seconds" @@ -117,11 +120,10 @@ buildPortableResult options appStore input = do (compilerIdentity, worker) <- loadCompiler sample options store let action = foldl apply worker (map ofString [directory, root, compilerIdentity, name]) - permissions = IOPermissions [directory, root] [root] False False - -- sourceBuildGraph returns a bare v1 action; this is transport framing, - -- not source compilation or a source-loaded IO adapter. - let envelope = Fork (ofString "tricuIO") (Fork (ofNumber 1) action) - output <- runIO permissions envelope >>= either (fail . ("Compiler IO error: " ++)) pure + version = compilerIOVersion options + -- Honor the selected protocol exactly; never probe or retry an interpreter. + let envelope = Fork (ofString "tricuIO") (Fork (ofNumber (toInteger version)) action) + output <- runIO envelope >>= either (fail . ("Compiler IO error: " ++)) pure sample "build-returned" case output of Fork (Stem Leaf) (Fork (Fork manifest receipt) _) -> do diff --git a/src/Runtime/Value.hs b/src/Runtime/Value.hs @@ -4,10 +4,19 @@ module Runtime.Value where import Runtime.Tree import Data.Word (Word8) import qualified Data.ByteString as BS +import qualified Data.IntMap.Strict as IM -- | Encode a single byte (Word8) as a Tree Calculus number (0..255). ofByte :: Word8 -> T -ofByte = ofNumber . fromIntegral +ofByte byte = byteTrees IM.! fromIntegral byte + +-- Immutable canonical values: host byte buffers have only 256 possible +-- elements. Share their numeral trees rather than retaining a conversion +-- computation (and eventually a fresh bit spine) for every input byte. +-- Tree equality/encoding is unchanged; no pointer identity is exposed. +{-# NOINLINE byteTrees #-} +byteTrees :: IM.IntMap T +byteTrees = IM.fromDistinctAscList [(n, ofNumber (toInteger n)) | n <- [0..255]] -- | Decode a Tree Calculus number as a single byte (Word8). -- Rejects values outside the range 0..255. @@ -20,7 +29,12 @@ toByte t = case toNumber t of -- | Encode a ByteString as a Tree Calculus list of Byte trees. ofBytes :: BS.ByteString -> T -ofBytes = ofList . map ofByte . BS.unpack +ofBytes = BS.foldr consByte Leaf + where + -- Force the bounded table lookup when the list cell is demanded, so an + -- unexamined body does not retain one lookup closure per byte. Input is + -- already a finite strict ByteString, not arbitrary language computations. + consByte byte rest = let value = ofByte byte in value `seq` Fork value rest -- | Decode a Tree Calculus list of Byte trees as a ByteString. -- Rejects non-list trees and elements that are not valid byte values (0..255). diff --git a/src/Wire.hs b/src/Wire.hs @@ -14,7 +14,9 @@ module Wire , ClosureMode (..) , BundleNode (..) , encodeBundle + , bundleEncodedLength, emitBundle , decodeBundle + , decodeBundleSnapshot , verifyBundle , buildBundle , buildBundleShared @@ -31,10 +33,13 @@ module Wire ) where import Runtime hiding (Node) +import ContentStore.Snapshot (Snapshot, snapshotLength, readSnapshot) +import Control.Monad.Trans.Except (runExceptT, throwE) +import Control.Monad.IO.Class (liftIO) import Control.Monad (foldM, forM_, unless, when) import Control.Exception (evaluate) -import Data.IORef (newIORef, readIORef, modifyIORef') +import Data.IORef (newIORef, readIORef, writeIORef, modifyIORef') import System.Mem.StableName (StableName, makeStableName, hashStableName) import qualified Data.IntMap.Strict as IntMap import Data.Bits (shiftL, shiftR, (.|.), (.&.)) @@ -669,6 +674,51 @@ encodeBundle bundle = (fromIntegral sectionCount) 0 dirOffset in header <> manifestEntry <> nodesEntry <> manifestBytes <> nodeSection +-- Canonical writer transport without a whole encoded ByteString. The indexed +-- table remains resident; each emission owns only its bounded pending buffer. +bundleEncodedLength :: Bundle -> Integer +bundleEncodedLength bundle = 104 + toInteger (BS.length (bundleManifestBytes bundle)) + + Foldable.foldl' (\n node -> n + recordLength node) 0 (bundleNodes bundle) + where + recordLength BNLeaf = 5 + recordLength (BNStem _) = 9 + recordLength (BNFork _ _) = 13 + +emitBundle :: Int -> Bundle -> (ByteString -> IO ()) -> IO () +emitBundle bound bundle sink = do + when (bound <= 0) (fail "invalid bundle transport chunk bound") + let total = bundleEncodedLength bundle + manifest = bundleManifestBytes bundle + manifestSize = toInteger (BS.length manifest) + nodeSize = total - 96 - manifestSize + when (total > toInteger (maxBound :: Word64)) (fail "bundle exceeds portable offset space") + pending <- newIORef (0, []) + let flush = do + (count, parts) <- readIORef pending + when (count > 0) $ do + -- Drop the accumulated parts before calling an arbitrary sink. No + -- emitted chunk history is retained across acknowledgements. + writeIORef pending (0, []) + sink (BS.concat (reverse parts)) + feed bytes = unless (BS.null bytes) $ do + (count, parts) <- readIORef pending + let (part, rest) = BS.splitAt (bound - count) bytes + count' = count + BS.length part + writeIORef pending (count', part : parts) + when (count' == bound) flush + feed rest + node value = do + let payload = serializeBundleNode value + feed (encode32 (fromIntegral (BS.length payload))) + feed payload + feed (encodeHeader bundleMajorVersion bundleMinorVersion 2 0 32) + feed (encodeSectionEntry sectionManifest 1 flagCritical compressionNone 96 (fromInteger manifestSize)) + feed (encodeSectionEntry sectionNodes 1 flagCritical compressionNone (fromInteger (96 + manifestSize)) (fromInteger nodeSize)) + feed manifest + feed (encode64 (fromIntegral (Seq.length (bundleNodes bundle)))) + traverse_ node (bundleNodes bundle) + flush + decodeBundle :: ByteString -> Either String Bundle decodeBundle bs | BS.take (BS.length bundleMagic) bs /= bundleMagic = Left "invalid magic" @@ -700,6 +750,94 @@ decodeBundle bs , bundleManifestBytes = manifestBytes } +-- Bounded transport decoder for an already authenticated immutable snapshot. +-- Limits are explicit arguments, not limits on the historical whole-value API. +-- Retains the indexed node table (and bounded manifest/directory metadata), never +-- the complete encoded input. Offset arithmetic is Integer before any narrowing. +decodeBundleSnapshot :: Int -> Int -> Snapshot -> IO (Either String Bundle) +decodeBundleSnapshot sectionLimit manifestLimit snapshot = runExceptT $ do + cache <- liftIO (newIORef (0, BS.empty)) + let size = snapshotLength snapshot + checkedRange offset count = when (offset < 0 || count < 0 || offset + count > size) $ + throwE "section extends beyond bundle end" + small offset count = do + checkedRange offset (toInteger count) + (base, cached) <- liftIO (readIORef cache) + bytes <- if offset >= base && offset + toInteger count <= base + toInteger (BS.length cached) + then pure (BS.take count (BS.drop (fromInteger (offset - base)) cached)) + else do + fresh <- liftIO (readSnapshot snapshot offset 4096) + liftIO (writeIORef cache (offset, fresh)) + pure (BS.take count fresh) + when (BS.length bytes /= count) (throwE "snapshot truncated") + pure bytes + decoded = either throwE pure + -- Manifest bytes are bounded by manifestLimit; each transport read <=4096. + boundedBytes offset count = BS.concat <$> go offset count + where + go _ 0 = pure [] + go pos remaining = do + let n = min 4096 remaining + bytes <- small pos n + (bytes :) <$> go (pos + toInteger n) (remaining - n) + when (sectionLimit < 0 || manifestLimit < 0) (throwE "invalid snapshot metadata limits") + header <- small 0 headerLength + (major, minor, count, _flags, directory) <- decoded (decodePortableHeader header) + when (major /= bundleMajorVersion) (throwE "unsupported bundle major version") + when (toInteger count > toInteger sectionLimit) (throwE "section count exceeds limit") + checkedRange (toInteger directory) (toInteger count * toInteger sectionEntryLength) + entries <- mapM (\index -> do + bytes <- small (toInteger directory + index * toInteger sectionEntryLength) sectionEntryLength + parsed <- decoded (decodeSectionEntries 1 bytes) + case parsed of + [entry] -> pure entry + _ -> throwE "invalid section directory") [0 .. toInteger count - 1] + mapM_ (decoded . rejectUnknownCritical) entries + manifestEntry <- decoded (requireSection sectionManifest entries) + nodesEntry <- decoded (requireSection sectionNodes entries) + let check entry = do + when (seCompression entry /= compressionNone) (throwE "unsupported compression codec") + checkedRange (toInteger (seOffset entry)) (toInteger (seLength entry)) + check manifestEntry + check nodesEntry + when (toInteger (seLength manifestEntry) > toInteger manifestLimit) (throwE "manifest exceeds limit") + manifestBytes <- boundedBytes (toInteger (seOffset manifestEntry)) (fromIntegral (seLength manifestEntry)) + manifest <- decoded (decodeManifest manifestBytes) + when (treeNodePayload (manifestTree manifest) /= "arboricx.indexed.payload.v1") $ + throwE "manifest does not use indexed payload" + let start = toInteger (seOffset nodesEntry) + end = start + toInteger (seLength nodesEntry) + nodeBytes offset count = do + when (offset + toInteger count > end) (throwE "truncated node section") + small offset count + nodes remaining offset !acc + | remaining == (0 :: Word64) = do + when (offset /= end) (throwE "trailing bytes after node section") + pure acc + | otherwise = do + lengthBytes <- nodeBytes offset 4 + (countBytes, _) <- decoded (decode32be "payload_len" lengthBytes) + -- Fixed-size payloads: reject huge lengths before allocating/reading. + when (countBytes `notElem` [1,5,9]) (throwE "invalid node payload length") + bytes <- nodeBytes (offset + 4) (fromIntegral countBytes) + -- Force child IDs now, not when verification traverses the finished + -- table: otherwise deferred payload decoding retains input blocks. + !node <- decoded (deserializeBundleNode bytes) + nodes (remaining - 1) (offset + 4 + toInteger countBytes) (acc |> node) + countBytes <- nodeBytes start 8 + (nodeCount, _) <- decoded (decode64be "node_count" countBytes) + when (toInteger nodeCount > (end - start - 8) `div` 5 || + toInteger nodeCount > toInteger (maxBound :: Word32)) $ + throwE "node count exceeds section or index bounds" + table <- nodes nodeCount (start + 8) Seq.empty + pure Bundle + { bundleVersion = major * 1000 + minor + , bundleRoots = map rootIndex (manifestRoots manifest) + , bundleNodes = table + , bundleManifest = manifest + , bundleManifestBytes = manifestBytes + } + -- --------------------------------------------------------------------------- -- Container encoding / decoding -- --------------------------------------------------------------------------- diff --git a/test/IOBoundaryTests.hs b/test/IOBoundaryTests.hs @@ -1,122 +1,28 @@ +-- The old path-grant sandbox is retired, not silently relaxed. Current resource +-- ownership/lease/cancellation controls live in the IOV2 suites; OS permissions +-- and real TCP/filesystem protocols also have independent CLI acceptance. module IOBoundaryTests (ioBoundaryTests) where import IODriver import Runtime -import Control.Exception (bracket, try, IOException) import Control.Monad (forM_) -import Control.Concurrent (threadDelay) -import System.Posix.Files (createNamedPipe) -import System.Posix.IO (openFd, closeFd, defaultFileFlags, OpenMode(ReadOnly), OpenFileFlags(..), fdRead) import qualified Data.ByteString as BS -import qualified Network.Socket as NS -import System.Directory (createDirectory, createDirectoryLink, doesFileExist) -import System.IO.Error (isEOFError, isFullError, isAlreadyInUseError) +import System.Directory (listDirectory) import System.IO.Temp (withSystemTempDirectory) -import qualified System.Timeout as Timeout import Test.Tasty import Test.Tasty.HUnit ioBoundaryTests :: TestTree -ioBoundaryTests = testGroup "IO ownership and path boundaries" - [ testCase "new file through escaping parent symlink is denied" $ - withSystemTempDirectory "io-path" $ \dir -> do - let allowed = dir ++ "/allowed" - outside = dir ++ "/outside" - target = allowed ++ "/link/new" - createDirectory allowed - createDirectory outside - createDirectoryLink outside (allowed ++ "/link") - reply <- runIO (IOPermissions [allowed] [allowed] False False) - (envelope (act 21 (Fork (ofString target) (ofString "escape")))) - reply @?= Right (Fork Leaf (Fork (ofString "permission denied") Leaf)) - doesFileExist (outside ++ "/new") >>= (@?= False) - , testCase "new file below allowed parent still succeeds" $ - withSystemTempDirectory "io-path" $ \dir -> do - let target = dir ++ "/new" - reply <- runIO (IOPermissions [dir] [dir] False False) - (envelope (act 21 (Fork (ofString target) (ofString "ok")))) - reply @?= Right (Fork (Stem Leaf) (Fork Leaf Leaf)) - BS.readFile target >>= (@?= "ok") - , testCase "CAS validation and permission denial do not force publication trees" $ - forM_ [36, 52] $ \tag -> do - let tree = error "denied tree was forced" - request path pin = envelope (act tag (Fork path (Fork pin tree))) - denied message = Right (Fork Leaf (Fork (ofString message) Leaf)) - runIO defaultPerms (request (ofString "/denied") (ofString (replicate 64 '0'))) - >>= (@?= denied "permission denied") - runIO defaultPerms (request (ofString "/denied") (ofString "BAD")) - >>= (@?= denied "invalid CAS digest") - runIO defaultPerms (request (Stem Leaf) (error "unused digest was forced")) - >>= (@?= denied "invalid string") - , testCase "root completion closes unclosed listening sockets" $ socketExit (act 0 Leaf) (Just (Right Leaf)) - , testCase "abort closes unclosed listening sockets" $ socketExit (act 2 (ofString "stop")) (Just (Left "execution aborted: stop")) - , testCase "timeout closes unclosed listening sockets" $ socketExit (act 63 (ofNumber 60000)) Nothing - , testCase "root completion joins blocked file workers" $ workerExit (act 0 Leaf) (Just (Right (Leaf, Leaf))) - , testCase "abort joins blocked file workers" $ workerExit (act 2 (ofString "stop")) (Just (Left "execution aborted: stop")) - , testCase "timeout joins blocked file workers" $ workerExit (act 63 (ofNumber 60000)) Nothing - , testCase "timeout interrupts blocked socket accept" $ socketExit - (act 74 (Fork (ofString "sock") (ofNumber 0))) Nothing +ioBoundaryTests = testGroup "Retired protocol effect boundary" + [ testCase "v1 writes, removal and publication cannot modify filesystem" $ + withSystemTempDirectory "retired-io" $ \directory -> do + let path = directory ++ "/existing" + BS.writeFile path (BS.pack [0,255,10]) + forM_ [21,25,26,33,36,52,60,61,62,63] $ \tag -> do + let payload = Fork (ofString path) (ofString "replacement") + action = Fork (ofNumber tag) payload + envelope = Fork (ofString "tricuIO") (Fork (ofNumber 1) action) + runIO envelope >>= (@?= Left "Unsupported tricuIO version") + BS.readFile path >>= (@?= BS.pack [0,255,10]) + listDirectory directory >>= (@?= ["existing"]) ] - --- The reader consumes one byte to prove the OS writer started, then stops --- draining until run exit. Cancellation must stop the blocked writer: draining --- afterward must reach EOF without receiving the entire payload. -workerExit :: T -> Maybe (Either String (T, T)) -> Assertion -workerExit final expected = withSystemTempDirectory "io-worker" $ \dir -> do - let fifo = dir ++ "/fifo" - size = 262144 - createNamedPipe fifo 0o600 - bracket (openFd fifo ReadOnly (defaultFileFlags { nonBlock = True })) closeFd $ \reader -> do - let readSome count = do - result <- try (fdRead reader count) - case result of - Right (bytes, _) -> pure bytes - Left (err :: IOException) | isEOFError err -> pure "" - | isFullError err -> threadDelay 1000 >> readSome count - | otherwise -> ioError err - started = do - bytes <- readSome 1 - if null bytes then threadDelay 1000 >> started else pure () - hook = do - ready <- Timeout.timeout 5000000 started - ready @?= Just () - drain total = do - bytes <- readSome 65536 - if null bytes then pure total else drain (total + length bytes) - payload = ofList (replicate size (ofNumber 120)) - action = thenDo (act 60 (act 22 (Fork (ofString fifo) payload))) $ - thenDo (act 62 Leaf) final - outcome <- Timeout.timeout 6000000 (runIOWithYieldHook hook unsafePerms Leaf Leaf (envelope action)) - outcome @?= expected - drained <- Timeout.timeout 5000000 (drain 1) - case drained of - Nothing -> assertFailure "writer did not terminate" - Just count -> assertBool "worker continued writing after run exit" (count < size) - --- SockId 0 belongs to this run. The independent probe keeps no driver handles. -socketExit :: T -> Maybe (Either String T) -> Assertion -socketExit final expected = do - port <- bracket (NS.socket NS.AF_INET NS.Stream NS.defaultProtocol) NS.close $ \sock -> do - NS.bind sock (NS.SockAddrInet 0 (NS.tupleToHostAddress (127,0,0,1))) - NS.SockAddrInet p _ <- NS.getSocketName sock - pure p - let handle = Fork (ofString "sock") (ofNumber 0) - action = thenDo (act 70 Leaf) $ thenDo - (act 72 (Fork handle (Fork (ofString "127.0.0.1") (ofNumber (fromIntegral port))))) $ - thenDo (act 73 (Fork handle (ofNumber 4))) (thenDo (act 62 Leaf) final) - hook = bracket (NS.socket NS.AF_INET NS.Stream NS.defaultProtocol) NS.close $ \probe -> do - bound <- try (NS.bind probe (NS.SockAddrInet port (NS.tupleToHostAddress (127,0,0,1)))) - case bound of - Left (err :: IOException) -> assertBool (show err) (isAlreadyInUseError err) - Right () -> assertFailure "driver did not bind its listening socket" - outcome <- Timeout.timeout 100000 (runIOWithYieldHook hook unsafePerms Leaf Leaf (envelope action)) - fmap (fmap fst) outcome @?= expected - bracket (NS.socket NS.AF_INET NS.Stream NS.defaultProtocol) NS.close $ \probe -> - NS.bind probe (NS.SockAddrInet port (NS.tupleToHostAddress (127,0,0,1))) - -act :: Integer -> T -> T -act tag = Fork (ofNumber tag) -envelope :: T -> T -envelope action = Fork (ofString "tricuIO") (Fork (ofNumber 1) action) -thenDo :: T -> T -> T -thenDo action next = act 1 (Fork action (Fork Leaf next)) diff --git a/test/IODriverTests.hs b/test/IODriverTests.hs @@ -1,1299 +1,33 @@ module IODriverTests (ioDriverTests) where -import ScriptedEffects (runScriptedEffects) -import Data.Text (pack) -import Data.Text.Encoding (encodeUtf8) -import Runtime -import ReferenceFixtures (fixtureExports) -import PortableTestCompiler (compileFixtureDefinitions) import IODriver -import Control.Exception (bracket) +import Runtime import Control.Monad (forM_) -import Data.IORef -import Data.List (isInfixOf) -import qualified Data.ByteString as BS -import qualified Data.Map as Map -import qualified Network.Socket as NS -import qualified System.Timeout as Timeout -import System.Directory (createDirectory, doesFileExist, doesDirectoryExist) -import System.IO.Temp (withSystemTempDirectory) import Test.Tasty import Test.Tasty.HUnit -ioDriverTests :: TestTree -ioDriverTests = testGroup "IO driver tests" - [ testCase "abortIO terminates before the pending Bind action" $ - withSystemTempDirectory "tricu-io-abort" $ \dir -> do - let output = dir ++ "/after" - action = envelope (thenAction (actionT 2 (ofString "stop")) - (actionT 21 (Fork (ofString output) (ofString "unexpected")))) - outcome <- runIO unsafePerms action - outcome @?= Left "execution aborted: stop" - exists <- doesFileExist output - exists @?= False - - , testCase "child abort terminates an awaiting execution" $ do - let source = "main = io (bind (fork (bind yield (_ : abortIO \"child\"))) (h : bind (await h) (_ : pure \"unexpected\")))" - action <- compileIO source - outcome <- Timeout.timeout 2000000 (runIO unsafePerms action) - outcome @?= Just (Left "execution aborted: child") - - , testCase "yield observation preserves result and explicit state" $ do - count <- newIORef (0 :: Int) - let action = envelope (thenAction (actionT 41 (ofNumber 7)) - (thenAction (actionT 62 Leaf) (thenAction (actionT 62 Leaf) (actionT 0 (ofNumber 9))))) - observed <- runIOWithYieldHook (modifyIORef' count (+ 1)) unsafePerms Leaf Leaf action - ordinary <- runIOWith unsafePerms Leaf Leaf action - observed @?= ordinary - observed @?= Right (ofNumber 9, ofNumber 7) - readIORef count >>= (@?= 2) - samples <- newIORef [] - profiled <- runIOWithYieldStatsHook (\sample -> modifyIORef' samples (sample :)) - unsafePerms Leaf Leaf action - profiled @?= ordinary - readIORef samples >>= (@?= [YieldStats 1 0 1, YieldStats 1 0 1]) - - , testCase "application Result keeps structured errors as data" $ do - let value = Fork Leaf (Fork (ofString "application-error") Leaf) - action = envelope (actionT 0 value) - runIO unsafePerms action >>= (@?= Right value) - - , testCase "readFile through onReadFile returns file contents" $ - withSystemTempDirectory "tricu-io-read" $ \dir -> do - let sourcePath = dir ++ "/input.txt" - writeFile sourcePath "abc123" - final <- runIOSource $ - unlines - [ "main = io (onReadFile \"" ++ sourcePath ++ "\"" - , " (err rest : pure \"read failed\")" - , " (contents rest : pure contents))" - ] - final @?= ofString "abc123" - - , testCase "readFile error path returns explicit error branch" $ - withSystemTempDirectory "tricu-io-read-missing" $ \dir -> do - let sourcePath = dir ++ "/missing.txt" - final <- runIOSource $ - unlines - [ "main = io (onReadFile \"" ++ sourcePath ++ "\"" - , " (err rest : pure \"read failed\")" - , " (contents rest : pure contents))" - ] - final @?= ofString "read failed" - - , testCase "chains multiple readFile actions through Result-aware helper" $ - withSystemTempDirectory "tricu-io-chain" $ \dir -> do - let firstPath = dir ++ "/first.txt" - secondPath = dir ++ "/second.txt" - writeFile firstPath "abc" - writeFile secondPath "def" - final <- runIOSource $ - unlines - [ "main = io (onReadFile \"" ++ firstPath ++ "\"" - , " (err rest : pure \"first read failed\")" - , " (first rest : onReadFile \"" ++ secondPath ++ "\"" - , " (err rest : pure \"second read failed\")" - , " (second rest : pure (append first second))))" - ] - final @?= ofString "abcdef" - - -- Monad law tests - , testCase "left identity: bind (pure x) f == f x" $ do - left <- runIOSource $ - unlines - [ "f = x : pure (append x \"!\")" - , "main = io (bind (pure \"abc\") f)" - ] - right <- runIOSource $ - unlines - [ "f = x : pure (append x \"!\")" - , "main = io (f \"abc\")" - ] - left @?= right - left @?= ofString "abc!" - - , testCase "right identity: bind m pure == m" $ - withSystemTempDirectory "tricu-io-right-id" $ \dir -> do - let path = dir ++ "/input.txt" - writeFile path "abc" - left <- runIOSource $ - unlines - [ "main = io (bind (readFile \"" ++ path ++ "\")" - , " (result : pure result))" - ] - right <- runAction (actionT 20 (ofString path)) - left @?= right - left @?= ioOkResult (ofString "abc") - - , testCase "associativity: bind (bind m f) g == bind m (x : bind (f x) g)" $ - withSystemTempDirectory "tricu-io-assoc" $ \dir -> do - let path = dir ++ "/input.txt" - writeFile path "abc" - left <- runIOSource $ - unlines - [ "m = readFile \"" ++ path ++ "\"" - , "f = result : matchResult (err rest : pure \"read failed\") (contents rest : pure (append contents \"-f\")) result" - , "g = value : pure (append value \"-g\")" - , "main = io (bind (bind m f) g)" - ] - right <- runIOSource $ - unlines - [ "m = readFile \"" ++ path ++ "\"" - , "f = result : matchResult (err rest : pure \"read failed\") (contents rest : pure (append contents \"-f\")) result" - , "g = value : pure (append value \"-g\")" - , "main = io (bind m (x : bind (f x) g))" - ] - left @?= right - left @?= ofString "abc-f-g" - - , testCase "associativity preserves error flow" $ - withSystemTempDirectory "tricu-io-assoc-err" $ \dir -> do - let missingPath = dir ++ "/missing.txt" - left <- runIOSource $ - unlines - [ "m = readFile \"" ++ missingPath ++ "\"" - , "f = result : matchResult (err rest : pure \"handled\") (contents rest : pure (append contents \"-ok\")) result" - , "g = value : pure (append value \"-g\")" - , "main = io (bind (bind m f) g)" - ] - right <- runIOSource $ - unlines - [ "m = readFile \"" ++ missingPath ++ "\"" - , "f = result : matchResult (err rest : pure \"handled\") (contents rest : pure (append contents \"-ok\")) result" - , "g = value : pure (append value \"-g\")" - , "main = io (bind m (x : bind (f x) g))" - ] - left @?= right - left @?= ofString "handled-g" - - , testCase "bind defers continuation until left action completes" $ - withSystemTempDirectory "tricu-io-lazy-k" $ \dir -> do - let path = dir ++ "/created.txt" - final <- runIOSource $ - unlines - [ "main = io (bind (writeFile \"" ++ path ++ "\" \"created\")" - , " (_ : readFile \"" ++ path ++ "\"))" - ] - final @?= ioOkResult (ofString "created") - - -- Primitive effect shape tests - , testCase "readFile without continuation returns Result" $ - withSystemTempDirectory "tricu-io-raw-read" $ \dir -> do - let path = dir ++ "/input.txt" - writeFile path "abc" - final <- runAction (actionT 20 (ofString path)) - final @?= ioOkResult (ofString "abc") - - , testCase "writeFile then readFile executes exactly once" $ - withSystemTempDirectory "tricu-io-once" $ \dir -> do - let path = dir ++ "/test.txt" - final <- runIOSource $ - unlines - [ "main = io (bind (writeFile \"" ++ path ++ "\" \"abc\")" - , " (_ : readFile \"" ++ path ++ "\"))" - ] - final @?= ioOkResult (ofString "abc") - - , testCase "sequencing order is left-to-right" $ - withSystemTempDirectory "tricu-io-order" $ \dir -> do - let path = dir ++ "/test.txt" - final <- runIOSource $ - unlines - [ "main = io (bind (writeFile \"" ++ path ++ "\" \"a\")" - , " (_ : bind (writeFile \"" ++ path ++ "\" \"ab\")" - , " (_ : readFile \"" ++ path ++ "\")))" - ] - final @?= ioOkResult (ofString "ab") - - , testCase "thenIO sequences two actions and discards first result" $ - withSystemTempDirectory "tricu-io-then" $ \dir -> do - let path = dir ++ "/test.txt" - final <- runIOSource $ - unlines - [ "main = io (thenIO (writeFile \"" ++ path ++ "\" \"x\")" - , " (readFile \"" ++ path ++ "\"))" - ] - final @?= ioOkResult (ofString "x") - - , testCase "bind does not short-circuit on readFile error" $ - withSystemTempDirectory "tricu-io-no-short" $ \dir -> do - let path = dir ++ "/missing.txt" - final <- runIOSource $ - unlines - [ "main = io (bind (readFile \"" ++ path ++ "\")" - , " (result : pure \"continued\"))" - ] - final @?= ofString "continued" - - , testCase "mapIO transforms pure value" $ do - final <- runIOSource $ - unlines - [ "main = io (mapIO (pure \"abc\") (x : append x \"!\"))" - ] - final @?= ofString "abc!" - - -- Malformed action tests - , testCase "unknown IO action tag returns err result" $ do - final <- runAction (actionT 99 Leaf) - final @?= ioErrResult "invalid action" - - , testCase "malformed Bind returns err result" $ do - final <- runAction (actionT 1 Leaf) - final @?= ioErrResult "invalid action" - - , testCase "malformed ReadFile payload returns err result" $ do - final <- runAction (actionT 20 (Stem Leaf)) - final @?= ioErrResult "invalid string" - - -- Permission tests - , testCase "allowed read path succeeds" $ - withSystemTempDirectory "tricu-io-allowed" $ \dir -> do - let path = dir ++ "/allowed.txt" - writeFile path "allowed" - let perms = defaultPerms { allowRead = [path] } - result <- fmap fst $ runActionWith perms Leaf Leaf (actionT 20 (ofString path)) - result @?= ioOkResult (ofString "allowed") - - , testCase "readFile denied path returns err result" $ - withSystemTempDirectory "tricu-io-read-denied" $ \dir -> do - let allowedPath = dir ++ "/allowed.txt" - deniedPath = dir ++ "/denied.txt" - writeFile allowedPath "allowed" - writeFile deniedPath "denied" - let perms = defaultPerms { allowRead = [allowedPath] } - result <- fmap fst $ runActionWith perms Leaf Leaf (actionT 20 (ofString deniedPath)) - result @?= ioErrResult "permission denied" - - , testCase "writeFile denied path returns err result" $ - withSystemTempDirectory "tricu-io-write-denied" $ \dir -> do - let allowedPath = dir ++ "/allowed.txt" - deniedPath = dir ++ "/denied.txt" - let perms = defaultPerms { allowWrite = [allowedPath] } - result <- fmap fst $ runActionWith perms Leaf Leaf (actionT 21 (Fork (ofString deniedPath) (ofString "x"))) - result @?= ioErrResult "permission denied" - - , testCase "path prefix does not allow prefix bypass" $ - withSystemTempDirectory "tricu-io-prefix" $ \dir -> do - let allowedDir = dir ++ "/foo" - bypassPath = dir ++ "/foobar/secret.txt" - createDirectory allowedDir - createDirectory (dir ++ "/foobar") - writeFile bypassPath "secret" - let perms = defaultPerms { allowRead = [allowedDir] } - result <- fmap fst $ runActionWith perms Leaf Leaf (actionT 20 (ofString bypassPath)) - result @?= ioErrResult "permission denied" - - -- Pure test - , testCase "pure performs no effects" $ do - final <- runAction (actionT 0 (ofString "abc")) - final @?= ofString "abc" - - -- Reader tests - , testCase "ask returns initial environment" $ do - final <- runIOSourceWithEnv unsafePerms (ofString "dev") $ - unlines - [ "main = io (bind ask (env : pure env))" - ] - final @?= ofString "dev" - - , testCase "local transforms environment" $ do - final <- runIOSourceWithEnv unsafePerms (ofString "root") $ - unlines - [ "main = io (local (env : append env \"-local\") (bind ask (env : pure env)))" - ] - final @?= ofString "root-local" - - , testCase "local restores environment afterward" $ do - final <- runIOSourceWithEnv unsafePerms (ofString "root") $ - unlines - [ "main = io (bind ask (before :" - , " bind (local (env : append env \"-local\") (bind ask (env : pure env))) (inside :" - , " bind ask (after :" - , " pure (pair before (pair inside after))))))" - ] - final @?= Fork (ofString "root") (Fork (ofString "root-local") (ofString "root")) - - , testCase "nested local composes correctly" $ do - final <- runIOSourceWithEnv unsafePerms (ofString "root") $ - unlines - [ "f = x : append x \"-f\"" - , "g = x : append x \"-g\"" - , "main = io (bind" - , " (local f (local g (bind ask (env : pure env))))" - , " (inner :" - , " bind ask (after :" - , " pure (pair inner after))))" - ] - final @?= Fork (ofString "root-f-g") (ofString "root") - - , testCase "local result passes through bind correctly" $ do - final <- runIOSourceWithEnv unsafePerms (ofString "root") $ - unlines - [ "main = io (bind" - , " (local (env : append env \"-local\") (pure \"value\"))" - , " (x : pure x))" - ] - final @?= ofString "value" - - , testCase "IO inside local uses transformed environment and restores after" $ do - final <- runIOSourceWithEnv unsafePerms (ofString "root") $ - unlines - [ "main = io (bind" - , " (local (env : append env \"-local\")" - , " (bind ask (env : pure env)))" - , " (result :" - , " bind ask (after :" - , " pure (pair result after))))" - ] - final @?= Fork (ofString "root-local") (ofString "root") - - , testCase "local does not affect outer bind continuation" $ do - final <- runIOSourceWithEnv unsafePerms (ofString "root") $ - unlines - [ "main = io (bind" - , " (local (env : append env \"-local\") (pure \"x\"))" - , " (_ : bind ask (env : pure env)))" - ] - final @?= ofString "root" - - , testCase "local environment persists across inner binds" $ do - final <- runIOSourceWithEnv unsafePerms (ofString "root") $ - unlines - [ "main = io (local (env : append env \"-local\")" - , " (bind (pure t) (_ :" - , " bind ask (env : pure env))))" - ] - final @?= ofString "root-local" - - , testCase "local restores environment when scoped action returns error value" $ do - final <- runIOSourceWithEnv defaultPerms (ofString "root") $ - unlines - [ "main = io (bind" - , " (local (env : append env \"-local\") (readFile \"definitely-missing.txt\"))" - , " (_ : bind ask (env : pure env)))" - ] - final @?= ofString "root" - - -- State tests - , testCase "get returns initial state" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf (ofNumber 42) $ - unlines - [ "main = io (bind get (s : pure s))" - ] - final @?= ofNumber 42 - st @?= ofNumber 42 - - , testCase "put updates state" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf (ofNumber 0) $ - unlines - [ "main = io (bind (put 100) (_ : bind get (s : pure s)))" - ] - final @?= ofNumber 100 - st @?= ofNumber 100 - - , testCase "state persists through bind" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf (ofNumber 5) $ - unlines - [ "main = io (bind get (s1 :" - , " bind (put (succ s1)) (_ :" - , " bind get (s2 :" - , " pure (pair s1 s2)))))" - ] - final @?= Fork (ofNumber 5) (ofNumber 6) - st @?= ofNumber 6 - - , testCase "local does not restore state" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf (ofNumber 0) $ - unlines - [ "main = io (bind (put 10) (_ :" - , " bind (local (env : env) (put 20)) (_ :" - , " bind get (s :" - , " pure s))))" - ] - final @?= ofNumber 20 - st @?= ofNumber 20 - - , testCase "state and reader are independent" $ do - (final, st) <- runIOSourceWith unsafePerms (ofString "hello") (ofNumber 42) $ - unlines - [ "main = io (bind ask (env :" - , " bind get (s :" - , " pure (pair env s))))" - ] - final @?= Fork (ofString "hello") (ofNumber 42) - st @?= ofNumber 42 - - -- Async tests - , testCase "fork returns handle and await returns child value" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (fork (pure \"child\")) (h :" - , " await h))" - ] - final @?= ofString "child" - st @?= Leaf - - , testCase "main completion abandons unawaited child" $ do - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (fork (pure \"child\")) (_ :" - , " pure \"main\"))" - ] - final @?= ofString "main" - - , testCase "fork captures reader environment at fork point" $ do - (final, _) <- runIOSourceWith unsafePerms (ofString "root") Leaf $ - unlines - [ "main = io (local (env : append env \"-local\")" - , " (bind (fork (bind ask (env : pure env))) (h :" - , " await h)))" - ] - final @?= ofString "root-local" - - , testCase "fork inside local captures child env and parent restores env" $ do - (final, _) <- runIOSourceWith unsafePerms (ofString "root") Leaf $ - unlines - [ "main = io (bind" - , " (local (env : append env \"-local\")" - , " (fork (bind ask (env : pure env))))" - , " (h : bind ask (after :" - , " bind (await h) (child :" - , " pure (pair after child)))))" - ] - final @?= Fork (ofString "root") (ofString "root-local") - - , testCase "fork copies state and child state does not merge" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf (ofNumber 0) $ - unlines - [ "main = io (bind (put 1) (_ :" - , " bind (fork (bind (put 99) (_ : bind get (s : pure s)))) (h :" - , " bind (put 2) (_ :" - , " bind (await h) (childState :" - , " bind get (parentState :" - , " pure (pair childState parentState)))))))" - ] - final @?= Fork (ofNumber 99) (ofNumber 2) - st @?= ofNumber 2 - - , testCase "multiple awaiters receive same completed value" $ do - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (fork (pure \"done\")) (h :" - , " bind (await h) (a :" - , " bind (await h) (b :" - , " pure (pair a b)))))" - ] - final @?= Fork (ofString "done") (ofString "done") - - , testCase "self await returns async error" $ do - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (await (pair \"task\" 0))" - ] - final @?= ioErrResult "self await" - - , testCase "await invalid handle returns async error" $ do - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (await 123)" - ] - final @?= ioErrResult "invalid task handle" - - , testCase "yield returns unit and resumes continuation" $ do - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind yield (_ : pure \"after\"))" - ] - final @?= ofString "after" - - , testCase "sleep resumes continuation" $ do - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (sleep 1) (_ : pure \"awake\"))" - ] - final @?= ofString "awake" - - , testCase "await waits for sleeping child" $ do - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (fork (bind (sleep 1) (_ : pure \"awake\"))) (h :" - , " await h))" - ] - final @?= ofString "awake" - - , testCase "await waits for sleeping child and returns child value" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (fork (bind (sleep 1) (_ : pure \"child done\"))) (h :" - , " await h))" - ] - final @?= ofString "child done" - st @?= Leaf - - , testCase "sleep inside bind resumes as unit" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (sleep 1) (_ : pure \"awake\"))" - ] - final @?= ofString "awake" - st @?= Leaf - - , testCase "fork await returns child value" $ do - (final, st) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (fork (pure \"child done\")) (h :" - , " await h))" - ] - final @?= ofString "child done" - st @?= Leaf - - -- Scheduler hardening tests - , testCase "runIO rejects non-IO tree with sentinel error" $ do - result <- runIO unsafePerms (ofString "not an io program") - case result of - Left _ -> return () - Right _ -> assertFailure "Expected Left for invalid sentinel" - - , testCase "cyclic await returns error instead of hanging" $ do - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "main = io (bind (fork (await (pair \"task\" 0))) (h :" - , " await h))" - ] - final @?= ioErrResult "cyclic await" - - , testCase "source file UTF-8 returns exact bytes and scalar characters" $ - withSystemTempDirectory "tricu-source-utf8" $ \dir -> do - let path = dir ++ "/source.tri" - samples = ["main = 42\n", "-- \233\8364\128512\nmain = \"\233\8364\128512\"\n", - "-- embedded \xfeff and decomposed e\x301\r\n", - "\0\x80\x7ff\x800\xd7ff\xe000\xffff\x10000\x10ffff"] - forM_ samples $ \text -> do - let bytes = encodeUtf8 (pack text) - BS.writeFile path bytes - let action = Fork (ofNumber 37) (ofString path) - program = Fork (ofString "tricuIO") (Fork (ofNumber 1) action) - actual <- runIO unsafePerms program - actual @?= Right (ioOkResult (Fork (ofBytes bytes) (ofString text))) - binary <- runIO unsafePerms - (Fork (ofString "tricuIO") (Fork (ofNumber 1) (Fork (ofNumber 20) (ofString path)))) - binary @?= Right (ioOkResult (ofBytes bytes)) - encoded <- runIO unsafePerms - (Fork (ofString "tricuIO") (Fork (ofNumber 1) (Fork (ofNumber 38) (ofString text)))) - encoded @?= Right (ioOkResult (ofBytes bytes)) - - , testCase "source UTF-8 rejects malformed sequences and initial BOM" $ - withSystemTempDirectory "tricu-source-invalid" $ \dir -> do - let path = dir ++ "/bad.tri" - malformed = [[0x80], [0xc0,0xaf], [0xc1,0xbf], [0xc2], [0xe2,0x82], - [0xf0,0x9f,0x98], [0xe0,0x80,0xaf], [0xed,0xa0,0x80], - [0xf0,0x80,0x80,0xaf], [0xf4,0x90,0x80,0x80], - [0xf5,0x80,0x80,0x80], [0xff], [0xc2,0x41], [0xef,0xbb,0xbf]] - forM_ malformed $ \bytes -> do - BS.writeFile path (BS.pack bytes) - actual <- runIO unsafePerms - (Fork (ofString "tricuIO") (Fork (ofNumber 1) (Fork (ofNumber 37) (ofString path)))) - case actual of - Right (Fork Leaf (Fork diagnostic Leaf)) -> case toString diagnostic of - Right message -> do - assertBool "source path in diagnostic" (path `isInfixOf` message) - assertBool "UTF-8 diagnostic" ("UTF-8" `isInfixOf` message) - _ -> assertFailure "non-string diagnostic" - _ -> assertFailure ("accepted malformed source: " ++ show bytes) - - , testCase "source read permission and scalar encoding controls" $ do - denied <- runIO defaultPerms - (Fork (ofString "tricuIO") (Fork (ofNumber 1) (Fork (ofNumber 37) (ofString "denied.tri")))) - case denied of - Right (Fork Leaf _) -> pure () - _ -> assertFailure "source read bypassed permission" - forM_ [0xd800, 0xdfff, 0x110000] $ \scalar -> do - actual <- runIO unsafePerms - (Fork (ofString "tricuIO") (Fork (ofNumber 1) - (Fork (ofNumber 38) (ofList [ofNumber scalar])))) - actual @?= Right (ioErrResult "invalid source scalar list") - - , testCase "source encoder rejects improper lists and noncanonical scalar naturals" $ do - forM_ [Stem Leaf, Fork (ofNumber 65) (Stem Leaf), - ofList [Fork Leaf Leaf], ofList [Fork (Stem Leaf) (Fork Leaf Leaf)]] $ \value -> do - actual <- runIO unsafePerms - (Fork (ofString "tricuIO") (Fork (ofNumber 1) (Fork (ofNumber 38) value))) - actual @?= Right (ioErrResult "invalid source scalar list") +frame :: Integer -> T -> T +frame version = Fork (ofString "tricuIO") . Fork (ofNumber version) - , testCase "writeBytes and readFile roundtrip binary data" $ - withSystemTempDirectory "tricu-io-bytes" $ \dir -> do - let path = dir ++ "/binary.bin" - final <- runIOSource $ - unlines - [ "main = io (bind (writeBytes \"" ++ path ++ "\" [(0) (255) (128) (1)])" - , " (_ : readFile \"" ++ path ++ "\"))" - ] - final @?= ioOkResult (ofBytes (BS.pack [0, 255, 128, 1])) - - , testCase "stress test: many concurrent sleepers complete promptly" $ do - let n = 5000 - (final, _) <- runIOSourceWith unsafePerms Leaf Leaf $ - unlines - [ "spawner = y (self n acc : if (equal? n 0) (pure acc) (bind (fork (sleep 1)) (h : self (pred n) (pair h acc))))" - , "awaitAll = y (self hs : matchList (pure \"done\") (h r : bind (await h) (_ : self r)) hs)" - , "main = io (bind (spawner " ++ show n ++ " t) (hs : awaitAll hs))" - ] - final @?= ofString "done" - - , testCase "long fork await loop does not leak" $ do - let awaited = actionT 1 (Fork (actionT 60 (actionT 0 (ofString "x"))) - (fixtureExports "io" Map.! "await")) - action = foldr (\_ next -> thenAction awaited next) (actionT 0 (ofString "done")) [1..200 :: Int] - (final, _) <- runActionWith unsafePerms Leaf Leaf action - final @?= ofString "done" - - , testGroup "Socket primitives" - [ testCase "socket returns ok result with valid handle" $ do - final <- runIOSource "main = io socket" - final @?= ioOkResult (Fork (ofString "sock") (ofNumber 0)) - - , testCase "closeSocket on invalid handle returns error" $ do - final <- runIOSource "main = io (closeSocket (pair \"sock\" 99999))" - final @?= ioErrResult "invalid socket handle" - - , testCase "bindSocket and listen succeed on loopback port 0" $ do - final <- runIOSource $ - unlines - [ "main = io (" - , " onOk socket (server rest :" - , " onOk (bindSocket server \"127.0.0.1\" 0) (_ rest :" - , " bind (listen server 1) (listenResult :" - , " pure listenResult))))" - ] - final @?= ioOkResult Leaf - - , testCase "connect to non-listening port returns error" $ do - final <- runIOSource "main = io (onOk socket (sock rest : connect sock \"127.0.0.1\" 1))" - case final of - Fork Leaf (Fork _ Leaf) -> return () - other -> assertFailure $ "Expected error result, got: " ++ show other - - , testCase "accept and recv receive bytes from forked client" $ - withFreePort $ \port -> do - final <- runIOSource $ - unlines - [ "client = port :" - , " onOk socket (sock rest :" - , " onOk (connect sock \"127.0.0.1\" port) (_ rest :" - , " send sock [104 105]))" - , "" - , "main = io (" - , " onOk socket (server rest :" - , " onOk (bindSocket server \"127.0.0.1\" " ++ show port ++ ") (_ rest :" - , " onOk (listen server 1) (_ rest :" - , " bind (fork (client " ++ show port ++ ")) (_ :" - , " onOk (accept server) (accepted rest :" - , " recv (fst accepted) 2))))))" - ] - final @?= ioOkResult (ofBytes (BS.pack [104, 105])) - - , testCase "client recv receives server response via accepted socket" $ - withFreePort $ \port -> do - final <- runIOSource $ - unlines - [ "serverTask = (server :" - , " onOk (accept server) (accepted rest :" - , " onOk (recv (fst accepted) 4) (msg rest :" - , " send (fst accepted) [112 111 110 103])))" - , "" - , "clientTask = (port :" - , " onOk socket (sock rest :" - , " onOk (connect sock \"127.0.0.1\" port) (_ rest :" - , " bind (send sock [112 105 110 103]) (_ :" - , " recv sock 4))))" - , "" - , "main = io (" - , " onOk socket (server rest :" - , " onOk (bindSocket server \"127.0.0.1\" " ++ show port ++ ") (_ rest :" - , " onOk (listen server 1) (_ rest :" - , " bind (fork (serverTask server)) (_ :" - , " clientTask " ++ show port ++ ")))))" - ] - final @?= ioOkResult (ofBytes (BS.pack [112, 111, 110, 103])) - - , testCase "recv on closed peer returns connection closed" $ - withFreePort $ \port -> do - final <- runIOSource $ - unlines - [ "clientTask = port :" - , " onOk socket (sock rest :" - , " onOk (connect sock \"127.0.0.1\" port) (_ rest :" - , " closeSocket sock))" - , "" - , "main = io (" - , " onOk socket (server rest :" - , " onOk (bindSocket server \"127.0.0.1\" " ++ show port ++ ") (_ rest :" - , " onOk (listen server 1) (_ rest :" - , " bind (fork (clientTask " ++ show port ++ ")) (_ :" - , " onOk (accept server) (accepted rest :" - , " bind (yield) (_ :" - , " recv (fst accepted) 1)))))))" - ] - final @?= ioErrResult "connection closed" - - , testCase "accept invalid socket handle returns error" $ do - final <- runIOSource "main = io (accept (pair \"sock\" 99999))" - final @?= ioErrResult "invalid socket handle" - - , testCase "recv invalid socket handle returns error" $ do - final <- runIOSource "main = io (recv (pair \"sock\" 99999) 1)" - final @?= ioErrResult "invalid socket handle" - - , testCase "send invalid socket handle returns error" $ do - final <- runIOSource "main = io (send (pair \"sock\" 99999) [(1)])" - final @?= ioErrResult "invalid socket handle" - - , testCase "socket conveniences close acquired handles on failures and preserve replies" $ do - let env = fixtureExports "socket" - let call name = foldl apply (env Map.! name) - sock = ofNumber 7 - primitive tag payload = Fork (ofNumber tag) payload - create = primitive 70 Leaf - close = primitive 71 sock - address = ofString "127.0.0.1" - port = ofNumber 9050 - bound = primitive 72 (Fork sock (Fork address port)) - listening = primitive 73 (Fork sock (ofNumber 128)) - connected = primitive 75 (Fork sock (Fork address port)) - acquired = parserOk sock (ofNumber 23) - ready = parserOk (ofNumber 19) (ofNumber 29) - failed = parserErr (ofString "denied") (ofString "opaque remainder") - closeFailed = parserErr (ofString "close failed") (ofNumber 41) - listener = call "listenSocket" [address, port, ofNumber 128] - client = call "connectTo" [address, port] - forM_ [listener, client] $ \action -> - runScriptedEffects action [(create, failed)] >>= (@?= failed) - runScriptedEffects listener [(create, acquired), (bound, ready), (listening, ready)] - >>= (@?= parserOk sock Leaf) - runScriptedEffects client [(create, acquired), (connected, ready)] - >>= (@?= parserOk sock (ofNumber 29)) - forM_ [ready, closeFailed] $ \closed -> do - runScriptedEffects listener [(create, acquired), (bound, failed), (close, closed)] - >>= (@?= failed) - runScriptedEffects listener [(create, acquired), (bound, ready), (listening, failed), (close, closed)] - >>= (@?= failed) - runScriptedEffects client [(create, acquired), (connected, failed), (close, closed)] - >>= (@?= failed) - - , testCase "listenSocket creates a live listener whose ownership transfers to the caller" $ do - final <- runIOSource "main = io (onOk_ (listenSocket \"127.0.0.1\" 0 1) (server : finally (getSocketName server) (closeSocket_ server)))" - case final of - Fork (Stem Leaf) (Fork value Leaf) -> case toNumber value of - Right port -> assertBool "positive bound port" (port > 0) - Left problem -> assertFailure problem - other -> assertFailure $ "expected bound port: " ++ show other - - , testCase "getSocketName returns positive port after bind 0" $ do - final <- runIOSource $ - unlines - [ "main = io (" - , " onOk socket (server rest :" - , " onOk (bindSocket server \"127.0.0.1\" 0) (_ rest :" - , " bind (getSocketName server) (nameResult :" - , " pure nameResult))))" - ] - case final of - Fork (Stem Leaf) (Fork val Leaf) -> - case toNumber val of - Right port | port > 0 -> return () - Right 0 -> assertFailure "Expected positive port, got 0" - Left _ -> assertFailure $ "Expected numeric port, got: " ++ show val - other -> assertFailure $ "Expected ok result, got: " ++ show other - - , testCase "connectTo creates connected socket" $ - withFreePort $ \port -> do - final <- runIOSource $ - unlines - [ "clientTask = port :" - , " onOk (connectTo \"127.0.0.1\" port) (client rest :" - , " onOk (send client [104 105]) (_ rest :" - , " pure t))" - , "" - , "main = io (" - , " onOk socket (server rest :" - , " onOk (bindSocket server \"127.0.0.1\" " ++ show port ++ ") (_ rest :" - , " onOk (listen server 1) (_ rest :" - , " bind (fork (clientTask " ++ show port ++ ")) (_ :" - , " onOk (accept server) (accepted rest :" - , " onOk (recv (fst accepted) 2) (msg rest :" - , " pure msg)))))))" - ] - final @?= ofBytes (BS.pack [104, 105]) - - , testCase "serveOnce handles a single client connection" $ - withFreePort $ \port -> do - final <- runIOSource $ - unlines - [ "echoHandler = (client peer :" - , " onOk (recv client 2) (msg rest :" - , " onOk (send client msg) (_ rest :" - , " pure t)))" - , "" - , "clientTask = (port :" - , " onOk socket (sock rest :" - , " onOk (connect sock \"127.0.0.1\" port) (_ rest :" - , " onOk (send sock [104 105]) (_ rest :" - , " onOk (recv sock 2) (msg rest :" - , " pure msg)))))" - , "" - , "main = io (" - , " onOk socket (server rest :" - , " onOk (bindSocket server \"127.0.0.1\" " ++ show port ++ ") (_ rest :" - , " onOk (listen server 1) (_ rest :" - , " bind (fork (serveOnce server echoHandler)) (_ :" - , " clientTask " ++ show port ++ ")))))" - ] - final @?= ofBytes (BS.pack [104, 105]) - - , testCase "finally preserves successful action result" $ do - final <- runIOSource $ - unlines - [ "main = io (finally (pure 42) (pure 99))" - ] - final @?= ofNumber 42 - - , testCase "finally runs cleanup after successful action" $ - withSystemTempDirectory "tricu-finally" $ \dir -> do - let cleanupPath = dir ++ "/cleanup.txt" - final <- runIOSource $ - unlines - [ "main = io (finally" - , " (pure 42)" - , " (writeFile \"" ++ cleanupPath ++ "\" \"cleaned\"))" - ] - final @?= ofNumber 42 - contents <- readFile cleanupPath - contents @?= "cleaned" - - , testCase "bracket passes acquired resource to use" $ do - final <- runIOSource $ - unlines - [ "main = io (bracket (pure (ok 41 7)) (_ : pure t) (r : pure (succ r)))" - ] - final @?= ofNumber 42 - - , testCase "bracket preserves successful use result over release result" $ do - final <- runIOSource $ - unlines - [ "main = io (bracket (pure (ok \"res\" 7)) (_ : pure 123) (_ : pure 99))" - ] - final @?= ofNumber 99 - - , testCase "bracket runs release on successful use" $ - withSystemTempDirectory "tricu-bracket" $ \dir -> do - let releasePath = dir ++ "/release.txt" - final <- runIOSource $ - unlines - [ "main = io (bracket" - , " (pure (ok \"" ++ releasePath ++ "\" 7))" - , " (path : writeFile path \"released\")" - , " (path : pure 99))" - ] - final @?= ofNumber 99 - contents <- readFile releasePath - contents @?= "released" - - , testCase "bracket passes acquired resource to release" $ - withSystemTempDirectory "tricu-bracket-release-resource" $ \dir -> do - let releasePath = dir ++ "/release.txt" - final <- runIOSource $ - unlines - [ "main = io (bracket" - , " (pure (ok \"" ++ releasePath ++ "\" 7))" - , " (path : writeFile path \"released\")" - , " (_ : pure 99))" - ] - final @?= ofNumber 99 - contents <- readFile releasePath - contents @?= "released" - - -- Directory and file management primitives - , testGroup "listDirectory" - [ testCase "listDirectory returns entry names" $ - withSystemTempDirectory "tricu-listdir" $ \dir -> do - writeFile (dir ++ "/a.txt") "a" - writeFile (dir ++ "/b.txt") "b" - final <- runIOSource $ - unlines - [ "main = io (onListDirectory \"" ++ dir ++ "\"" - , " (err rest : pure false)" - , " (entries rest :" - , " pure (pair (lExist? \"a.txt\" entries) (lExist? \"b.txt\" entries))))" - ] - final @?= Fork (Stem Leaf) (Stem Leaf) - - , testCase "listDirectory missing path returns does not exist" $ do - final <- runIOSource $ - unlines - [ "main = io (onListDirectory \"/nonexistent/path/12345\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "does not exist" - - , testCase "listDirectory on file returns not a directory" $ - withSystemTempDirectory "tricu-listdir-file" $ \dir -> do - let path = dir ++ "/file.txt" - writeFile path "x" - final <- runIOSource $ - unlines - [ "main = io (onListDirectory \"" ++ path ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "not a directory" - - , testCase "listDirectory denied path returns permission denied" $ - withSystemTempDirectory "tricu-listdir-denied" $ \dir -> do - let allowedDir = dir ++ "/allowed" - deniedDir = dir ++ "/denied" - createDirectory allowedDir - createDirectory deniedDir - let perms = defaultPerms { allowRead = [allowedDir] } - final <- runIOSourceWithPerms perms $ - unlines - [ "main = io (listDirectory \"" ++ deniedDir ++ "\")" - ] - final @?= ioErrResult "permission denied" - ] - - , testCase "listDirectory excludes dot entries" $ - withSystemTempDirectory "tricu-listdir-dot" $ \dir -> do - final <- runIOSource $ - unlines - [ "main = io (onListDirectory \"" ++ dir ++ "\"" - , " (err rest : pure false)" - , " (entries rest :" - , " pure (pair (lExist? \".\" entries) (lExist? \"..\" entries))))" - ] - final @?= Fork Leaf Leaf - - , testGroup "renameFile" - [ testCase "renameFile moves file atomically" $ - withSystemTempDirectory "tricu-rename" $ \dir -> do - let oldPath = dir ++ "/old.txt" - newPath = dir ++ "/new.txt" - writeFile oldPath "contents" - final <- runIOSource $ - unlines - [ "main = io (onRenameFile \"" ++ oldPath ++ "\" \"" ++ newPath ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "ok" - newExists <- doesFileExist newPath - oldExists <- doesFileExist oldPath - newExists @?= True - oldExists @?= False - - , testCase "renameFile missing source returns does not exist" $ do - final <- runIOSource $ - unlines - [ "main = io (onRenameFile \"/nonexistent/old.txt\" \"/nonexistent/new.txt\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "does not exist" - - , testCase "renameFile denied destination returns permission denied" $ - withSystemTempDirectory "tricu-rename-denied" $ \dir -> do - let allowedDir = dir ++ "/allowed" - deniedDir = dir ++ "/denied" - createDirectory allowedDir - createDirectory deniedDir - let oldPath = allowedDir ++ "/old.txt" - newPath = deniedDir ++ "/new.txt" - writeFile oldPath "contents" - let perms = defaultPerms { allowWrite = [allowedDir] } - final <- runIOSourceWithPerms perms $ - unlines - [ "main = io (renameFile \"" ++ oldPath ++ "\" \"" ++ newPath ++ "\")" - ] - final @?= ioErrResult "permission denied" - - , testCase "renameFile replaces existing destination atomically" $ - withSystemTempDirectory "tricu-rename-replace" $ \dir -> do - let oldPath = dir ++ "/old.txt" - newPath = dir ++ "/new.txt" - writeFile oldPath "new" - writeFile newPath "old" - final <- runIOSource $ - unlines - [ "main = io (onRenameFile \"" ++ oldPath ++ "\" \"" ++ newPath ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "ok" - readFile newPath >>= (@?= "new") - oldExists <- doesFileExist oldPath - oldExists @?= False - ] - - , testGroup "createDirectory" - [ testCase "createDirectory creates new directory" $ - withSystemTempDirectory "tricu-mkdir" $ \dir -> do - let newDir = dir ++ "/subdir" - final <- runIOSource $ - unlines - [ "main = io (onCreateDirectory \"" ++ newDir ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "ok" - exists <- doesDirectoryExist newDir - exists @?= True - - , testCase "createDirectory is idempotent for existing directory" $ - withSystemTempDirectory "tricu-mkdir-idempotent" $ \dir -> do - let existingDir = dir ++ "/exists" - createDirectory existingDir - final <- runIOSource $ - unlines - [ "main = io (onCreateDirectory \"" ++ existingDir ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "ok" - - , testCase "createDirectory on existing file returns already exists" $ - withSystemTempDirectory "tricu-mkdir-file" $ \dir -> do - let path = dir ++ "/file.txt" - writeFile path "x" - final <- runIOSource $ - unlines - [ "main = io (onCreateDirectory \"" ++ path ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "already exists" - - , testCase "createDirectory missing parent returns does not exist" $ do - final <- runIOSource $ - unlines - [ "main = io (onCreateDirectory \"/nonexistent/path/12345/sub\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "does not exist" - - , testCase "createDirectory denied path returns permission denied" $ - withSystemTempDirectory "tricu-mkdir-denied" $ \dir -> do - let allowedDir = dir ++ "/allowed" - deniedDir = dir ++ "/denied" - createDirectory allowedDir - createDirectory deniedDir - let perms = defaultPerms { allowWrite = [allowedDir] } - final <- runIOSourceWithPerms perms $ - unlines - [ "main = io (createDirectory \"" ++ deniedDir ++ "/new\")" - ] - final @?= ioErrResult "permission denied" - , testCase "createDirectory with file parent returns not a directory or does not exist" $ - withSystemTempDirectory "tricu-mkdir-file-parent" $ \dir -> do - let parentFile = dir ++ "/file" - child = parentFile ++ "/sub" - writeFile parentFile "x" - final <- runIOSource $ - unlines - [ "main = io (onCreateDirectory \"" ++ child ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "not a directory" - ] - - , testGroup "deleteFile" - [ testCase "deleteFile removes file" $ - withSystemTempDirectory "tricu-delete" $ \dir -> do - let path = dir ++ "/del.txt" - writeFile path "x" - final <- runIOSource $ - unlines - [ "main = io (onDeleteFile \"" ++ path ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "ok" - exists <- doesFileExist path - exists @?= False - - , testCase "deleteFile is idempotent for missing file" $ do - final <- runIOSource $ - unlines - [ "main = io (onDeleteFile \"/nonexistent/path/12345.txt\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "ok" - - , testCase "deleteFile on directory returns is a directory" $ - withSystemTempDirectory "tricu-delete-dir" $ \dir -> do - let subDir = dir ++ "/subdir" - createDirectory subDir - final <- runIOSource $ - unlines - [ "main = io (onDeleteFile \"" ++ subDir ++ "\"" - , " (err rest : pure err)" - , " (_ rest : pure \"ok\"))" - ] - final @?= ofString "is a directory" - - , testCase "deleteFile denied path returns permission denied" $ - withSystemTempDirectory "tricu-delete-denied" $ \dir -> do - let allowedDir = dir ++ "/allowed" - deniedDir = dir ++ "/denied" - createDirectory allowedDir - createDirectory deniedDir - let path = deniedDir ++ "/file.txt" - writeFile path "x" - let perms = defaultPerms { allowWrite = [allowedDir] } - final <- fmap fst $ runActionWith perms Leaf Leaf (actionT 26 (ofString path)) - final @?= ioErrResult "permission denied" - ] - - , testGroup "fileExists" - [ testCase "fileExists true for existing file" $ - withSystemTempDirectory "tricu-exists" $ \dir -> do - let path = dir ++ "/file.txt" - writeFile path "x" - final <- runIOSource $ - unlines - [ "main = io (onFileExists \"" ++ path ++ "\"" - , " (err rest : pure err)" - , " (exists rest : pure exists))" - ] - final @?= Stem Leaf - - , testCase "fileExists false for missing path" $ do - final <- runIOSource $ - unlines - [ "main = io (onFileExists \"/nonexistent/path/12345.txt\"" - , " (err rest : pure err)" - , " (exists rest : pure exists))" - ] - final @?= Leaf - - , testCase "fileExists denied path returns permission denied" $ - withSystemTempDirectory "tricu-exists-denied" $ \dir -> do - let allowedDir = dir ++ "/allowed" - deniedDir = dir ++ "/denied" - createDirectory allowedDir - createDirectory deniedDir - let path = deniedDir ++ "/file.txt" - writeFile path "x" - let perms = defaultPerms { allowRead = [allowedDir] } - final <- fmap fst $ runActionWith perms Leaf Leaf (actionT 27 (ofString path)) - final @?= ioErrResult "permission denied" - ] - - , testGroup "sha256Hex" - [ testCase "sha256Hex returns lowercase hex digest" $ do - final <- runIOSource $ - unlines - [ "main = io (onSha256Hex [(104) (105)]" - , " (err rest : pure err)" - , " (hex rest : pure hex))" - ] - final @?= ofString "8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4" - - , testCase "sha256Hex empty bytes returns empty digest" $ do - final <- runIOSource $ - unlines - [ "main = io (onSha256Hex []" - , " (err rest : pure err)" - , " (hex rest : pure hex))" - ] - final @?= ofString "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - - , testCase "sha256Hex hashes raw bytes" $ do - final <- runIOSource $ - unlines - [ "main = io (onSha256Hex [(0) (255) (1)]" - , " (err rest : pure err)" - , " (hex rest : pure hex))" - ] - final @?= ofString "47ffa3ea45a70b8a41c2c0825df323c00a8b7a01c1ea06083cc41dddcc001123" - ] - - , testGroup "currentTime" - [ testCase "currentTime returns a positive integer" $ do - final <- runIOSource $ - unlines - [ "main = io (onCurrentTime" - , " (err rest : pure 0)" - , " (v rest : pure v))" - ] - case toNumber final of - Right n | n > 1600000000 -> return () -- after ~Sep 2020 - Right n -> assertFailure $ "Expected recent timestamp, got: " ++ show n - Left err -> assertFailure $ "Expected number, got error: " ++ err - ] - ] +ioDriverTests :: TestTree +ioDriverTests = testGroup "Current IO protocol dispatch" + [ testCase "only v2 executes; historical inspection is inert" $ do + checkIOSentinel (frame 1 Leaf) @?= Right (1, Leaf) + forM_ [0,1,3,99,65535] $ \version -> + runIO (frame version (error "rejected protocol body demanded")) + >>= (@?= Left "Unsupported tricuIO version") + , testCase "all retired action numbers reject before their payloads" $ + forM_ [0..100] $ \tag -> + runIO (frame 1 (Fork (ofNumber tag) (error "retired payload demanded"))) + >>= (@?= Left "Unsupported tricuIO version") + , testCase "ordinary Result values are not terminal outcomes" $ do + let value = Fork Leaf (Fork (ofString "ordinary failure") Leaf) + runIO (frame 2 (Fork (ofNumber 0) value)) >>= (@?= Right value) + , testCase "malformed versions reject without touching action" $ do + forM_ [Stem Leaf, ofNumber 65536, Fork Leaf (Fork (Stem Leaf) (Fork Leaf Leaf))] $ \version -> + runIO (Fork (ofString "tricuIO") (Fork version (error "invalid version body demanded"))) + >>= (@?= Left "invalid IO envelope version") + , testCase "bare computations are not guessed or retried" $ do + result <- runIO (Fork (ofNumber 0) Leaf) + assertBool "bare computation rejected" (case result of Left _ -> True; _ -> False) ] - -withFreePort :: (Int -> IO a) -> IO a -withFreePort action = - bracket - (NS.socket NS.AF_INET NS.Stream NS.defaultProtocol) - NS.close - (\s -> do - NS.setSocketOption s NS.ReuseAddr 1 - NS.bind s (NS.SockAddrInet 0 (NS.tupleToHostAddress (127, 0, 0, 1))) - port <- NS.socketPort s - action (fromIntegral port)) - --- Explicit IO/prelude/socket interfaces only; the retained portable compiler --- owns source sequencing. No ambient imports, recovery kernel or source loader. -compileIO :: String -> IO T -compileIO source = do - env <- compileFixtureDefinitions (Map.unions (map fixtureExports ["io", "prelude", "socket"])) source - maybe (assertFailure "IO program has no main" >> pure Leaf) pure (Map.lookup "main" env) - -runIOSourceWith :: IOPermissions -> T -> T -> String -> IO (T, T) -runIOSourceWith perms readerEnv initialState source = do - fullTree <- compileIO source - runEnvelopeWith perms readerEnv initialState fullTree - --- Direct ABI construction is independent of the library/compiler under test. -actionT :: Integer -> T -> T -actionT tag = Fork (ofNumber tag) -envelope :: T -> T -envelope action = Fork (ofString "tricuIO") (Fork (ofNumber 1) action) -thenAction :: T -> T -> T -thenAction left right = actionT 1 (Fork left (Fork Leaf right)) - -runEnvelopeWith :: IOPermissions -> T -> T -> T -> IO (T, T) -runEnvelopeWith perms readerEnv initialState fullTree = do - result <- runIOWith perms readerEnv initialState fullTree - case result of - Left err -> assertFailure ("IO runtime error: " ++ err) - Right pair -> pure pair - -runActionWith :: IOPermissions -> T -> T -> T -> IO (T, T) -runActionWith perms readerEnv initialState = runEnvelopeWith perms readerEnv initialState . envelope -runAction :: T -> IO T -runAction = fmap fst . runActionWith unsafePerms Leaf Leaf - -runIOSource :: String -> IO T -runIOSource source = fmap fst $ runIOSourceWith unsafePerms Leaf Leaf source - -runIOSourceWithPerms :: IOPermissions -> String -> IO T -runIOSourceWithPerms perms source = fmap fst $ runIOSourceWith perms Leaf Leaf source - -runIOSourceWithEnv :: IOPermissions -> T -> String -> IO T -runIOSourceWithEnv perms readerEnv source = fmap fst $ runIOSourceWith perms readerEnv Leaf source - -ioOkResult :: T -> T -ioOkResult val = Fork (Stem Leaf) (Fork val Leaf) - -ioErrResult :: String -> T -ioErrResult msg = Fork Leaf (Fork (ofString msg) Leaf) - -parserOk, parserErr :: T -> T -> T -parserOk value rest = Fork (Stem Leaf) (Fork value rest) -parserErr diagnostic rest = Fork Leaf (Fork diagnostic rest) diff --git a/test/IOV2ClockTests.hs b/test/IOV2ClockTests.hs @@ -0,0 +1,68 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2ClockTests (ioV2ClockTests) where + +import qualified IOV2.Clock as C +import qualified IOV2.Driver as V +import qualified IOV2.Protocol as P +import Runtime (T(..), ofNumber, ofString, ofList) +import Control.Concurrent +import Control.Exception +import Control.Monad +import Data.IORef +import System.Timeout (timeout) +import Test.Tasty +import Test.Tasty.HUnit + +request g op payload = Fork (ofNumber 10) (Fork (ofString g) (Fork (ofNumber 1) (Fork (ofString op) payload))) +ok value = Fork (Stem Leaf) (Fork value Leaf) +completed action value = V.runDetailed action >>= \result -> case result of + V.Completed actual -> actual @?= value + _ -> assertFailure "expected completion" + +ioV2ClockTests = localOption (mkTimeout 10000000) $ testGroup "IO v2 clocks/scheduler" + [ testCase "monotonic result is canonical unsigned Word64, not a wall epoch" $ do + before <- C.monotonicNanoseconds + V.runDetailed (request "clock" "monotonic" Leaf) >>= \outcome -> case outcome of + V.Completed (Fork (Stem Leaf) (Fork value Leaf)) -> case V.canonicalNat 64 value of + Just value -> do + after <- C.monotonicNanoseconds + assertBool "sample order" (before <= value && value <= after) + _ -> assertFailure "noncanonical clock value" + _ -> assertFailure "clock failed" + , testCase "zero sleep and yield return ordinary unit Results" $ do + completed (request "clock" "sleep" Leaf) (ok Leaf) + completed (request "scheduler" "yield" Leaf) (ok Leaf) + , testCase "relative sleep does not acknowledge before the requested duration" $ do + before <- C.monotonicNanoseconds + completed (request "clock" "sleep" (ofNumber 20)) (ok Leaf) + after <- C.monotonicNanoseconds + assertBool "early sleep completion" (after-before >= 20000000) + , testCase "wait chunks preserve milliseconds and never overflow host microseconds" $ do + calls <- newIORef [] + C.sleepWith (\n -> modifyIORef' calls (++[n])) 2501 + readIORef calls >>= (@?= [1000000,1000000,501000]) + writeIORef calls [] + C.sleepWith (\n -> modifyIORef' calls (++[n])) 0 + readIORef calls >>= (@?= []) + result <- try (C.sleepWith (\n -> writeIORef calls [n] >> ioError (userError "stop")) 9223372036854775807) :: IO (Either IOException ()) + case result of Left _ -> pure (); _ -> assertFailure "waiter failure ignored" + readIORef calls >>= (@?= [1000000]) + , testCase "maximum relative wait is interruptible rather than an overflowing short wait" $ do + entered <- newEmptyMVar + done <- newEmptyMVar + tid <- forkIO $ (try (putMVar entered () >> C.sleepMilliseconds 9223372036854775807) :: IO (Either SomeException ())) >>= putMVar done + takeMVar entered + threadDelay 20000 + tryReadMVar done >>= \result -> case result of Nothing -> pure (); _ -> assertFailure "wait completed early" + killThread tid + timeout 2000000 (takeMVar done) >>= \result -> case result of + Just (Left _) -> pure () + _ -> assertFailure "wait ignored interruption" + , testCase "invalid clock/scheduler arguments fail ordinarily without truncation" $ + forM_ [("clock","sleep",ofNumber 9223372036854775808), ("clock","sleep",Fork Leaf Leaf), + ("clock","sleep",Stem Leaf), ("clock","monotonic",ofNumber 1), ("scheduler","yield",ofNumber 1)] $ \(g,op,args) -> + completed (request g op args) (Fork Leaf (Fork (ofList (map ofString ["invalid-argument",g++"/"++op,"","none"])) Leaf)) + , testCase "clock inspection is inert even with an undefined duration" $ + P.inspectAction (request "clock" "sleep" (error "duration demanded")) @?= + Right (ofList [ofString "facility",ofString "clock",ofNumber 1,ofString "sleep",ofString "available"]) + ] diff --git a/test/IOV2ContextTests.hs b/test/IOV2ContextTests.hs @@ -0,0 +1,39 @@ +module IOV2ContextTests (ioV2ContextTests) where +import qualified IOV2.Context as C +import qualified IOV2.Driver as V +import qualified IOV2.Resources as R +import qualified IOV2.Protocol as P +import Runtime (T(..),ofString,ofList,ofNumber) +import System.Directory (getCurrentDirectory) +import System.Environment (lookupEnv) +import Test.Tasty +import Test.Tasty.HUnit + +request op payload = Fork (ofNumber 10) (Fork (ofString "process") (Fork (ofNumber 1) (Fork (ofString op) payload))) +ok value = Fork (Stem Leaf) (Fork value Leaf) +check action expected = V.runDetailed action >>= \outcome -> case outcome of + V.Completed value -> value @?= expected + _ -> assertFailure (show outcome) +ioV2ContextTests = testGroup "IO v2 read-only process context" + [ testCase "cwd is the actual host directory" $ do + expected <- getCurrentDirectory + check (request "working-directory" Leaf) (ok (ofString expected)) + , testCase "environment query is distinct from lexical application configuration" $ do + expected <- lookupEnv "PATH" + actual <- C.environmentVariable "PATH" + actual @?= traverse (C.boundedText "environment-variable") expected + , testCase "invalid names and payloads reject ordinarily" $ do + mapM_ (\name -> C.environmentVariable name >>= \result -> case result of + Left _ -> pure (); _ -> assertFailure "invalid name accepted") ["","A=B","A\0B",replicate 4097 'x'] + let bad op = Fork Leaf (Fork (ofList (map ofString ["invalid-argument","process/"++op,"","none"])) Leaf) + check (request "working-directory" (Stem Leaf)) (bad "working-directory") + check (request "environment-variable" (Stem Leaf)) (bad "environment-variable") + , testCase "result bounds and strict scalars do not truncate or normalize" $ do + C.boundedText "test" "λ\n\xFEFF" @?= Right "λ\n\xFEFF" + C.boundedText "test" (replicate 4096 'x') @?= Right (replicate 4096 'x') + mapM_ (\value -> case C.boundedText "test" value of + Left _ -> pure (); _ -> assertFailure "invalid context text accepted") [replicate 4097 'x',"\xD800","\0"] + , testCase "context inspection does not execute or demand payloads" $ do + P.inspectAction (request "working-directory" (error "demanded")) @?= + Right (ofList [ofString "facility",ofString "process",ofNumber 1,ofString "working-directory",ofString "available"]) + ] diff --git a/test/IOV2FilesystemTests.hs b/test/IOV2FilesystemTests.hs @@ -0,0 +1,158 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2FilesystemTests (ioV2FilesystemTests) where +import qualified IOV2.Filesystem as F +import qualified IOV2.Resources as R +import qualified System.Posix.Files as P +import qualified System.Posix.Files.ByteString as PB +import qualified Data.ByteString.Char8 as B +import System.IO.Temp (withSystemTempDirectory) +import Control.Concurrent +import Control.Concurrent.STM +import Control.Exception +import Control.Monad +import Data.IORef +import Data.List (sort) +import System.Timeout (timeout) +import Test.Tasty +import Test.Tasty.HUnit + +right :: Show e => Either e a -> IO a +right = either (\e -> assertFailure (show e) >> error "assertion") pure +category name result = case result of + Left p -> R.category p @?= name + _ -> assertFailure ("expected "++name) +fixture body = withSystemTempDirectory "v2-fs" $ \dir -> + bracket R.newRuntime (\r -> void (R.closeScope r 0)) (\r -> body r dir) +allNames r scope token = do + name <- R.nextDirectory r scope token >>= right + case name of Nothing -> pure []; Just value -> (value:) <$> allNames r scope token +wait done = timeout 3000000 (readMVar done) >>= maybe (assertFailure "timeout" >> error "timeout") pure + +ioV2FilesystemTests = localOption (mkTimeout 30000000) $ testGroup "IO v2 filesystem" + [ testCase "stat/lstat distinguish symlinks, dangling links, directory and file" $ fixture $ \_ dir -> do + B.writeFile (dir++"/file") (B.pack "abc") + P.createSymbolicLink "file" (dir++"/link") + P.createSymbolicLink "absent" (dir++"/dangling") + F.statPath True (dir++"/file") >>= (@?= Right (F.Regular 3)) + F.statPath True dir >>= (@?= Right F.Directory) + F.statPath True (dir++"/link") >>= (@?= Right (F.Regular 3)) + F.statPath False (dir++"/link") >>= (@?= Right F.Symlink) + F.statPath False (dir++"/dangling") >>= (@?= Right F.Symlink) + F.statPath True (dir++"/dangling") >>= category "not-found" + F.statPath True "" >>= category "invalid-argument" + , testCase "strict directory creation and nonrecursive removal retain contents" $ fixture $ \_ dir -> do + F.createDirectory (dir++"/new") >>= (@?= Right ()) + F.createDirectory (dir++"/new") >>= category "already-exists" + B.writeFile (dir++"/new/item") (B.pack "keep") + F.removeDirectory (dir++"/new") >>= \reply -> case reply of + Left p -> R.progress p @?= "unknown" + _ -> assertFailure "removed nonempty directory" + B.readFile (dir++"/new/item") >>= (@?= B.pack "keep") + F.removeFile (dir++"/new/item") >>= (@?= Right ()) + F.removeDirectory (dir++"/new") >>= (@?= Right ()) + F.statPath False (dir++"/new") >>= category "not-found" + , testCase "rename replaces names; unlink removes link rather than its target" $ fixture $ \_ dir -> do + B.writeFile (dir++"/old") (B.pack "new") + B.writeFile (dir++"/target") (B.pack "prior") + F.renamePath (dir++"/old") (dir++"/target") >>= (@?= Right ()) + F.statPath True (dir++"/old") >>= category "not-found" + B.readFile (dir++"/target") >>= (@?= B.pack "new") + P.createSymbolicLink "target" (dir++"/link") + F.removeFile (dir++"/link") >>= (@?= Right ()) + B.readFile (dir++"/target") >>= (@?= B.pack "new") + F.removeFile dir >>= category "io-error" + , testCase "directory is bounded per entry, excludes dots, has sticky EOF and normal lifetime" $ fixture $ \r dir -> do + let names = ["a", "λ", "space name", "line\nname"] + forM_ names $ \name -> B.writeFile (dir++"/"++name) B.empty + d <- F.openDirectory r 0 dir >>= right + allNames r 0 d >>= (\actual -> sort actual @?= sort names) + R.nextDirectory r 0 d >>= (@?= Right Nothing) + R.nextDirectory r 0 d >>= (@?= Right Nothing) + R.readSome r 0 d 1 >>= category "wrong-kind" + R.fileSize r 0 d >>= category "wrong-kind" + R.closeResource r 0 d >>= (@?= Right ()) + R.nextDirectory r 0 d >>= category "stale-handle" + R.liveCounts r >>= (@?= (1,3)) + , testCase "directory cursor follows opened inode across rename and transfer" $ fixture $ \r dir -> do + F.createDirectory (dir++"/old") >>= right + B.writeFile (dir++"/old/item") B.empty + d <- F.openDirectory r 0 (dir++"/old") >>= right + F.renamePath (dir++"/old") (dir++"/new") >>= right + F.createDirectory (dir++"/old") >>= right + (scope, tokens) <- atomically (R.reserveChildSTM r 0 [d]) >>= right + case tokens of + [fresh] -> allNames r scope fresh >>= (@?= ["item"]) + _ -> assertFailure "transfer failed" + R.nextDirectory r 0 d >>= category "stale-handle" + R.closeScope r scope >>= (@?= []) + R.liveCounts r >>= (@?= (1,3)) + , testCase "IO failure poisons cursor without replay and close runs once" $ fixture $ \r _ -> do + calls <- newIORef (0::Int); closed <- newIORef (0::Int) + d <- R.acquire r 0 (F.directoryBackend + (modifyIORef' calls (+1) >> ioError (userError "read fault")) + (modifyIORef' closed (+1))) >>= right + R.nextDirectory r 0 d >>= category "io-error" + R.nextDirectory r 0 d >>= category "invalid-state" + readIORef calls >>= (@?= 1) + R.closeResource r 0 d >>= (@?= Right ()) + R.closeScope r 0 >>= (@?= []) + readIORef closed >>= (@?= 1) + , testCase "failed raw close reports uncertainty and is never retried" $ fixture $ \r _ -> do + calls <- newIORef (0::Int) + d <- R.acquire r 0 (F.directoryBackend (pure "") + (modifyIORef' calls (+1) >> ioError (userError "close fault"))) >>= right + R.closeResource r 0 d >>= \reply -> case reply of + Left p -> R.progress p @?= "unknown" + _ -> assertFailure "close fault disappeared" + R.closeResource r 0 d >>= category "stale-handle" + R.closeScope r 0 >>= (@?= []) + readIORef calls >>= (@?= 1) + , testCase "async close settlement cannot reuse a possibly freed directory pointer" $ fixture $ \r _ -> do + calls <- newIORef (0::Int) + d <- R.acquire r 0 (F.directoryBackend (pure "") + (modifyIORef' calls (+1) >> throwIO ThreadKilled)) >>= right + result <- try (R.closeResource r 0 d) + case result of + Left (_ :: AsyncException) -> pure () + Right _ -> assertFailure "async close swallowed" + R.closeScope r 0 >>= (@?= []) + readIORef calls >>= (@?= 1) + , testCase "invalid Unicode, components and excess names poison without demanding excess character" $ fixture $ \r _ -> do + forM_ [("\xdcff", "invalid-encoding"),("a/b", "invalid-name"), + (replicate 4096 'a' ++ [error "excess name character forced"], "invalid-name")] $ \(name, expected) -> do + d <- R.acquire r 0 (F.directoryBackend (pure name) (pure ())) >>= right + R.nextDirectory r 0 d >>= category expected + R.nextDirectory r 0 d >>= category "invalid-state" + R.closeResource r 0 d >>= (@?= Right ()) + , testCase "actual non-Unicode directory entry fails rather than emitting replacement text" $ fixture $ \r dir -> do + PB.createSymbolicLink (B.pack "absent") (B.pack (dir++"/") <> B.singleton '\xff') + d <- F.openDirectory r 0 dir >>= right + R.nextDirectory r 0 d >>= category "invalid-encoding" + R.nextDirectory r 0 d >>= category "invalid-state" + , testCase "cancelled cursor holds one lease, poisons, and releases before settlement" $ fixture $ \r _ -> do + entered <- newEmptyMVar; gate <- newEmptyMVar; done <- newEmptyMVar + closed <- newIORef False + d <- R.acquire r 0 (F.directoryBackend (putMVar entered () >> takeMVar gate) + (writeIORef closed True)) >>= right + bracket (forkFinally (R.nextDirectory r 0 d) (putMVar done)) + (\worker -> killThread worker >> void (wait done)) $ \worker -> do + takeMVar entered + R.nextDirectory r 0 d >>= category "busy" + atomically (R.reserveChildSTM r 0 [d]) >>= category "busy" + readIORef closed >>= (@?= False) + killThread worker + wait done >>= \outcome -> case outcome of + Left _ -> pure () + Right _ -> assertFailure "cancelled operation resumed" + R.nextDirectory r 0 d >>= category "invalid-state" + R.closeResource r 0 d >>= (@?= Right ()) + readIORef closed >>= (@?= True) + , testCase "EOF never re-enters host and repeated dot entries have bounded scanning" $ fixture $ \r _ -> do + count <- newIORef (0::Int) + d <- R.acquire r 0 (F.directoryBackend (modifyIORef' count (+1) >> pure "") (pure ())) >>= right + replicateM_ 10 (R.nextDirectory r 0 d >>= (@?= Right Nothing)) + readIORef count >>= (@?= 1) + looping <- R.acquire r 0 (F.directoryBackend (pure ".") (pure ())) >>= right + R.nextDirectory r 0 looping >>= category "invalid-state" + R.nextDirectory r 0 looping >>= category "invalid-state" + ] diff --git a/test/IOV2NetworkTests.hs b/test/IOV2NetworkTests.hs @@ -0,0 +1,220 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2NetworkTests (ioV2NetworkTests) where +import qualified IOV2.Resources as R +import qualified IOV2.Network as TCP +import qualified IOV2.Tasks as S +import qualified IOV2.Driver as V +import Runtime (T(..), ofString, ofNumber, ofList) +import qualified Network.Socket as N +import qualified Network.Socket.ByteString as NB +import qualified Data.ByteString as B +import Control.Concurrent +import Control.Concurrent.STM +import Control.Exception +import Control.Monad +import Data.IORef +import GHC.Conc (threadStatus, ThreadStatus(..)) +import System.Timeout (timeout) +import Test.Tasty +import Test.Tasty.HUnit + +right :: Show e => Either e a -> IO a +right = either (\e -> assertFailure (show e) >> error "assertion") pure +category name result = case result of + Left p -> R.category p @?= name + _ -> assertFailure ("expected "++name) +withRuntime = bracket R.newRuntime (\r -> void (R.closeScope r 0)) +wait done = timeout 3000000 (readMVar done) >>= maybe (assertFailure "timeout" >> error "timeout") pure +fork action = do + done <- newEmptyMVar + worker <- forkFinally action (putMVar done) + pure (worker,done) +stop (worker,done) = killThread worker >> void (wait done) +-- Probing a live socket with an exclusive address query can WIN the lease and +-- make the intended reader/acceptor fail busy. Observe the actual worker instead. +waitBlocked worker = do + ready <- timeout 3000000 loop + case ready of Just () -> pure (); Nothing -> assertFailure "worker never blocked" + where loop = threadStatus worker >>= \status -> case status of + ThreadBlocked _ -> pure () + ThreadFinished -> assertFailure "worker finished instead of blocking" + ThreadDied -> assertFailure "worker died instead of blocking" + _ -> yield >> loop +loop4 = R.IPv4 (127,0,0,1) 0 +loop6 = R.IPv6 (0,0,0,0,0,0,0,1) 0 0 +connected r target = do + listener <- TCP.listenTCP r 0 target 16 >>= right + address <- R.socketAddress r 0 listener False >>= right + client <- TCP.connectTCP r 0 address >>= right + server <- R.acceptConnection r 0 listener 0 >>= right + pure (listener,client,server,address) +write r token bytes = do + n <- R.writeSome r 0 token bytes >>= right + if n == B.length bytes then pure () else write r token (B.drop n bytes) +readAll r scope token = do + bytes <- R.readSome r scope token 4096 >>= right + if B.null bytes then pure B.empty else B.append bytes <$> readAll r scope token + +ioV2NetworkTests = localOption (mkTimeout 30000000) $ testGroup "IO v2 TCP" + [ testCase "real IPv4/IPv6 byte streams, addresses, EOF and independent write half-close" $ withRuntime $ \r -> + forM_ [loop4,loop6] $ \target -> do + (listener,client,server,address) <- connected r target + R.socketAddress r 0 server False >>= (@?= Right address) + R.socketAddress r 0 client True >>= (@?= Right address) + R.socketAddress r 0 listener True >>= category "wrong-kind" + R.readSome r 0 listener 1 >>= category "wrong-kind" + R.filePosition r 0 client >>= category "wrong-kind" + let bytes = B.pack ([0..255]++[255,0,13,10]) + write r client bytes + R.shutdownWrite r 0 client >>= (@?= Right ()) + R.shutdownWrite r 0 client >>= (@?= Right ()) + R.writeSome r 0 client (B.singleton 7) >>= category "invalid-state" + readAll r 0 server >>= (@?= bytes) + R.readSome r 0 server 1 >>= (@?= Right B.empty) + write r server (B.reverse bytes) + R.shutdownWrite r 0 server >>= (@?= Right ()) + readAll r 0 client >>= (@?= B.reverse bytes) + mapM_ (\token -> R.closeResource r 0 token >>= (@?= Right ())) [listener,client,server] + R.liveCounts r >>= (@?= (1,3)) + , testCase "duplex leases coexist, release only their own bit, and close waits for both" $ withRuntime $ \r -> do + enteredRead <- newEmptyMVar; enteredWrite <- newEmptyMVar + readGate <- newEmptyMVar; writeGate <- newEmptyMVar + released <- newIORef False + let methods = R.DuplexOps + (\_ -> putMVar enteredRead () >> takeMVar readGate >> pure B.empty) + (\bytes -> putMVar enteredWrite () >> takeMVar writeGate >> pure (B.length bytes)) + (pure ()) (pure loop4) (pure loop4) (writeIORef released True) + token <- R.acquire r 0 (pure (R.Duplex methods)) >>= right + bracket (fork (R.readSome r 0 token 1)) stop $ \(_,readDone) -> + bracket (fork (R.writeSome r 0 token (B.singleton 1))) stop $ \(_,writeDone) -> do + wait enteredRead; wait enteredWrite + R.readSome r 0 token 1 >>= category "busy" + R.writeSome r 0 token (B.singleton 2) >>= category "busy" + R.shutdownWrite r 0 token >>= category "busy" + R.socketAddress r 0 token False >>= category "busy" + atomically (R.reserveChildSTM r 0 [token]) >>= category "busy" + putMVar readGate () + wait readDone >>= right >>= (@?= Right B.empty) + -- Finishing a read must not clear the simultaneous write lease. + atomically (R.reserveChildSTM r 0 [token]) >>= category "busy" + R.writeSome r 0 token (B.singleton 3) >>= category "busy" + bracket (fork (R.closeResource r 0 token)) stop $ \(_,closed) -> do + threadDelay 20000 + isEmptyMVar closed >>= (@?= True) + readIORef released >>= (@?= False) + putMVar writeGate () + wait writeDone >>= right >>= (@?= Right 1) + wait closed >>= right >>= (@?= Right ()) + readIORef released >>= (@?= True) + , testCase "real pending receive still permits send and shutdown-write" $ withRuntime $ \r -> do + (_,client,server,_) <- connected r loop4 + bracket (fork (R.readSome r 0 server 1)) stop $ \(worker,done) -> do + waitBlocked worker + write r server (B.singleton 255) + R.shutdownWrite r 0 server >>= (@?= Right ()) + readAll r 0 client >>= (@?= B.singleton 255) + write r client (B.singleton 7) + wait done >>= right >>= (@?= Right (B.singleton 7)) + , testCase "cancelled receive poisons only that direction and drains its lease" $ withRuntime $ \r -> do + (_,client,server,_) <- connected r loop4 + bracket (fork (R.readSome r 0 server 1)) stop $ \worker@(_,done) -> do + waitBlocked (fst worker) + stop worker + wait done >>= \outcome -> case outcome of Left _ -> pure (); _ -> assertFailure "receive resumed" + R.readSome r 0 server 1 >>= category "invalid-state" + write r server (B.singleton 8) + R.shutdownWrite r 0 server >>= (@?= Right ()) + readAll r 0 client >>= (@?= B.singleton 8) + , testCase "accepted connection transfers atomically and child settlement sends EOF" $ withRuntime $ \r -> do + (_,client,server,_) <- connected r loop4 + (scope,tokens) <- atomically (R.reserveChildSTM r 0 [server]) >>= right + R.readSome r 0 server 1 >>= category "stale-handle" + write r client (B.pack [0,255]) + R.shutdownWrite r 0 client >>= (@?= Right ()) + case tokens of + [fresh] -> readAll r scope fresh >>= (@?= B.pack [0,255]) + _ -> assertFailure "missing transferred connection" + R.closeScope r scope >>= (@?= []) + R.readSome r 0 client 1 >>= (@?= Right B.empty) + , testCase "accept cancellation releases admission and keeps listener reusable" $ withRuntime $ \r -> do + listener <- TCP.listenTCP r 0 loop4 1 >>= right + address <- R.socketAddress r 0 listener False >>= right + scope <- R.childScope r 0 >>= right + bracket (fork (R.acceptConnection r scope listener scope)) stop $ \worker -> do + waitBlocked (fst worker) + R.acceptConnection r 0 listener 0 >>= category "busy" + stop worker + R.closeScope r scope >>= (@?= []) + client <- TCP.connectTCP r 0 address >>= right + accepted <- R.acceptConnection r 0 listener 0 >>= right + R.shutdownWrite r 0 client >>= (@?= Right ()) + R.readSome r 0 accepted 1 >>= (@?= Right B.empty) + , testCase "closed owner admission cannot accept or leak a new connection" $ withRuntime $ \r -> do + (listener,_,_,_) <- connected r loop4 + child <- R.childScope r 0 >>= right + atomically (R.beginCloseScopeSTM r 0) + R.acceptConnection r child listener 0 >>= category "closed-scope" + R.liveCounts r >>= (@?= (2,6)) + , testCase "bind/refused errors and bounds remain ordinary with no leaked entries" $ withRuntime $ \r -> do + listener <- TCP.listenTCP r 0 loop4 1 >>= right + address <- R.socketAddress r 0 listener False >>= right + TCP.listenTCP r 0 address 1 >>= category "address-in-use" + TCP.listenTCP r 0 loop4 0 >>= category "invalid-bound" + TCP.listenTCP r 0 loop4 4097 >>= category "invalid-bound" + TCP.connectTCP r 0 loop4 >>= category "invalid-bound" + R.liveCounts r >>= (@?= (1,4)) + R.closeResource r 0 listener >>= (@?= Right ()) + TCP.connectTCP r 0 address >>= category "connection-refused" + R.liveCounts r >>= (@?= (1,3)) + , testCase "structured cancellation settles a real accept before scope release" $ withRuntime $ \r -> do + scheduler <- S.newScheduler r + root <- S.rootGroup scheduler + outcome <- S.runGroup root $ do + listener <- TCP.listenTCP r 0 loop4 1 >>= right + started <- newEmptyMVar + child <- S.spawn root True True [] (\group _ -> do + myThreadId >>= putMVar started + _ <- R.acceptConnection r (S.scopeOf group) listener (S.scopeOf group) >>= right + pure (S.Completed Leaf)) >>= right + takeMVar started >>= waitBlocked + S.cancel root child (ofString "stop") >>= (@?= Right ()) + S.join root child >>= right >>= \result -> case result of + S.Cancelled reason [] -> reason @?= ofString "stop" + _ -> assertFailure "cancellation lost" + R.liveCounts r >>= (@?= (1,4)) + pure (S.Completed Leaf) + case outcome of S.Completed Leaf -> pure (); _ -> assertFailure ("parent failed: " ++ show outcome) + R.liveCounts r >>= (@?= (0,0)) + , testCase "wire bounds reject noncanonical/excess address fields before acquisition" $ do + let v4 bytes port = ofList [ofString "ipv4",bytes,port] + v6 words scope = ofList [ofString "ipv6",words,scope,ofNumber 80] + zero4 = ofList (map ofNumber [127,0,0,1]) + zero6 = ofList (replicate 8 Leaf) + excess = foldr Fork (Fork (error "excess octet forced") (error "excess tail forced")) + (replicate 4 Leaf) + addresses = [v4 (ofList (map ofNumber [127,0,0,256])) (ofNumber 80), + v4 zero4 (ofNumber 65536), v4 excess (ofNumber 80), + v4 (ofList [Fork Leaf Leaf,Leaf,Leaf,Leaf]) (ofNumber 80), + v6 zero6 (ofNumber 4294967296), + v6 (ofList (ofNumber 65536:replicate 7 Leaf)) Leaf] + forM_ addresses $ \address -> withRuntime $ \r -> do + identity <- R.runtimeIdentity r + let owner = ofList [ofString "io.v2.scope",ofNumber identity,Leaf] + action = Fork (ofNumber 10) (Fork (ofString "tcp") (Fork (ofNumber 1) + (Fork (ofString "connect") (Fork owner address)))) + V.runInRuntime r action >>= \outcome -> case outcome of + V.Completed value -> value @?= Fork Leaf (Fork + (ofList (map ofString ["invalid-argument","tcp/connect","","none"])) Leaf) + _ -> assertFailure "bad address executed" + , testCase "blocked real write cancellation poisons writer; read direction survives" $ withRuntime $ \r -> + bracket (N.socketPair N.AF_UNIX N.Stream N.defaultProtocol) + (\(a,b) -> N.close a >> N.close b) $ \(socket,peer) -> do + N.setSocketOption socket N.SendBuffer 4096 + token <- R.acquire r 0 (TCP.duplexBackend socket) >>= right + bracket (fork (forever (write r token (B.replicate 4096 7)))) stop $ \worker -> do + waitBlocked (fst worker) + stop worker + R.writeSome r 0 token (B.singleton 1) >>= category "invalid-state" + NB.sendAll peer (B.singleton 255) + R.readSome r 0 token 1 >>= (@?= Right (B.singleton 255)) + ] diff --git a/test/IOV2PositionTests.hs b/test/IOV2PositionTests.hs @@ -0,0 +1,198 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2PositionTests (ioV2PositionTests) where +import qualified IOV2.Resources as R +import qualified IOV2.Driver as V +import qualified Data.ByteString as BS +import Control.Concurrent +import Control.Concurrent.STM +import Control.Exception +import Control.Monad +import Data.IORef +import Foreign.Ptr (castPtr) +import qualified System.Posix.IO as P +import System.Directory (renameFile) +import System.IO.Temp (withSystemTempDirectory) +import System.Timeout (timeout) +import Runtime (T(..), ofNumber, ofString, ofList) +import Test.Tasty +import Test.Tasty.HUnit + +right :: Show e => Either e a -> IO a +right = either (\e -> assertFailure (show e) >> error "assertion") pure +category name result = case result of + Left p -> R.category p @?= name + _ -> assertFailure ("expected " ++ name) +fixture use = withSystemTempDirectory "v2-position" $ \dir -> + bracket R.newRuntime (\r -> void (R.closeScope r 0)) (\r -> use r (dir++"/file")) +write r token bytes = R.writeSome r 0 token (BS.pack bytes) >>= (@?= Right (length bytes)) +wait done = timeout 3000000 (readMVar done) >>= maybe (assertFailure "timeout" >> error "timeout") pure +fake = R.FileOps Nothing Nothing Nothing Nothing (pure 0) Nothing Nothing (pure ()) + +ioV2PositionTests = localOption (mkTimeout 30000000) $ testGroup "IO v2 positioned files" + [ testCase "update preserves bytes; logical cursor switches read/write correctly" $ fixture $ \r path -> do + BS.writeFile path (BS.pack [0..9]) + f <- R.openUpdate r 0 path >>= right + R.fileSize r 0 f >>= (@?= Right 10) + R.readSome r 0 f 2 >>= (@?= Right (BS.pack [0,1])) + R.filePosition r 0 f >>= (@?= Right 2) + write r f [255,254] + R.filePosition r 0 f >>= (@?= Right 4) + R.readSome r 0 f 2 >>= (@?= Right (BS.pack [4,5])) + R.seekFile r 0 f 0 >>= (@?= Right ()) + R.readSome r 0 f 10 >>= (@?= Right (BS.pack [0,1,255,254,4,5,6,7,8,9])) + R.closeResource r 0 f >>= (@?= Right ()) + BS.readFile path >>= (@?= BS.pack [0,1,255,254,4,5,6,7,8,9]) + , testCase "resize discards read ahead, preserves cursor and zero-fills growth" $ fixture $ \r path -> do + BS.writeFile path (BS.pack [0..99]) + f <- R.openUpdate r 0 path >>= right + R.readSome r 0 f 3 >>= (@?= Right (BS.pack [0,1,2])) + R.resizeFile r 0 f 2 >>= (@?= Right ()) + R.filePosition r 0 f >>= (@?= Right 3) + R.readSome r 0 f 1 >>= (@?= Right BS.empty) + R.resizeFile r 0 f 8 >>= (@?= Right ()) + R.filePosition r 0 f >>= (@?= Right 3) + R.readSome r 0 f 5 >>= (@?= Right (BS.replicate 5 0)) + R.seekFile r 0 f 12 >>= (@?= Right ()) + write r f [255] + R.fileSize r 0 f >>= (@?= Right 13) + R.closeResource r 0 f >>= (@?= Right ()) + BS.readFile path >>= (@?= BS.pack ([0,1]++replicate 10 0++[255])) + , testCase "new update/append files and wide sparse offsets" $ fixture $ \r path -> do + f <- R.openUpdate r 0 path >>= right + R.fileSize r 0 f >>= (@?= Right 0) + R.seekFile r 0 f 4294967296 >>= (@?= Right ()) + write r f [255] + R.filePosition r 0 f >>= (@?= Right 4294967297) + R.fileSize r 0 f >>= (@?= Right 4294967297) + R.seekFile r 0 f 4294967294 >>= (@?= Right ()) + R.readSome r 0 f 3 >>= (@?= Right (BS.pack [0,0,255])) + R.resizeFile r 0 f 0 >>= (@?= Right ()) + R.closeResource r 0 f >>= (@?= Right ()) + BS.readFile path >>= (@?= BS.empty) + appended <- R.openAppender r 0 (path++"-append") >>= right + write r appended [0,255] + R.closeResource r 0 appended >>= (@?= Right ()) + BS.readFile (path++"-append") >>= (@?= BS.pack [0,255]) + , testCase "append is at each write's EOF, not an open-time seek" $ fixture $ \r path -> do + BS.writeFile path (BS.pack [1,2]) + f <- R.openAppender r 0 path >>= right + -- Separate POSIX fd models an external writer, bypassing GHC Handle locks. + bracket (P.openFd path P.WriteOnly (P.defaultFileFlags {P.append=True})) P.closeFd $ \fd -> + BS.useAsCStringLen (BS.pack [3,4]) $ \(ptr,n) -> + P.fdWriteBuf fd (castPtr ptr) (fromIntegral n) >>= (@?= 2) + write r f [5,255] + R.fileSize r 0 f >>= (@?= Right 6) + R.seekFile r 0 f 0 >>= category "unsupported" + R.filePosition r 0 f >>= category "unsupported" + R.resizeFile r 0 f 0 >>= category "unsupported" + R.readSome r 0 f 1 >>= category "wrong-kind" + R.closeResource r 0 f >>= (@?= Right ()) + BS.readFile path >>= (@?= BS.pack [1,2,3,4,5,255]) + , testCase "reader mode and unrelated resource kinds cannot mutate files" $ fixture $ \r path -> do + BS.writeFile path (BS.pack [1,2,3]) + f <- R.openReader r 0 path >>= right + R.resizeFile r 0 f 0 >>= category "unsupported" + R.writeSome r 0 f (BS.singleton 4) >>= category "wrong-kind" + R.seekFile r 0 f 2 >>= (@?= Right ()) + R.readSome r 0 f 2 >>= (@?= Right (BS.singleton 3)) + standard <- R.standardStream r 0 R.StandardInput >>= right + R.fileSize r 0 standard >>= category "wrong-kind" + R.seekFile r 0 standard 0 >>= category "wrong-kind" + R.closeResource r 0 f >>= (@?= Right ()) + R.filePosition r 0 f >>= category "stale-handle" + BS.readFile path >>= (@?= BS.pack [1,2,3]) + , testCase "host Handle locking rejects conflicting opens without changing content" $ fixture $ \r path -> do + BS.writeFile path (BS.pack [1,2,3]) + reader <- R.openReader r 0 path >>= right + R.openUpdate r 0 path >>= category "io-error" + R.openAppender r 0 path >>= category "io-error" + R.readSome r 0 reader 3 >>= (@?= Right (BS.pack [1,2,3])) + R.closeResource r 0 reader >>= (@?= Right ()) + BS.readFile path >>= (@?= BS.pack [1,2,3]) + , testCase "size is queried on the opened file, not its replaced pathname" $ fixture $ \r path -> do + BS.writeFile path (BS.pack [1,2,3]) + f <- R.openReader r 0 path >>= right + renameFile path (path++"-old") + BS.writeFile path (BS.replicate 10 7) + R.fileSize r 0 f >>= (@?= Right 3) + R.readSome r 0 f 4 >>= (@?= Right (BS.pack [1,2,3])) + , testCase "position transfers with ownership and old aliases become stale" $ fixture $ \r path -> do + BS.writeFile path (BS.pack [0..9]) + f <- R.openUpdate r 0 path >>= right + R.seekFile r 0 f 7 >>= (@?= Right ()) + (child, moved) <- atomically (R.reserveChildSTM r 0 [f]) >>= right + case moved of + [fresh] -> do + R.filePosition r child fresh >>= (@?= Right 7) + R.seekFile r child fresh 3 >>= (@?= Right ()) + R.readSome r child fresh 1 >>= (@?= Right (BS.singleton 3)) + _ -> assertFailure "bad transfer" + R.fileSize r 0 f >>= category "stale-handle" + R.closeScope r child >>= (@?= []) + R.liveCounts r >>= (@?= (1,3)) + , testCase "bounds rejected before effects; bad backend positions cannot escape" $ fixture $ \r _ -> do + calls <- newIORef (0::Int) + let methods = fake {R.fileSeekAction=Just (\_ -> modifyIORef' calls (+1)), + R.fileResizeAction=Just (\_ -> modifyIORef' calls (+1)), + R.filePositionAction=Just (pure (-1)), R.fileSizeAction=pure (-1)} + f <- R.acquire r 0 (pure (R.File methods)) >>= right + forM_ [-1,9223372036854775808] $ \n -> do + R.seekFile r 0 f n >>= category "invalid-bound" + R.resizeFile r 0 f n >>= category "invalid-bound" + readIORef calls >>= (@?= 0) + R.filePosition r 0 f >>= category "unsupported" + R.fileSize r 0 f >>= category "unsupported" + R.seekFile r 0 f 9223372036854775807 >>= (@?= Right ()) + readIORef calls >>= (@?= 1) + , testCase "position IO shares read/write lease; cancellation drains it before release" $ fixture $ \r _ -> do + entered <- newEmptyMVar; blocked <- newEmptyMVar; finished <- newEmptyMVar + released <- newIORef False + let methods = fake {R.fileSeekAction=Just (\_ -> putMVar entered () >> takeMVar blocked), + R.fileReadAction=Just (const (pure BS.empty)), + R.fileWriteAction=Just (pure . BS.length), + R.fileReleaseAction=writeIORef released True} + f <- R.acquire r 0 (pure (R.File methods)) >>= right + bracket (forkFinally (R.seekFile r 0 f 2) (putMVar finished)) + (\worker -> killThread worker >> void (wait finished)) $ \worker -> do + takeMVar entered + R.fileSize r 0 f >>= category "busy" + R.readSome r 0 f 1 >>= category "busy" + R.writeSome r 0 f (BS.singleton 1) >>= category "busy" + atomically (R.reserveChildSTM r 0 [f]) >>= category "busy" + readIORef released >>= (@?= False) + killThread worker + wait finished >>= \outcome -> case outcome of + Left _ -> pure () + Right _ -> assertFailure "cancelled seek resumed" + R.closeResource r 0 f >>= (@?= Right ()) + readIORef released >>= (@?= True) + , testCase "backend mutation errors report unknown state, not no progress" $ fixture $ \r _ -> do + let methods = fake {R.fileSeekAction=Just (\_ -> ioError (userError "seek fault")), + R.fileResizeAction=Just (\_ -> ioError (userError "resize fault"))} + f <- R.acquire r 0 (pure (R.File methods)) >>= right + forM_ [R.seekFile r 0 f 0,R.resizeFile r 0 f 0] $ \action -> action >>= \reply -> case reply of + Left problem -> R.progress problem @?= "unknown" + Right _ -> assertFailure "fault succeeded" + , testCase "wire preserves the entire signed-64 positive offset range" $ fixture $ \r _ -> do + observed <- newIORef (-1) + f@(R.Token identity key) <- R.acquire r 0 (pure (R.File + (fake {R.fileSeekAction=Just (writeIORef observed)}))) >>= right + let token = ofList [ofString "io.v2.resource",ofNumber identity,ofNumber key] + action = Fork (ofNumber 10) (Fork (ofString "file") (Fork (ofNumber 1) + (Fork (ofString "seek") (Fork token (ofNumber 9223372036854775807))))) + V.runInRuntime r action >>= \outcome -> case outcome of + V.Completed value -> value @?= Fork (Stem Leaf) (Fork Leaf Leaf) + _ -> assertFailure "wide offset failed" + readIORef observed >>= (@?= 9223372036854775807) + R.filePosition r 0 f >>= category "stale-handle" + , testCase "wire rejects overlong offset without demanding excess bits" $ fixture $ \r _ -> do + identity <- R.runtimeIdentity r + let token = ofList [ofString "io.v2.resource",ofNumber identity,ofNumber 1] + offset = foldr Fork (Stem (error "excess bits demanded")) (replicate 63 Leaf) + action = Fork (ofNumber 10) (Fork (ofString "file") (Fork (ofNumber 1) + (Fork (ofString "seek") (Fork token offset)))) + V.runInRuntime r action >>= \outcome -> case outcome of + V.Completed (Fork Leaf (Fork problem Leaf)) -> problem @?= + ofList (map ofString ["invalid-argument","file/seek","","none"]) + _ -> assertFailure "invalid offset executed" + ] diff --git a/test/IOV2ProcessTests.hs b/test/IOV2ProcessTests.hs @@ -0,0 +1,181 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2ProcessTests (ioV2ProcessTests) where +import qualified IOV2.Process as P +import qualified IOV2.Driver as V +import qualified IOV2.Protocol as Protocol +import qualified IOV2.Resources as R +import qualified IOV2.Tasks as S +import Runtime (T(..), ofString, ofList, ofNumber) +import Control.Concurrent +import Control.Exception +import Data.IORef +import Control.Monad +import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as B +import System.Directory (findExecutable) +import System.Posix.Signals (signalProcess, nullSignal) +import System.IO.Error (isDoesNotExistError) +import Test.Tasty +import Test.Tasty.HUnit + +right (Right x) = pure x +right (Left e) = assertFailure (show e) >> error "failed" +withGroup action = do + runtime <- R.newRuntime + scheduler <- S.newScheduler runtime + group <- S.rootGroup scheduler + outcome <- S.runGroup group (action runtime group) + R.liveCounts runtime >>= (@?= (0,0)) + S.liveCounts scheduler >>= (@?= (0,0,0)) + pure outcome +success (S.Completed Leaf) = pure () +success other = assertFailure (show other) +program name = findExecutable name >>= maybe (assertFailure ("missing test executable: " ++ name) >> error "missing") pure +readAll runtime owner token = do + chunk <- R.readSome runtime owner token 4096 >>= right + if BS.null chunk then pure BS.empty else BS.append chunk <$> readAll runtime owner token +joined group task wanted = S.join group task >>= \value -> case value of + Right (S.Completed actual) -> actual @?= wanted + other -> assertFailure (show other) +exit n = ofList [ofString "exit",ofNumber n] + +ioV2ProcessTests = localOption (mkTimeout 15000000) $ testGroup "IO v2 direct child processes" + [ testCase "argv is literal; environment and cwd are explicit; nonzero exit is data" $ do + shell <- program "sh" + withGroup (\runtime group -> do + (task,[input,output,errors]) <- P.launch runtime group + (P.Spec shell ["-c","printf '%s|%s|%s' \"$1\" \"$VALUE\" \"$PWD\"; exit 7", "test", "$(not-a-command); *"] (Just "/") (Just [("VALUE","literal")]) 20) >>= right + R.closeResource runtime (S.scopeOf group) input >>= right + readAll runtime (S.scopeOf group) output >>= (@?= B.pack "$(not-a-command); *|literal|/") + readAll runtime (S.scopeOf group) errors >>= (@?= BS.empty) + joined group task (exit 7) + pure (S.Completed Leaf)) >>= success + , testCase "stream a binary pipeline while draining concurrently" $ do + cat <- program "cat" + let bytes = BS.pack (take 262144 (cycle [0..255])) + withGroup (\runtime group -> do + (task,[input,output,errors]) <- P.launch runtime group (P.Spec cat [] Nothing Nothing 20) >>= right + drained <- newEmptyMVar + _ <- forkIO $ (try (readAll runtime (S.scopeOf group) output) :: IO (Either SomeException BS.ByteString)) >>= putMVar drained + let write remaining | BS.null remaining = pure () + | otherwise = do + let (chunk,rest) = BS.splitAt 4096 remaining + R.writeSome runtime (S.scopeOf group) input chunk >>= right >>= (@?= BS.length chunk) + write rest + write bytes + R.closeResource runtime (S.scopeOf group) input >>= right + takeMVar drained >>= either throwIO (@?= bytes) + readAll runtime (S.scopeOf group) errors >>= (@?= BS.empty) + joined group task (exit 0) + pure (S.Completed Leaf)) >>= success + , testCase "missing executable is an ordinary error, settled without leaked tickets" $ do + withGroup (\runtime group -> do + result <- P.launch runtime group (P.Spec "/definitely-absent-tricu-process" [] Nothing Nothing 0) + case result of Left _ -> pure (); _ -> assertFailure "launch succeeded" + pure (S.Completed Leaf)) >>= success + , testCase "normal scope exit joins an unconsumed process result" $ do + shell <- program "sh" + withGroup (\runtime group -> do + -- Output fits the pipe, so implicit joining does not require buffering it. + _ <- P.launch runtime group (P.Spec shell ["-c","printf done; exit 3"] Nothing Nothing 20) >>= right + pure (S.Completed Leaf)) >>= success + , testCase "cancellation while scope joins escalates TERM-ignoring child and reaps" $ do + shell <- program "sh" + withGroup (\runtime root -> do + entered <- newEmptyMVar + parent <- S.spawn root True True [] (\group _ -> do + (_,[_input,output,_errors]) <- P.launch runtime group + (P.Spec shell ["-c","trap '' TERM; printf '%s\\n' \"$$\"; while :; do :; done"] Nothing Nothing 20) >>= right + bytes <- R.readSome runtime (S.scopeOf group) output 4096 >>= right + let pid = read (B.unpack bytes) + putMVar entered (pid,S.scopeOf group) + pure (S.Completed Leaf)) >>= right + (pid,owner) <- takeMVar entered + -- Observe closed admission, rather than guessing the phase from a delay. + -- A still-live process child means this scope cannot yet be Releasing. + let joining = do + admitted <- R.acquire runtime owner (pure (R.Internal (pure ()))) + case admitted of + Left problem -> R.category problem @?= "closed-scope" + Right token -> R.closeInternalResource runtime owner token >>= right >> threadDelay 1000 >> joining + joining + signalProcess nullSignal pid + S.cancel root parent (ofString "stop") >>= right + result <- S.join root parent + case result of Right (S.Cancelled reason []) -> reason @?= ofString "stop"; _ -> assertFailure (show result) + alive <- try (signalProcess nullSignal pid) + case alive of Left (e :: IOException) -> assertBool "reaped PID" (isDoesNotExistError e); Right _ -> assertFailure "child remains live or zombie" + pure (S.Completed Leaf)) >>= success + , testCase "failure cancels a blocked pipe child and preserves the primary" $ do + cat <- program "cat" + outcome <- withGroup (\runtime group -> do + _ <- P.launch runtime group (P.Spec cat [] Nothing Nothing 20) >>= right + pure (S.Failed (ofString "primary") [])) + case outcome of S.Failed problem [] -> problem @?= ofString "primary"; _ -> assertFailure (show outcome) + , testCase "repeated implicit joins release pipes, private handles, scopes and tickets" $ do + shell <- program "sh" + withGroup (\runtime group -> do + baseline <- R.liveCounts runtime + replicateM_ 30 $ do + S.nested group (\child -> do + _ <- P.launch runtime child (P.Spec shell ["-c","exit 0"] Nothing Nothing 20) >>= right + pure (S.Completed Leaf)) >>= success + R.liveCounts runtime >>= (@?= baseline) + pure (S.Completed Leaf)) >>= success + , testCase "failed pipe writes poison subsequent attempts instead of replay" $ do + shell <- program "sh" + withGroup (\runtime group -> do + (task,[input,_,_]) <- P.launch runtime group (P.Spec shell ["-c","exit 0"] Nothing Nothing 0) >>= right + joined group task (exit 0) + first <- R.writeSome runtime (S.scopeOf group) input (B.pack "x") + case first of Left _ -> pure (); _ -> assertFailure "write to exited child succeeded" + second <- R.writeSome runtime (S.scopeOf group) input (B.pack "x") + case second of Left problem -> R.category problem @?= "invalid-state"; _ -> assertFailure "failed pipe was reused" + -- hClose may report the buffered failed write; keep that cleanup failure + -- visible instead of insisting that the parent must complete successfully. + pure (S.Completed Leaf)) >>= \outcome -> case outcome of + S.Completed Leaf -> pure () + S.Failed _ _ -> pure () + _ -> assertFailure (show outcome) + , testCase "termination failures still attempt escalation and reaping exactly once" $ do + forM_ [[],["term"],["kill"],["term","kill","reap"]] $ \failures -> do + calls <- newIORef [] + let action name = modifyIORef' calls (++[name]) >> when (name `elem` failures) (ioError (userError name)) + ops = P.TerminationOps (pure False) (action "term") (action "kill") (action "reap") (pure 0) (action "pause") + outcome <- try (P.terminateWith ops 0) :: IO (Either SomeException ()) + readIORef calls >>= (@?= ["term","kill","reap"]) + case (failures,outcome) of ([],Right ()) -> pure (); (_:_,Left _) -> pure (); _ -> assertFailure "wrong cleanup result" + , testCase "relative grace survives monotonic Word64 wrap" $ do + samples <- newIORef [18446744073709551606,5,1000000] + calls <- newIORef [] + let now = atomicModifyIORef' samples (\(x:xs) -> (xs,x)) + action name = modifyIORef' calls (++[name]) + ops = P.TerminationOps (pure False) (action "term") (action "kill") (action "reap") now (action "pause") + P.terminateWith ops 1 + readIORef calls >>= (@?= ["term","pause","kill","reap"]) + , testCase "raw pipe cleanup failure does not skip process settlement" $ do + settled <- newIORef False + outcome <- try (P.cleanupAll [ioError (userError "pipe-close"), writeIORef settled True]) :: IO (Either SomeException ()) + readIORef settled >>= (@?= True) + case outcome of Left _ -> pure (); _ -> assertFailure "cleanup error lost" + , testCase "wire bounds reject excess arguments/text without demanding the extra element" $ do + let args n tail = foldr Fork tail (replicate n (ofString "a")) + text n tail = foldr Fork tail (replicate n (ofNumber 97)) + excess = Fork (error "excess element demanded") Leaf + inherit = ofList [ofString "inherit"] + payload arguments = ofList [ofString "/not-executed",arguments,inherit,inherit,ofNumber 0] + request arguments = Fork (ofNumber 10) (Fork (ofString "process") (Fork (ofNumber 1) (Fork (ofString "spawn") arguments))) + expected = Fork Leaf (Fork (ofList (map ofString ["invalid-argument","process/spawn","","none"])) Leaf) + forM_ [payload (args 256 excess),payload (ofList [text 4096 excess])] $ \value -> + V.runDetailed (request value) >>= \outcome -> case outcome of + S.Completed actual -> actual @?= expected + _ -> assertFailure (show outcome) + Protocol.inspectAction (request (error "inspection demanded process spec")) @?= + Right (ofList [ofString "facility",ofString "process",ofNumber 1,ofString "spawn",ofString "available"]) + , testCase "invalid launch values are rejected before admission" $ do + forM_ [P.Spec "" [] Nothing Nothing 0, P.Spec "x" [] Nothing Nothing (-1), + P.Spec "x" [] Nothing (Just [("X","1"),("X","2")]) 0, + P.Spec "x" ["a\0b"] Nothing Nothing 0, + P.Spec "x" (replicate 257 "a") Nothing Nothing 0] $ \spec -> + assertBool (show spec) (not (P.validSpec spec)) + ] diff --git a/test/IOV2ProtocolTests.hs b/test/IOV2ProtocolTests.hs @@ -0,0 +1,83 @@ +module IOV2ProtocolTests (ioV2ProtocolTests) where + +import qualified IOV2.Driver as V +import qualified IOV2.Protocol as P +import qualified IOV2.Resources as R +import Runtime (T(..), ofNumber, ofString, ofList) +import Test.Tasty +import Test.Tasty.HUnit + +request :: String -> Integer -> String -> T -> T +request g v op args = Fork (ofNumber 10) (Fork (ofString g) (Fork (ofNumber v) (Fork (ofString op) args))) +ok, err :: T -> T +ok x = Fork (Stem Leaf) (Fork x Leaf) +err x = Fork Leaf (Fork x Leaf) +problem category operation detail = ofList (map ofString [category,operation,detail,"none"]) +completed action expected = V.runDetailed action >>= \outcome -> case outcome of + V.Completed value -> value @?= expected + _ -> assertFailure "expected completion" + +ioV2ProtocolTests :: TestTree +ioV2ProtocolTests = testGroup "IO v2 protocol" + [ testCase "capability schema reports exact implemented groups and limits" $ do + let strings = ofList . map ofString + row key value = ofList [ofString key,value] + groups = [("runtime",["capabilities","inspect"]),("standard",["input","output","error"]),("file",["open-reader","open-writer","open-appender","open-update","seek","position","size","resize","sync"]), + ("path",["stat","lstat","remove-file","rename"]),("directory",["create","remove","open","next","sync"]),("tcp",["listen","connect","accept","local-address","peer-address","shutdown-write"]),("resolver",["resolve"]),("indexed-tree",["read","hash","write","verify"]),("utf8",["encode","decode"]),("stream",["read","write"]),("resource",["close"]),("stage",["open","seal","publish"]), + ("digest",["open","update","finish"]),("clock",["monotonic","sleep"]),("scheduler",["yield"]),("process",["spawn","working-directory","environment-variable"])] + expected = ofList [row "schema" (ofNumber 1), row "io-version" (ofNumber 2), + row "backend" (ofString "haskell-native"), row "authority" (ofString "os-process"), + row "core" (strings ["pure","bind","fail","scope","current-scope","spawn","join","discard","cancel","cancelled"]), + row "groups" (ofList [ofList [ofString g,ofNumber 1,strings ops] | (g,ops) <- groups]), + row "limits" (ofList [row "chunk-bytes" (ofNumber 4096), row "transfer-handles" (ofNumber 256), row "file-offset-max" (ofNumber 9223372036854775807), row "directory-name-scalars" (ofNumber 4096), row "tcp-backlog" (ofNumber 4096), row "resolver-host-characters" (ofNumber 253), row "resolver-addresses" (ofNumber 64), row "indexed-sections" (ofNumber 64), row "indexed-manifest-bytes" (ofNumber 65536), row "utf8-encode-scalars" (ofNumber 1024), row "sleep-milliseconds-max" (ofNumber 9223372036854775807), row "process-arguments" (ofNumber 256), row "process-environment-entries" (ofNumber 256), row "process-text-scalars" (ofNumber 4096), row "process-total-scalars" (ofNumber 65536), row "process-grace-milliseconds-max" (ofNumber 9223372036854775807)]), + row "tcp-address-families" (strings ["ipv4","ipv6"]), + row "digest-algorithms" (strings ["sha256"]), + row "restrictions" (strings ["action-boundary-cancellation","no-hard-deadlines","stage-same-filesystem","stage-no-copy-fallback", + "stage-private-namespace","publish-new-requires-hard-links","no-crash-recovery","borrowed-standard-streams","standard-stream-leases-per-run","file-single-cursor-lease","append-no-position-or-resize","file-host-handle-locking","directory-unsorted-non-snapshot","filesystem-host-encoding","directory-failure-poisons-cursor","tcp-numeric-addresses","tcp-ipv6-only-listeners","tcp-direction-leases","tcp-failure-poisons-direction","resolver-host-order-and-buffering","resolver-may-delay-cancellation","indexed-authenticated-snapshot","indexed-caller-frame-and-disk-limit","indexed-dag-resident","indexed-writer-preflight","indexed-budget-not-heap-quota","sync-host-filesystem-guarantees","utf8-strict-scalars","clock-host-monotonic-word64","sleep-may-run-late","yield-no-ordering-guarantee", "process-direct-child-only", "process-normal-exit-joins", "process-explicit-pipe-draining"])] + completed (request "runtime" 1 "capabilities" Leaf) (ok expected) + R.chunkLimit @?= 4096 + P.transferLimit @?= 256 + , testCase "unsupported requests do not inspect arguments and remain ordinary errors" $ + mapM_ (\(g,v,op) -> completed (request g v op (error "unsupported arguments demanded")) + (err (problem "unsupported" (g++"/"++op) ("group revision "++show v)))) + [("future-tcp",1,"connect"),("digest",2,"open"),("file",1,"absent"),("clock",2,"sleep"),("clock",1,"deadline"),("scheduler",1,"absent"),("process",2,"spawn"),("process",1,"shell")] + , testCase "recognized operation arguments fail ordinarily" $ + mapM_ (\(g,op) -> V.runDetailed (request g 1 op (if g == "utf8" && op == "encode" then Stem Leaf else Leaf)) >>= \outcome -> case outcome of + V.Completed (Fork Leaf (Fork (Fork _ (Fork operation _)) Leaf)) -> operation @?= ofString (g++"/"++op) + _ -> assertFailure (g++"/"++op)) + [("file","open-reader"),("file","open-writer"),("file","open-appender"),("file","open-update"),("file","seek"),("file","position"),("file","size"),("file","resize"),("file","sync"),("stream","read"),("stream","write"), + ("path","stat"),("path","lstat"),("path","remove-file"),("path","rename"),("directory","create"),("directory","remove"),("directory","open"),("directory","next"),("directory","sync"),("tcp","listen"),("tcp","connect"),("tcp","accept"),("tcp","local-address"),("tcp","peer-address"),("tcp","shutdown-write"),("resolver","resolve"),("indexed-tree","read"),("indexed-tree","hash"),("indexed-tree","write"),("indexed-tree","verify"),("utf8","encode"),("utf8","decode"),("resource","close"),("stage","open"),("stage","seal"),("stage","publish"), + ("digest","open"),("digest","update"),("digest","finish"),("runtime","inspect")] + , testCase "inspection does not demand core payloads or callbacks" $ + mapM_ (\(tag,label) -> completed (request "runtime" 1 "inspect" (Fork (ofNumber tag) (error "payload demanded"))) + (ok (ofList [ofString "core",ofString label]))) + (zip [0..9] ["pure","bind","fail","scope","current-scope","spawn","join","discard","cancel","cancelled"]) + , testCase "inspection classifies headers without executing or validating arguments" $ do + completed (request "runtime" 1 "inspect" (request "file" 1 "open-writer" (error "writer executed"))) + (ok (ofList [ofString "facility",ofString "file",ofNumber 1,ofString "open-writer",ofString "available"])) + completed (request "runtime" 1 "inspect" (request "file" 2 "open-writer" (error "unsupported arguments demanded"))) + (ok (ofList [ofString "facility",ofString "file",ofNumber 2,ofString "open-writer",ofString "unsupported"])) + , testCase "retired numeric facility tags cannot accidentally execute" $ + mapM_ (\tag -> V.runDetailed (Fork (ofNumber tag) (error "retired payload demanded")) >>= \outcome -> case outcome of + V.Failed diagnostic [] -> diagnostic @?= problem "protocol-error" "core/decode" "unknown computation constructor" + _ -> assertFailure "retired tag accepted") [20,21,22,23,24,25,26,27,30,31,32,40,41,42,50,51,52,53,54,55,56,60,61,62,63,64,65,66,67,68,80,81,82,83,84,85,86,87,88,89,95,96,97,100,101,255] + , testCase "malformed headers are protocol failures rather than unsupported" $ do + let header g v op = Fork (ofNumber 10) (Fork g (Fork v (Fork op (error "malformed args demanded")))) + oversized = foldr Fork (Fork (error "excess name head demanded") (error "excess name tail demanded")) + (replicate 64 (ofNumber 97)) + inputs = [header (ofString "") (ofNumber 1) (ofString "read"), + header (ofString "File") (ofNumber 1) (ofString "read"), + header (ofString "file") Leaf (ofString "read"), + header (ofString "file") (Fork Leaf Leaf) (ofString "read"), + header oversized (ofNumber 1) (ofString "read"), + Fork (Fork Leaf Leaf) (error "noncanonical core payload demanded")] + mapM_ (\input -> V.runDetailed input >>= \outcome -> case outcome of + V.Failed _ [] -> pure () + _ -> assertFailure "malformed header accepted") inputs + , testCase "standard stream lookup validates unit without demanding its child" $ + mapM_ (\op -> completed (request "standard" 1 op (Stem (error "payload forced"))) + (err (problem "invalid-argument" ("standard/"++op) ""))) ["input","output","error"] + , testCase "runtime capability request validates unit without traversing payload" $ + completed (request "runtime" 1 "capabilities" (Stem (error "ignored child demanded"))) + (err (problem "invalid-argument" "runtime/capabilities" "")) + ] diff --git a/test/IOV2ResolverTests.hs b/test/IOV2ResolverTests.hs @@ -0,0 +1,71 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2ResolverTests (ioV2ResolverTests) where +import qualified IOV2.Resolver as D +import qualified IOV2.Resources as R +import qualified IOV2.Driver as V +import Runtime (T(..), ofString, ofNumber, ofList) +import Control.Exception +import Control.Concurrent +import Control.Monad +import Data.IORef +import System.Timeout (timeout) +import Test.Tasty +import Test.Tasty.HUnit + +category name reply = case reply of Left p -> R.category p @?= name; _ -> assertFailure name +address = R.IPv4 (127,0,0,1) 1234 +ioV2ResolverTests = localOption (mkTimeout 30000000) $ testGroup "IO v2 host resolver" + [ testCase "invalid inputs fail before touching resolver" $ do + touched <- newIORef False + let query = writeIORef touched True >> pure [] + forM_ ["","bad name","bad/name","a\0b","λ",replicate 254 'a'] $ \host -> + D.resolveWith host 80 1 query >>= category "invalid-argument" + forM_ [(-1,1),(65536,1),(80,0),(80,65)] $ \(port,limit) -> + D.resolveWith "localhost" port limit query >>= category "invalid-bound" + readIORef touched >>= (@?= False) + , testCase "prefix is bounded without demanding an excess endpoint or tail" $ do + D.resolveWith "localhost" 1234 1 (pure [address]) >>= (@?= Right (D.Resolution [address] False)) + D.resolveWith "localhost" 1234 1 (pure (address:error "excess endpoint forced":error "excess tail forced")) + >>= (@?= Right (D.Resolution [address] True)) + D.resolveWith "localhost" 1234 1 (pure []) >>= (@?= Right (D.Resolution [] False)) + , testCase "host order and duplicates are preserved, not silently reprioritized" $ + D.resolveWith "localhost" 1234 3 (pure [address,address,R.IPv4 (127,0,0,2) 1234,address]) + >>= (@?= Right (D.Resolution [address,address,R.IPv4 (127,0,0,2) 1234] True)) + , testCase "resolver IO errors are ordinary and bounded; unrelated faults are not" $ do + D.resolveWith "localhost" 80 1 (ioError (userError (replicate 1000 'x'))) >>= \reply -> case reply of + Left p -> do + R.category p @?= "resolution-failed" + assertBool "bounded diagnostic" (length (R.detail p) <= 512) + R.progress p @?= "none" + _ -> assertFailure "failure swallowed" + actual <- try (D.resolveWith "localhost" 80 1 (throwIO (ErrorCall "unexpected"))) + case actual of Left (_ :: ErrorCall) -> pure (); Right _ -> assertFailure "fault converted to ordinary result" + , testCase "numeric/localhost host resolution preserves requested port and family" $ do + D.resolveTCP "127.0.0.1" D.IPv4Only 1234 64 >>= \reply -> case reply of + Right (D.Resolution values False) -> do + assertBool "numeric endpoint missing" (address `elem` values) + assertBool "unexpected endpoint" (all (== address) values) + _ -> assertFailure (show reply) + D.resolveTCP "localhost" D.AnyFamily 4321 1 >>= \reply -> case reply of + Right (D.Resolution [value] _) -> case value of + R.IPv4 _ port -> port @?= 4321 + R.IPv6 _ _ port -> port @?= 4321 + _ -> assertFailure (show reply) + , testCase "cancellation is not swallowed as a resolver error" $ do + entered <- newEmptyMVar; gate <- newEmptyMVar; done <- newEmptyMVar + worker <- forkFinally (D.resolveWith "localhost" 80 1 (putMVar entered () >> takeMVar gate)) (putMVar done) + takeMVar entered + killThread worker + timeout 3000000 (takeMVar done) >>= \reply -> case reply of + Just (Left _) -> pure () + _ -> assertFailure "cancellation became a result" + , testCase "wire host bound does not demand excess characters" $ do + let host = foldr Fork (Fork (error "excess character forced") (error "excess tail forced")) + (replicate 253 (ofNumber 97)) + action = Fork (ofNumber 10) (Fork (ofString "resolver") (Fork (ofNumber 1) + (Fork (ofString "resolve") (ofList [host,ofString "any",ofNumber 80,ofNumber 1])))) + V.runDetailed action >>= \outcome -> case outcome of + V.Completed value -> value @?= Fork Leaf (Fork + (ofList (map ofString ["invalid-argument","resolver/resolve","","none"])) Leaf) + _ -> assertFailure "invalid host executed" + ] diff --git a/test/IOV2StandardTests.hs b/test/IOV2StandardTests.hs @@ -0,0 +1,133 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2StandardTests (ioV2StandardTests) where + +import qualified IOV2.Resources as R +import qualified Data.ByteString as BS +import Control.Concurrent +import Control.Concurrent.STM +import GHC.Conc (threadStatus, ThreadStatus(..)) +import Control.Exception +import Control.Monad +import System.IO +import System.IO.Temp +import System.Posix.IO (createPipe, fdToHandle) +import System.Timeout (timeout) +import Test.Tasty +import Test.Tasty.HUnit + +right :: Show e => Either e a -> IO a +right = either (\e -> assertFailure (show e) >> error "assertion") pure +category name result = case result of + Left p -> R.category p @?= name + _ -> assertFailure ("expected " ++ name) +pipe = bracket (createPipe >>= \(r,w) -> (,) <$> fdToHandle r <*> fdToHandle w) + (\(r,w) -> hClose r >> (hClose w `catch` (\(_ :: IOException) -> pure ()))) +withStreams use = withSystemTempDirectory "v2-standard" $ \dir -> + bracket (openBinaryFile (dir++"/in") ReadWriteMode) hClose $ \input -> + bracket (openBinaryFile (dir++"/out") ReadWriteMode) hClose $ \output -> + bracket (openBinaryFile (dir++"/err") ReadWriteMode) hClose $ \diagnostic -> + bracket (R.newRuntimeWithStreams input output diagnostic) (\r -> void (R.closeScope r 0)) + (\r -> use r input output diagnostic) + +ioV2StandardTests = localOption (mkTimeout 30000000) $ testGroup "IO v2 inherited streams" + [ testCase "fixed aliases, root borrowing, no close or transfer, bounded entries" $ + withStreams $ \r _ output _ -> do + child <- R.childScope r 0 >>= right + token <- R.standardStream r child R.StandardOutput >>= right + replicateM_ 1000 (R.standardStream r 0 R.StandardOutput >>= (@?= Right token)) + R.liveCounts r >>= (@?= (2,3)) + R.closeResource r child token >>= category "borrowed-resource" + atomically (R.reserveChildSTM r 0 [token]) >>= category "borrowed-resource" + R.liveCounts r >>= (@?= (2,3)) + owned <- R.acquire r 0 (pure (R.Writer (pure . BS.length) (pure ()))) >>= right + atomically (R.reserveChildSTM r 0 [owned, token]) >>= category "borrowed-resource" + R.liveCounts r >>= (@?= (2,4)) + R.writeSome r 0 owned (BS.singleton 97) >>= (@?= Right 1) + R.closeResource r 0 owned >>= (@?= Right ()) + R.closeScope r child >>= (@?= []) + R.writeSome r 0 token (BS.pack [0,255,65]) >>= (@?= Right 3) + R.closeScope r 0 >>= (@?= []) + hIsClosed output >>= (@?= False) + hSeek output AbsoluteSeek 0 + BS.hGet output 3 >>= (@?= BS.pack [0,255,65]) + R.writeSome r 0 token (BS.singleton 1) >>= category "stale-handle" + R.liveCounts r >>= (@?= (0,0)) + , testCase "first lookup during root joining remains available to existing children" $ + withStreams $ \r _ _ _ -> do + child <- R.childScope r 0 >>= right + atomically (R.beginCloseScopeSTM r 0) + token <- R.standardStream r child R.StandardError >>= right + R.writeSome r child token (BS.singleton 255) >>= (@?= Right 1) + R.closeScope r child >>= (@?= []) + R.standardStream r child R.StandardError >>= category "out-of-scope" + , testCase "binary reads, explicit EOF, wrong kinds and cross-run references" $ + withStreams $ \r input _ _ -> do + BS.hPut input (BS.pack [0,255,13,10]); hSeek input AbsoluteSeek 0 + reader <- R.standardStream r 0 R.StandardInput >>= right + writer <- R.standardStream r 0 R.StandardOutput >>= right + R.readSome r 0 reader 4 >>= (@?= Right (BS.pack [0,255,13,10])) + R.readSome r 0 reader 1 >>= (@?= Right BS.empty) + R.readSome r 0 writer 1 >>= category "wrong-kind" + R.writeSome r 0 reader (BS.singleton 1) >>= category "wrong-kind" + withStreams $ \other _ _ _ -> R.readSome other 0 reader 1 >>= category "stale-handle" + , testCase "shared pending-read lease and cancellation leave inherited pipe open" $ + pipe $ \(input, producer) -> withStreams $ \_ _ output diagnostic -> + bracket (R.newRuntimeWithStreams input output diagnostic) (\r -> void (R.closeScope r 0)) $ \r -> do + reader <- R.standardStream r 0 R.StandardInput >>= right + started <- newEmptyMVar; finished <- newEmptyMVar + let work = putMVar started () >> void (R.readSome r 0 reader 1) + waitBlocked worker = do + status <- threadStatus worker + case status of ThreadBlocked _ -> pure (); _ -> yield >> waitBlocked worker + bracket (forkFinally work (\_ -> putMVar finished ())) + (\worker -> killThread worker >> void (timeout 3000000 (readMVar finished))) $ \worker -> do + takeMVar started + -- A second read cannot be used as the barrier: it could win the + -- race and block. Observe the actual worker's blocked state. + timeout 3000000 (waitBlocked worker) >>= (@?= Just ()) + alias <- R.standardStream r 0 R.StandardInput >>= right + R.readSome r 0 alias 1 >>= category "busy" + killThread worker + timeout 3000000 (readMVar finished) >>= (@?= Just ()) + BS.hPut producer (BS.singleton 97); hFlush producer + R.readSome r 0 reader 1 >>= (@?= Right (BS.singleton 97)) + R.closeScope r 0 >>= (@?= []) + hIsClosed input >>= (@?= False) + , testCase "cancelled blocked output poisons the borrowed writer without closing it" $ + pipe $ \(consumer, output) -> withStreams $ \_ input _ diagnostic -> + bracket (R.newRuntimeWithStreams input output diagnostic) (\r -> void (R.closeScope r 0)) $ \r -> do + writer <- R.standardStream r 0 R.StandardOutput >>= right + finished <- newEmptyMVar + let work = forever (R.writeSome r 0 writer (BS.replicate 4096 97) >>= right) + waitBlocked worker = do + status <- threadStatus worker + case status of ThreadBlocked _ -> pure (); _ -> yield >> waitBlocked worker + bracket (forkFinally work (\_ -> putMVar finished ())) + (\worker -> killThread worker >> void (timeout 3000000 (readMVar finished)) >> hClose consumer) $ \worker -> do + timeout 3000000 (waitBlocked worker) >>= (@?= Just ()) + alias <- R.standardStream r 0 R.StandardOutput >>= right + R.writeSome r 0 alias (BS.singleton 98) >>= category "busy" + killThread worker + timeout 3000000 (readMVar finished) >>= (@?= Just ()) + R.writeSome r 0 writer (BS.singleton 98) >>= category "invalid-state" + R.closeScope r 0 >>= (@?= []) + hIsClosed output >>= (@?= False) + , testCase "broken pipes report uncertain progress without closing inherited output" $ + pipe $ \(consumer, output) -> withStreams $ \_ input _ diagnostic -> do + hClose consumer + bracket (R.newRuntimeWithStreams input output diagnostic) (\r -> void (R.closeScope r 0)) $ \r -> do + writer <- R.standardStream r 0 R.StandardOutput >>= right + result <- R.writeSome r 0 writer (BS.singleton 97) + case result of + Left p -> do { R.category p @?= "io-error"; R.progress p @?= "unknown" } + Right _ -> assertFailure "broken pipe acknowledged" + R.writeSome r 0 writer (BS.singleton 98) >>= category "invalid-state" + hIsClosed output >>= (@?= False) + , testCase "output acknowledgement flushes without altering host buffering" $ + withStreams $ \r _ output _ -> do + hSetBuffering output (BlockBuffering (Just 8192)) + writer <- R.standardStream r 0 R.StandardOutput >>= right + R.writeSome r 0 writer (BS.pack [0,255]) >>= (@?= Right 2) + hGetBuffering output >>= (@?= BlockBuffering (Just 8192)) + hFileSize output >>= (@?= 2) + ] diff --git a/test/IOV2StorageTests.hs b/test/IOV2StorageTests.hs @@ -0,0 +1,455 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2StorageTests (ioV2StorageTests) where + +import qualified IOV2.Resources as R +import qualified IOV2.Staging as S +import qualified IOV2.Digest as D +import Control.Concurrent +import Control.Concurrent.STM +import Control.Exception +import Control.Monad +import Data.IORef +import Foreign.C.Error (eXDEV, errnoToIOError) +import Data.List (sort, isInfixOf) +import qualified Data.ByteString.Char8 as BS +import System.Directory +import System.Posix.IO (closeFd) +import System.Posix.Files (getFdStatus, fileID, deviceID) +import System.IO.Temp (withSystemTempDirectory) +import Test.Tasty +import Test.Tasty.HUnit + +right :: Show e => Either e a -> IO a +right (Right value) = pure value +right (Left e) = assertFailure (show e) >> error "assertion failed" +category :: String -> Either R.Problem a -> Assertion +category name (Left p) = R.category p @?= name +category name _ = assertFailure ("expected " ++ name) +withRuntime :: (R.Runtime -> IO a) -> IO a +withRuntime = bracket R.newRuntime (\r -> void (R.closeScope r 0)) +write :: R.Runtime -> R.Token -> String -> IO () +write r token bytes = R.writeSome r 0 token (BS.pack bytes) >>= (@?= Right (length bytes)) +sealed :: S.StageOps -> R.Runtime -> FilePath -> String -> IO R.Token +sealed ops r dir bytes = do + token <- S.openStageWith ops r 0 dir >>= right + write r token bytes + R.sealStage r 0 token >>= (@?= Right ()) + pure token + +ioV2StorageTests :: TestTree +ioV2StorageTests = localOption (mkTimeout 30000000) $ testGroup "IO v2 staging and digests" + [ testCase "independent SHA-256 vectors, bounded updates and consuming finish" $ withRuntime $ \r -> do + forM_ [(0,"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), + (1000,"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0")] $ \(n,expected) -> do + token <- D.openDigest r 0 "sha256" >>= right + replicateM_ n (R.updateDigest r 0 token (BS.replicate 1000 'a') >>= (@?= Right ())) + R.finishDigest r 0 token >>= (@?= Right expected) + R.finishDigest r 0 token >>= category "invalid-state" + R.updateDigest r 0 token (BS.pack "x") >>= category "invalid-state" + R.closeResource r 0 token >>= (@?= Right ()) + D.openDigest r 0 "sha999" >>= category "unsupported" + token <- D.openDigest r 0 "sha256" >>= right + R.updateDigest r 0 token BS.empty >>= category "invalid-bound" + R.updateDigest r 0 token (BS.replicate 4097 'a') >>= category "invalid-bound" + R.writeSome r 0 token (BS.pack "a") >>= category "wrong-kind" + forM_ ["a","b","c"] $ \chunk -> R.updateDigest r 0 token (BS.pack chunk) >>= (@?= Right ()) + R.finishDigest r 0 token >>= (@?= Right "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") + , testCase "staging is invisible at destination until sealed publication" $ + withSystemTempDirectory "v2-stage" $ \dir -> withRuntime $ \r -> do + S.openStage r 0 "" >>= category "invalid-argument" + let target = dir ++ "/target" + BS.writeFile target (BS.pack "old") + token <- S.openStage r 0 dir >>= right + R.publishStage r 0 token True target >>= category "invalid-state" + write r token "new" + BS.readFile target >>= (@?= BS.pack "old") + R.sealStage r 0 token >>= (@?= Right ()) + BS.readFile target >>= (@?= BS.pack "old") + R.writeSome r 0 token (BS.pack "extra") >>= category "invalid-state" + R.publishStage r 0 token True target >>= (@?= Right "published") + BS.readFile target >>= (@?= BS.pack "new") + R.publishStage r 0 token True target >>= category "invalid-state" + R.closeResource r 0 token >>= (@?= Right ()) + listDirectory dir >>= (@?= ["target"]) + , testCase "no-replace preserves existing winner and abandon removes staging" $ + withSystemTempDirectory "v2-stage" $ \dir -> withRuntime $ \r -> do + BS.writeFile (dir++"/target") (BS.pack "winner") + token <- sealed S.nativeOps r dir "loser" + R.publishStage r 0 token False (dir++"/target") >>= (@?= Right "exists") + BS.readFile (dir++"/target") >>= (@?= BS.pack "winner") + R.closeResource r 0 token >>= (@?= Right ()) + listDirectory dir >>= (@?= ["target"]) + , testCase "concurrent no-replace has exactly one whole-file winner" $ + withSystemTempDirectory "v2-race" $ \dir -> withRuntime $ \r -> do + a <- sealed S.nativeOps r dir "first" + b <- sealed S.nativeOps r dir "second" + gate <- newEmptyMVar + outputs <- newEmptyMVar + forM_ [a,b] $ \token -> void $ forkIO $ readMVar gate >> + R.publishStage r 0 token False (dir++"/target") >>= putMVar outputs + putMVar gate () + x <- takeMVar outputs >>= right + y <- takeMVar outputs >>= right + sort [x,y] @?= ["exists","published"] + bytes <- BS.readFile (dir++"/target") + assertBool "whole winner" (bytes `elem` map BS.pack ["first","second"]) + R.closeScope r 0 >>= (@?= []) + listDirectory dir >>= (@?= ["target"]) + , testCase "file sync failure prevents publication and cleanup preserves old target" $ + withSystemTempDirectory "v2-sync" $ \dir -> withRuntime $ \r -> do + let ops = S.nativeOps { S.syncFile = \_ -> ioError (userError "sync failed") } + BS.writeFile (dir++"/target") (BS.pack "old") + token <- S.openStageWith ops r 0 dir >>= right + write r token "new" + R.sealStage r 0 token >>= category "io-error" + R.publishStage r 0 token True (dir++"/target") >>= category "invalid-state" + R.closeScope r 0 >>= (@?= []) + BS.readFile (dir++"/target") >>= (@?= BS.pack "old") + listDirectory dir >>= (@?= ["target"]) + , testCase "post-publication sync failure reports visible output, never rolls it back" $ + withSystemTempDirectory "v2-sync" $ \dir -> withRuntime $ \r -> do + let ops = S.nativeOps { S.syncDirectory = \_ -> ioError (userError "directory sync failed") } + token <- sealed ops r dir "new" + outcome <- R.publishStage r 0 token True (dir++"/target") + case outcome of Left p -> R.progress p @?= "published"; _ -> assertFailure "failure hidden" + R.publishStage r 0 token True (dir++"/target") >>= category "invalid-state" + R.closeScope r 0 >>= (@?= []) + BS.readFile (dir++"/target") >>= (@?= BS.pack "new") + listDirectory dir >>= (@?= ["target"]) + , testCase "ambiguous namespace failure freezes stage and never deletes destination" $ + withSystemTempDirectory "v2-ambiguous" $ \dir -> withRuntime $ \r -> do + let ops = S.nativeOps { S.replaceName = \from to -> renameFile from to >> ioError (userError "lost reply") } + token <- sealed ops r dir "new" + outcome <- R.publishStage r 0 token True (dir++"/target") + case outcome of Left p -> R.progress p @?= "unknown"; _ -> assertFailure "ambiguity lost" + R.publishStage r 0 token True (dir++"/target") >>= category "invalid-state" + R.closeScope r 0 >>= (@?= []) + BS.readFile (dir++"/target") >>= (@?= BS.pack "new") + , testCase "partial failed staged write cannot be sealed or published" $ + withSystemTempDirectory "v2-write" $ \dir -> withRuntime $ \r -> do + let ops = S.nativeOps { S.putBytes = \h bytes -> BS.hPut h (BS.take 2 bytes) >> ioError (userError "partial write") } + token <- S.openStageWith ops r 0 dir >>= right + result <- R.writeSome r 0 token (BS.pack "abcdef") + case result of Left p -> R.progress p @?= "unknown"; _ -> assertFailure "write failure hidden" + R.sealStage r 0 token >>= category "invalid-state" + R.closeScope r 0 >>= (@?= []) + listDirectory dir >>= (@?= []) + , testCase "cancellation settles a staged write before abandonment" $ + withSystemTempDirectory "v2-cancel" $ \dir -> withRuntime $ \r -> do + entered <- newEmptyMVar + blocked <- newEmptyMVar + finished <- newEmptyMVar + let ops = S.nativeOps { S.putBytes = \_ _ -> putMVar entered () >> takeMVar blocked } + token <- S.openStageWith ops r 0 dir >>= right + worker <- forkIO $ (try (R.writeSome r 0 token (BS.pack "x")) :: IO (Either SomeException (Either R.Problem Int))) >>= putMVar finished + takeMVar entered + R.sealStage r 0 token >>= category "busy" + killThread worker + result <- takeMVar finished + case result of Left _ -> pure (); _ -> assertFailure "cancelled write resumed" + R.sealStage r 0 token >>= category "invalid-state" + R.closeScope r 0 >>= (@?= []) + listDirectory dir >>= (@?= []) + , testCase "stage and digest transfer together, with old aliases invalidated" $ + withSystemTempDirectory "v2-transfer" $ \dir -> withRuntime $ \r -> do + stage <- S.openStage r 0 dir >>= right + digest <- D.openDigest r 0 "sha256" >>= right + (child, moved) <- atomically (R.reserveChildSTM r 0 [stage,digest]) >>= right + R.writeSome r 0 stage (BS.pack "abc") >>= category "stale-handle" + R.updateDigest r 0 digest (BS.pack "abc") >>= category "stale-handle" + case moved of + [freshStage,freshDigest] -> do + R.writeSome r child freshStage (BS.pack "abc") >>= (@?= Right 3) + R.updateDigest r child freshDigest (BS.pack "abc") >>= (@?= Right ()) + R.finishDigest r child freshDigest >>= (@?= Right "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") + R.sealStage r child freshStage >>= (@?= Right ()) + R.publishStage r child freshStage False (dir++"/target") >>= (@?= Right "published") + _ -> assertFailure "transfer arity" + R.closeScope r child >>= (@?= []) + BS.readFile (dir++"/target") >>= (@?= BS.pack "abc") + listDirectory dir >>= (@?= ["target"]) + , testCase "cancelled seal closes its descriptor and abandons private output" $ + withSystemTempDirectory "v2-seal-cancel" $ \dir -> withRuntime $ \r -> do + entered <- newEmptyMVar + blocked <- newEmptyMVar + done <- newEmptyMVar + let ops = S.nativeOps { S.syncFile = \fd -> putMVar entered fd >> takeMVar blocked } + stage <- S.openStageWith ops r 0 dir >>= right + write r stage "abc" + worker <- forkIO $ (try (R.sealStage r 0 stage) :: IO (Either SomeException (Either R.Problem ()))) >>= putMVar done + fd <- takeMVar entered + original <- getFdStatus fd + killThread worker + takeMVar done >>= \outcome -> case outcome of Left _ -> pure (); _ -> assertFailure "seal resumed" + R.publishStage r 0 stage True (dir++"/target") >>= category "invalid-state" + R.closeScope r 0 >>= (@?= []) + descriptor <- try (getFdStatus fd) + case descriptor of + Left (_ :: IOException) -> pure () + Right current -> assertBool "original descriptor leaked" + ((deviceID current, fileID current) /= (deviceID original, fileID original)) + listDirectory dir >>= (@?= []) + , testCase "cancellation after namespace publication cannot undo visibility" $ + withSystemTempDirectory "v2-publish-cancel" $ \dir -> withRuntime $ \r -> do + entered <- newEmptyMVar + blocked <- newEmptyMVar + done <- newEmptyMVar + let ops = S.nativeOps { S.syncDirectory = \_ -> putMVar entered () >> takeMVar blocked } + stage <- sealed ops r dir "abc" + worker <- forkIO $ (try (R.publishStage r 0 stage True (dir++"/target")) :: IO (Either SomeException (Either R.Problem String))) >>= putMVar done + takeMVar entered + BS.readFile (dir++"/target") >>= (@?= BS.pack "abc") + killThread worker + takeMVar done >>= \outcome -> case outcome of Left _ -> pure (); _ -> assertFailure "publish resumed" + R.publishStage r 0 stage True (dir++"/target") >>= category "invalid-state" + R.closeScope r 0 >>= (@?= []) + BS.readFile (dir++"/target") >>= (@?= BS.pack "abc") + listDirectory dir >>= (@?= ["target"]) + , testCase "no-replace unlink failure reports published and cleanup retries only private name" $ + withSystemTempDirectory "v2-unlink" $ \dir -> withRuntime $ \r -> do + first <- newIORef True + let ops = S.nativeOps { S.removeName = \path -> do + failNow <- atomicModifyIORef' first (\value -> (False,value)) + if failNow then ioError (userError "unlink failed") else removeFile path } + stage <- sealed ops r dir "abc" + result <- R.publishStage r 0 stage False (dir++"/target") + case result of Left p -> R.progress p @?= "published"; _ -> assertFailure "unlink failure hidden" + R.closeScope r 0 >>= (@?= []) + BS.readFile (dir++"/target") >>= (@?= BS.pack "abc") + listDirectory dir >>= (@?= ["target"]) + , testCase "cross-directory publication succeeds but self-publication is rejected" $ + withSystemTempDirectory "v2-destination" $ \dir -> withRuntime $ \r -> do + createDirectory (dir++"/other") + a <- sealed S.nativeOps r dir "abc" + R.publishStage r 0 a True (dir++"/other/target") >>= (@?= Right "published") + BS.readFile (dir++"/other/target") >>= (@?= BS.pack "abc") + R.closeResource r 0 a >>= (@?= Right ()) + b <- sealed S.nativeOps r dir "abc" + names <- listDirectory dir + let temporary = head (filter (/= "other") names) + R.publishStage r 0 b True (dir++"/"++temporary) >>= category "invalid-destination" + R.closeScope r 0 >>= (@?= []) + listDirectory dir >>= (@?= ["other"]) + , testCase "cross-directory link and replace sync destination then source, preserving existing winners" $ + withSystemTempDirectory "v2-cross-directory" $ \dir -> withRuntime $ \r -> do + let source = dir++"/source"; target = dir++"/target" + createDirectory source + createDirectory target + synced <- newIORef [] + let ops = S.nativeOps { S.openDirectory = \path -> do + modifyIORef' synced (++ [path]) + S.openDirectory S.nativeOps path } + forM_ [False, True] $ \replace -> do + stage <- sealed ops r source (if replace then "replacement" else "new") + writeIORef synced [] + R.publishStage r 0 stage replace (target++"/object") >>= (@?= Right "published") + readIORef synced >>= (@?= [target, source]) + R.closeResource r 0 stage >>= (@?= Right ()) + listDirectory source >>= (@?= []) + BS.readFile (target++"/object") >>= (@?= BS.pack "replacement") + winner <- sealed ops r source "must not replace" + writeIORef synced [] + R.publishStage r 0 winner False (target++"/object") >>= (@?= Right "exists") + readIORef synced >>= (@?= []) -- exists is not an identity/durability claim + R.closeScope r 0 >>= (@?= []) + BS.readFile (target++"/object") >>= (@?= BS.pack "replacement") + listDirectory source >>= (@?= []) + , testCase "cross-filesystem syscall failures never fall back to copying or alter destination" $ + withSystemTempDirectory "v2-exdev" $ \dir -> withRuntime $ \r -> do + let target = dir++"/target" + createDirectory target + BS.writeFile (target++"/object") (BS.pack "keep") + calls <- newIORef (0 :: Int) + let unavailable _ destination = do + modifyIORef' calls (+1) + ioError (errnoToIOError "publish" eXDEV Nothing (Just destination)) + ops = S.nativeOps { S.linkName = unavailable, S.replaceName = unavailable } + forM_ [False, True] $ \replace -> do + stage <- sealed ops r dir "new" + outcome <- R.publishStage r 0 stage replace (target++"/object") + case outcome of + Left p -> R.progress p @?= "unknown" -- never turn errno into replay permission + _ -> assertFailure "cross-filesystem failure hidden" + R.publishStage r 0 stage replace (target++"/object") >>= category "invalid-state" + R.closeResource r 0 stage >>= (@?= Right ()) + BS.readFile (target++"/object") >>= (@?= BS.pack "keep") + readIORef calls >>= (@?= 2) + listDirectory dir >>= (@?= ["target"]) + , testCase "failure syncing either publication directory preserves published data" $ + withSystemTempDirectory "v2-cross-sync" $ \dir -> withRuntime $ \r -> do + let source = dir++"/source"; target = dir++"/target" + createDirectory source + createDirectory target + forM_ [1,2 :: Int] $ \failureAt -> do + count <- newIORef (0 :: Int) + let ops = S.nativeOps { S.syncDirectory = \fd -> do + n <- atomicModifyIORef' count (\n -> (n+1,n+1)) + if n == failureAt then ioError (userError "directory sync failed") + else S.syncDirectory S.nativeOps fd } + stage <- sealed ops r source "visible" + outcome <- R.publishStage r 0 stage True (target++"/object") + case outcome of + Left p -> do + R.progress p @?= "published" + assertBool "sync diagnostic" ("directory sync failed" `isInfixOf` R.detail p) + _ -> assertFailure "directory sync failure hidden" + R.publishStage r 0 stage True (target++"/object") >>= category "invalid-state" + R.closeResource r 0 stage >>= (@?= Right ()) + listDirectory source >>= (@?= []) + BS.readFile (target++"/object") >>= (@?= BS.pack "visible") + , testCase "cross-directory ambiguous link error never unlinks the published destination" $ + withSystemTempDirectory "v2-cross-ambiguous" $ \dir -> withRuntime $ \r -> do + let target = dir++"/target" + createDirectory target + let ops = S.nativeOps { S.linkName = \source destination -> do + S.linkName S.nativeOps source destination + ioError (userError "reply lost after link") } + stage <- sealed ops r dir "visible" + outcome <- R.publishStage r 0 stage False (target++"/object") + case outcome of Left p -> R.progress p @?= "unknown"; _ -> assertFailure "ambiguity hidden" + R.closeScope r 0 >>= (@?= []) + listDirectory dir >>= (@?= ["target"]) + BS.readFile (target++"/object") >>= (@?= BS.pack "visible") + , testCase "cancellation during source-directory sync preserves cross-directory publication" $ + withSystemTempDirectory "v2-cross-cancel" $ \dir -> withRuntime $ \r -> do + let target = dir++"/target" + createDirectory target + count <- newIORef (0 :: Int) + entered <- newEmptyMVar + blocked <- newEmptyMVar + done <- newEmptyMVar + let ops = S.nativeOps { S.syncDirectory = \fd -> do + n <- atomicModifyIORef' count (\n -> (n+1,n+1)) + if n == 2 then putMVar entered () >> takeMVar blocked else S.syncDirectory S.nativeOps fd } + stage <- sealed ops r dir "visible" + worker <- forkIO $ (try (R.publishStage r 0 stage False (target++"/object")) :: IO (Either SomeException (Either R.Problem String))) >>= putMVar done + takeMVar entered + killThread worker + takeMVar done >>= \outcome -> case outcome of Left _ -> pure (); _ -> assertFailure "cancellation lost" + R.closeScope r 0 >>= (@?= []) + listDirectory dir >>= (@?= ["target"]) + BS.readFile (target++"/object") >>= (@?= BS.pack "visible") + , testCase "source-directory close failure is published and never retries the descriptor" $ + withSystemTempDirectory "v2-cross-close" $ \dir -> withRuntime $ \r -> do + let target = dir++"/target" + createDirectory target + calls <- newIORef (0 :: Int) + let ops = S.nativeOps { S.closeDescriptor = \fd -> do + n <- atomicModifyIORef' calls (\n -> (n+1,n+1)) + closeFd fd + when (n == 3) (ioError (userError "source directory close")) } + stage <- sealed ops r dir "visible" + outcome <- R.publishStage r 0 stage True (target++"/object") + case outcome of Left p -> R.progress p @?= "published"; _ -> assertFailure "close failure hidden" + R.closeScope r 0 >>= (@?= []) + readIORef calls >>= (@?= 3) + BS.readFile (target++"/object") >>= (@?= BS.pack "visible") + , testCase "cross-directory publication cannot target another stage's private namespace" $ + withSystemTempDirectory "v2-cross-private" $ \dir -> withRuntime $ \r -> do + let target = dir++"/target" + createDirectory target + stage <- sealed S.nativeOps r dir "source" + other <- sealed S.nativeOps r target "private" + names <- listDirectory target + R.publishStage r 0 stage True (target++"/"++head names) >>= category "invalid-destination" + R.publishStage r 0 other False (target++"/safe") >>= (@?= Right "published") + R.closeScope r 0 >>= (@?= []) + BS.readFile (target++"/safe") >>= (@?= BS.pack "private") + listDirectory target >>= (@?= ["safe"]) + , testCase "repeated digest/stage scopes leave no private files or registry history" $ + withSystemTempDirectory "v2-repeat" $ \dir -> withRuntime $ \r -> do + replicateM_ 100 $ do + child <- R.childScope r 0 >>= right + _ <- S.openStage r child dir >>= right + digest <- D.openDigest r child "sha256" >>= right + R.updateDigest r child digest (BS.pack "abc") >>= (@?= Right ()) + R.closeScope r child >>= (@?= []) + R.liveCounts r >>= (@?= (1,3)) + listDirectory dir >>= (@?= []) + , testCase "failed sync keeps its primary error and reports descriptor release separately" $ + withSystemTempDirectory "v2-sync-close" $ \dir -> withRuntime $ \r -> do + let ops = S.nativeOps + { S.syncFile = \_ -> ioError (userError "primary sync") + , S.closeDescriptor = \fd -> closeFd fd >> ioError (userError "secondary close") } + stage <- S.openStageWith ops r 0 dir >>= right + write r stage "abc" + outcome <- R.sealStage r 0 stage + case outcome of + Left p -> assertBool "sync error masked" ("primary sync" `isInfixOf` R.detail p) + _ -> assertFailure "sync error hidden" + errors <- R.closeScope r 0 + case errors of + [p] -> assertBool "release error hidden" ("secondary close" `isInfixOf` R.detail p) + _ -> assertFailure (show errors) + listDirectory dir >>= (@?= []) + , testCase "failed descriptor close is never retried after possible descriptor reuse" $ + withSystemTempDirectory "v2-close-once" $ \dir -> withRuntime $ \r -> do + attempts <- newIORef (0 :: Int) + let ops = S.nativeOps { S.closeDescriptor = \fd -> do + modifyIORef' attempts (+1) + closeFd fd + ioError (userError "close reported failure") } + stage <- S.openStageWith ops r 0 dir >>= right + write r stage "abc" + R.sealStage r 0 stage >>= category "io-error" + R.closeScope r 0 >>= (@?= []) + readIORef attempts >>= (@?= 1) + listDirectory dir >>= (@?= []) + , testCase "unexpected descriptor release fault still attempts private-name cleanup" $ + withSystemTempDirectory "v2-release-fault" $ \dir -> withRuntime $ \r -> do + let ops = S.nativeOps + { S.syncFile = \_ -> ioError (userError "sync failed") + , S.closeDescriptor = \fd -> closeFd fd >> throwIO (ErrorCall "unexpected close") } + stage <- S.openStageWith ops r 0 dir >>= right + R.sealStage r 0 stage >>= category "io-error" + errors <- R.closeScope r 0 + case errors of [p] -> R.category p @?= "backend-fault"; _ -> assertFailure (show errors) + listDirectory dir >>= (@?= []) + , testCase "directory sync failure preserves its error and owns directory release until settlement" $ + withSystemTempDirectory "v2-directory-close" $ \dir -> withRuntime $ \r -> do + calls <- newIORef (0 :: Int) + let ops = S.nativeOps + { S.syncDirectory = \_ -> ioError (userError "primary directory sync") + , S.closeDescriptor = \fd -> do + count <- atomicModifyIORef' calls (\n -> (n+1,n)) + closeFd fd + when (count == 1) (ioError (userError "secondary directory close")) } + stage <- sealed ops r dir "abc" + result <- R.publishStage r 0 stage False (dir++"/target") + case result of + Left p -> do + R.progress p @?= "published" + assertBool "primary hidden" ("primary directory sync" `isInfixOf` R.detail p) + _ -> assertFailure "directory sync failure hidden" + errors <- R.closeScope r 0 + case errors of + [p] -> assertBool "directory release hidden" ("secondary directory close" `isInfixOf` R.detail p) + _ -> assertFailure (show errors) + readIORef calls >>= (@?= 2) + BS.readFile (dir++"/target") >>= (@?= BS.pack "abc") + listDirectory dir >>= (@?= ["target"]) + , testCase "publication cannot overwrite another stage's private name" $ + withSystemTempDirectory "v2-private-namespace" $ \dir -> withRuntime $ \r -> do + first <- sealed S.nativeOps r dir "first" + original <- listDirectory dir + second <- S.openStage r 0 dir >>= right + write r second "second" + names <- listDirectory dir + assertBool "temporary naming contract" (all (".tricu-stage-" `isInfixOf`) names) + let other = head (filter (`notElem` original) names) + R.publishStage r 0 first True (dir++"/"++other) >>= category "invalid-destination" + write r second "tail" + R.sealStage r 0 second >>= (@?= Right ()) + R.publishStage r 0 second False (dir++"/safe") >>= (@?= Right "published") + R.closeScope r 0 >>= (@?= []) + BS.readFile (dir++"/safe") >>= (@?= BS.pack "secondtail") + listDirectory dir >>= (@?= ["safe"]) + , testCase "explicit abandonment reports removal failure" $ + withSystemTempDirectory "v2-cleanup" $ \dir -> withRuntime $ \r -> do + attempts <- newIORef (0 :: Int) + let ops = S.nativeOps { S.removeName = \_ -> modifyIORef' attempts (+1) >> ioError (userError "remove failed") } + token <- S.openStageWith ops r 0 dir >>= right + R.closeResource r 0 token >>= category "io-error" + readIORef attempts >>= (@?= 1) + R.liveCounts r >>= (@?= (1,3)) + ] diff --git a/test/IOV2SyncTests.hs b/test/IOV2SyncTests.hs @@ -0,0 +1,113 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2SyncTests (ioV2SyncTests) where + +import qualified IOV2.Resources as R +import qualified IOV2.Filesystem as F +import qualified IOV2.Descriptor as D +import Control.Concurrent +import Control.Exception +import Control.Monad (forM_) +import Data.IORef +import qualified Data.ByteString as BS +import Data.List (isInfixOf) +import System.Directory (renameFile) +import System.Posix.Files (createNamedPipe) +import System.FilePath ((</>)) +import System.IO.Temp (withSystemTempDirectory) +import System.Timeout (timeout) +import Test.Tasty +import Test.Tasty.HUnit + +fake = R.FileOps Nothing Nothing Nothing Nothing (pure 0) Nothing Nothing (pure ()) +category wanted value = case value of Left p -> R.category p @?= wanted; Right _ -> assertFailure wanted + +ioV2SyncTests = localOption (mkTimeout 30000000) $ testGroup "IO v2 synchronization" + [ testCase "reader/update/append sync preserves bytes and logical cursor" $ withSystemTempDirectory "tricu-sync" $ \directory -> do + let path = directory </> "file" + BS.writeFile path "abcdef" + runtime <- R.newRuntime + Right reader <- R.openReader runtime R.rootScope path + R.readSome runtime R.rootScope reader 2 >>= (@?= Right "ab") + forM_ [1..10::Int] $ \_ -> R.syncFile runtime R.rootScope reader >>= (@?= Right ()) + R.filePosition runtime R.rootScope reader >>= (@?= Right 2) + R.readSome runtime R.rootScope reader 2 >>= (@?= Right "cd") + R.closeResource runtime R.rootScope reader >>= (@?= Right ()) + Right update <- R.openUpdate runtime R.rootScope path + R.seekFile runtime R.rootScope update 3 >>= (@?= Right ()) + R.writeSome runtime R.rootScope update "X" >>= (@?= Right 1) + R.syncFile runtime R.rootScope update >>= (@?= Right ()) + R.filePosition runtime R.rootScope update >>= (@?= Right 4) + R.closeResource runtime R.rootScope update >>= (@?= Right ()) + Right append <- R.openAppender runtime R.rootScope path + R.writeSome runtime R.rootScope append "Z" >>= (@?= Right 1) + R.syncFile runtime R.rootScope append >>= (@?= Right ()) + R.closeScope runtime R.rootScope >>= (@?= []) + BS.readFile path >>= (@?= "abcXefZ") + , testCase "sync uses the opened inode, not a fresh lookup of its old path" $ withSystemTempDirectory "tricu-sync" $ \directory -> do + let path = directory </> "old" + BS.writeFile path "bytes" + runtime <- R.newRuntime + Right reader <- R.openReader runtime R.rootScope path + renameFile path (directory </> "moved") + R.syncFile runtime R.rootScope reader >>= (@?= Right ()) + F.syncDirectory runtime R.rootScope directory >>= (@?= Right ()) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "directory sync rejects non-directories and missing paths ordinarily" $ withSystemTempDirectory "tricu-sync" $ \directory -> do + let path = directory </> "file" + BS.writeFile path "bytes" + runtime <- R.newRuntime + counts <- R.liveCounts runtime + F.syncDirectory runtime R.rootScope path >>= category "not-directory" + createNamedPipe (directory </> "fifo") 0o600 + F.syncDirectory runtime R.rootScope (directory </> "fifo") >>= category "not-directory" + F.syncDirectory runtime R.rootScope (directory </> "missing") >>= category "not-found" + F.syncDirectory runtime R.rootScope "" >>= category "invalid-argument" + R.liveCounts runtime >>= (@?= counts) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "sync and release failures preserve primary diagnostics without history" $ do + runtime <- R.newRuntime + calls <- newIORef (0::Int) + let actions = pure (ioError (userError "primary sync failure"), + modifyIORef' calls (+1) >> ioError (userError "secondary release failure")) + Right file <- R.acquire runtime R.rootScope (pure (R.File fake {R.fileSyncActions=Just actions})) + R.syncFile runtime R.rootScope file >>= \reply -> case reply of + Left p -> do + R.progress p @?= "unknown" + assertBool "lost primary" ("primary sync failure" `isInfixOf` R.detail p) + assertBool "lost cleanup" ("secondary release failure" `isInfixOf` R.detail p) + _ -> assertFailure "sync failure swallowed" + R.liveCounts runtime >>= (@?= (1,4)) + readIORef calls >>= (@?= 1) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "cancelled sync releases both leases and settles cleanup lexically" $ do + runtime <- R.newRuntime + entered <- newEmptyMVar; gate <- newEmptyMVar; done <- newEmptyMVar + released <- newIORef (0::Int) + let actions = pure (putMVar entered () >> takeMVar gate,modifyIORef' released (+1)) + Right file <- R.acquire runtime R.rootScope (pure (R.File fake {R.fileSyncActions=Just actions})) + worker <- forkFinally (R.syncFile runtime R.rootScope file) (putMVar done) + takeMVar entered + R.syncFile runtime R.rootScope file >>= category "busy" + killThread worker + timeout 3000000 (takeMVar done) >>= \reply -> case reply of + Just (Left err) -> case fromException err of Just ThreadKilled -> pure (); _ -> assertFailure (show err) + _ -> assertFailure "cancellation lost" + readIORef released >>= (@?= 0) + R.closeScope runtime R.rootScope >>= (@?= []) + readIORef released >>= (@?= 1) + , testCase "once-only raw release is not retried after close error or interruption" $ do + forM_ [toException (userError "uncertain close"),toException ThreadKilled] $ \exception -> do + calls <- newIORef (0::Int) + release <- D.releaseOnce (modifyIORef' calls (+1) >> throwIO exception) + first <- try release + case first of Left (_ :: SomeException) -> pure (); Right _ -> assertFailure "injection missing" + release + readIORef calls >>= (@?= 1) + , testCase "unsupported methods and borrowed streams cannot become file sync" $ do + runtime <- R.newRuntime + Right file <- R.acquire runtime R.rootScope (pure (R.File fake)) + R.syncFile runtime R.rootScope file >>= category "unsupported" + Right standard <- R.standardStream runtime R.rootScope R.StandardOutput + R.syncFile runtime R.rootScope standard >>= category "wrong-kind" + R.closeScope runtime R.rootScope >>= (@?= []) + ] diff --git a/test/IOV2TaskTests.hs b/test/IOV2TaskTests.hs @@ -0,0 +1,328 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2TaskTests (ioV2TaskTests) where + +import qualified IOV2.Resources as R +import qualified IOV2.Tasks as S +import Runtime +import Control.Concurrent hiding (yield) +import Control.Exception +import Control.Monad hiding (join) +import Data.IORef +import qualified Data.ByteString as BS +import qualified System.Timeout as Timeout +import Test.Tasty +import Test.Tasty.HUnit + +right :: Show e => Either e a -> IO a +right (Right x) = pure x +right (Left e) = assertFailure (show e) >> error "assertion failed" +wait :: MVar a -> IO a +wait var = Timeout.timeout 3000000 (takeMVar var) >>= maybe (assertFailure "timeout" >> error "timeout") pure +completed :: S.Outcome -> Assertion +completed (S.Completed value) = value @?= Leaf +completed value = assertFailure (show value) +rejected :: String -> Either R.Problem a -> Assertion +rejected name (Left e) = R.category e @?= name +rejected name _ = assertFailure ("expected " ++ name) +withGroup :: (R.Runtime -> S.Scheduler -> S.Group -> IO S.Outcome) -> IO S.Outcome +withGroup action = do + runtime <- R.newRuntime + scheduler <- S.newScheduler runtime + group <- S.rootGroup scheduler + result <- S.runGroup group (action runtime scheduler group) + R.liveCounts runtime >>= (@?= (0,0)) + S.liveCounts scheduler >>= (@?= (0,0,0)) + pure result + +ioV2TaskTests :: TestTree +ioV2TaskTests = localOption (mkTimeout 30000000) $ testGroup "IO v2 structured tasks" + [ testCase "normal exit joins borrowing child before ancestor release" $ do + released <- newIORef False + entered <- newEmptyMVar + finish <- newEmptyMVar + result <- newEmptyMVar + _ <- forkIO $ withGroup (\runtime _ group -> do + token <- R.acquire runtime 0 (pure (R.Reader (const (pure BS.empty)) (writeIORef released True))) >>= right + _ <- S.spawn group False False [] $ \child _ -> do + putMVar entered () + wait finish + readIORef released >>= (@?= False) + R.readSome runtime (S.scopeOf child) token 1 >>= (@?= Right BS.empty) + pure (S.Completed Leaf) + pure (S.Completed Leaf)) >>= putMVar result + wait entered + tryTakeMVar result >>= \value -> case value of Nothing -> pure (); _ -> assertFailure "scope returned before child" + readIORef released >>= (@?= False) + putMVar finish () + wait result >>= completed + readIORef released >>= (@?= True) + , testCase "joined outcome is consumed once, ordinary errors remain values" $ + withGroup (\_ _ group -> do + ref <- S.spawn group False True [] (\_ _ -> pure (S.Completed (Fork Leaf Leaf))) >>= right + S.join group ref >>= right >>= \outcome -> case outcome of + S.Completed value -> value @?= Fork Leaf Leaf + _ -> assertFailure "domain value became terminal failure" + S.join group ref >>= rejected "stale-task" + S.discard group ref >>= rejected "stale-task" + pure (S.Completed Leaf)) >>= completed + , testCase "supervision is installed before execution and preserves lazy diagnostic" $ + withGroup (\_ _ group -> do + ref <- S.spawn group True True [] (\_ _ -> pure (S.Failed (error "diagnostic forced") [])) >>= right + S.join group ref >>= right >>= \outcome -> case outcome of + S.Failed _ [] -> pure () + _ -> assertFailure "supervised failure lost" + pure (S.Completed Leaf)) >>= completed + , testCase "uncontained fault interrupts body, cancels sibling and joins its lease" $ do + siblingReady <- newEmptyMVar + parentReady <- newEmptyMVar + blocked <- newEmptyMVar + resumed <- newIORef False + released <- newIORef False + outcome <- withGroup $ \runtime _ group -> do + _ <- S.spawn group False False [] $ \child _ -> do + token <- R.acquire runtime (S.scopeOf child) (pure (R.Writer + (\_ -> putMVar siblingReady () >> takeMVar blocked) + (writeIORef released True))) >>= right + _ <- R.writeSome runtime (S.scopeOf child) token (BS.singleton 1) + writeIORef resumed True + pure (S.Completed Leaf) + _ <- S.spawn group False False [] $ \_ _ -> do + wait siblingReady + wait parentReady + pure (S.Failed (ofString "child fault") []) + putMVar parentReady () + takeMVar blocked + writeIORef resumed True + pure (S.Completed Leaf) + case outcome of S.Failed diagnostic [] -> diagnostic @?= ofString "child fault"; _ -> assertFailure "fault lost" + readIORef resumed >>= (@?= False) + readIORef released >>= (@?= True) + , testCase "explicit cancellation settles blocked acquisition and reports reason" $ + withGroup (\runtime _ group -> do + entered <- newEmptyMVar + block <- newEmptyMVar + resumed <- newIORef False + ref <- S.spawn group True True [] (\child _ -> do + _ <- R.acquire runtime (S.scopeOf child) (putMVar entered () >> takeMVar block) + writeIORef resumed True + pure (S.Completed Leaf)) >>= right + wait entered + S.cancel group ref (ofString "shutdown") >>= (@?= Right ()) + S.join group ref >>= right >>= \outcome -> case outcome of + S.Cancelled reason [] -> reason @?= ofString "shutdown" + _ -> assertFailure "cancellation not observed" + readIORef resumed >>= (@?= False) + pure (S.Completed Leaf)) >>= completed + , testCase "transfer publication gives child fresh tokens and rejects old aliases" $ + withGroup (\runtime _ group -> do + released <- newIORef False + ready <- newEmptyMVar + finish <- newEmptyMVar + token <- R.acquire runtime 0 (pure (R.Reader (const (pure BS.empty)) (writeIORef released True))) >>= right + ref <- S.spawn group False True [token] (\child tokens -> do + case tokens of + [fresh] -> R.readSome runtime (S.scopeOf child) fresh 1 >>= (@?= Right BS.empty) + _ -> assertFailure "bad fresh tokens" + putMVar ready () + wait finish + pure (S.Completed Leaf)) >>= right + wait ready + R.readSome runtime 0 token 1 >>= rejected "stale-handle" + readIORef released >>= (@?= False) + putMVar finish () + S.join group ref >>= right >>= completed + readIORef released >>= (@?= True) + pure (S.Completed Leaf)) >>= completed + , testCase "failed transfer does not start callback or allocate task history" $ + withGroup (\runtime scheduler group -> do + started <- newIORef False + token <- R.acquire runtime 0 (pure (R.Reader (const (pure BS.empty)) (pure ()))) >>= right + S.spawn group False True [token,token] (\_ _ -> writeIORef started True >> pure (S.Completed Leaf)) >>= rejected "duplicate-handle" + readIORef started >>= (@?= False) + S.liveCounts scheduler >>= (@?= (0,0,0)) + R.readSome runtime 0 token 1 >>= (@?= Right BS.empty) + pure (S.Completed Leaf)) >>= completed + , testCase "repeated joins and discards leave no result archive" $ + withGroup (\_ scheduler group -> do + replicateM_ 500 $ do + ref <- S.spawn group False True [] (\_ _ -> pure (S.Completed (error "value forced"))) >>= right + S.join group ref >>= right >>= \outcome -> case outcome of S.Completed _ -> pure (); _ -> assertFailure "failed" + S.liveCounts scheduler >>= (@?= (0,0,0)) + replicateM_ 500 $ do + ref <- S.spawn group False True [] (\_ _ -> pure (S.Completed (error "discard forced"))) >>= right + S.discard group ref >>= (@?= Right ()) + pure (S.Completed Leaf)) >>= completed + , testCase "cancellation preserves primary and attaches all release errors" $ + withGroup (\runtime _ group -> do + ready <- newEmptyMVar + blocked <- newEmptyMVar + ref <- S.spawn group True True [] (\child _ -> do + replicateM_ 24 $ void $ R.acquire runtime (S.scopeOf child) + (pure (R.Reader (const (pure BS.empty)) (ioError (userError "release")))) >>= right + putMVar ready () + takeMVar blocked + pure (S.Completed Leaf)) >>= right + wait ready + S.cancel group ref (error "reason forced") >>= (@?= Right ()) + S.join group ref >>= right >>= \outcome -> case outcome of + S.Cancelled _ errors -> do + length errors @?= 17 + R.detail (last errors) @?= "8" + _ -> assertFailure "primary cancellation lost" + pure (S.Completed Leaf)) >>= completed + , testCase "explicit cancellation of leased read/write remains sticky after backend catches wakeup" $ + withGroup (\runtime _ group -> do + forM_ [True, False] $ \reading -> do + entered <- newEmptyMVar + blocked <- newEmptyMVar + resumed <- newIORef False + released <- newIORef False + let operation = (putMVar entered () >> takeMVar blocked) + `catch` (\(_ :: AsyncException) -> pure ()) + backend = if reading + then R.Reader (\_ -> operation >> pure BS.empty) (writeIORef released True) + else R.Writer (\_ -> operation >> pure 1) (writeIORef released True) + ref <- S.spawn group True True [] (\child _ -> do + token <- R.acquire runtime (S.scopeOf child) (pure backend) >>= right + if reading then void (R.readSome runtime (S.scopeOf child) token 1) + else void (R.writeSome runtime (S.scopeOf child) token (BS.singleton 1)) + S.checkpoint child + writeIORef resumed True + pure (S.Completed Leaf)) >>= right + wait entered + S.cancel group ref Leaf >>= (@?= Right ()) + S.join group ref >>= right >>= \outcome -> case outcome of + S.Cancelled Leaf [] -> pure () + _ -> assertFailure "backend neutralized cancellation" + readIORef resumed >>= (@?= False) + readIORef released >>= (@?= True) + pure (S.Completed Leaf)) >>= completed + , testCase "cancellation versus completion linearizes to one terminal outcome" $ + withGroup (\_ _ group -> do + replicateM_ 200 $ do + ready <- newEmptyMVar + finish <- newEmptyMVar + cancelled <- newEmptyMVar + ref <- S.spawn group True True [] (\_ _ -> putMVar ready () >> wait finish >> pure (S.Completed Leaf)) >>= right + wait ready + _ <- forkIO (S.cancel group ref Leaf >>= putMVar cancelled) + putMVar finish () + request <- wait cancelled + outcome <- S.join group ref >>= right + case (request, outcome) of + (Right (), S.Cancelled Leaf []) -> pure () + (Left e, S.Completed Leaf) -> assertBool "completion won" (R.category e `elem` ["stale-task", "completed-task"]) + _ -> assertFailure "inconsistent completion/cancellation race" + pure (S.Completed Leaf)) >>= completed + , testCase "self/ancestor joins reject without consuming the ticket" $ + withGroup (\_ _ group -> do + reference <- newEmptyMVar + checked <- newEmptyMVar + ref <- S.spawn group True True [] (\child _ -> do + self <- wait reference + S.join child self >>= rejected "join-cycle" + result <- S.nested child (\inner -> S.join inner self >>= rejected "join-cycle" >> pure (S.Completed Leaf)) + putMVar checked () + pure result) >>= right + putMVar reference ref + wait checked + S.join group ref >>= right >>= completed + pure (S.Completed Leaf)) >>= completed + , testCase "implicit scope joins participate in wait-cycle detection" $ do + outcome <- withGroup $ \_ _ group -> do + aRef <- newEmptyMVar + bRef <- newEmptyMVar + aReady <- newEmptyMVar + b <- S.spawn group False True [] (\child _ -> do + a <- wait aRef + wait aReady + -- A's descendant already waits on B, or will do so after this claim. + result <- S.join child a + case result of Left e -> R.category e @?= "join-cycle"; Right _ -> pure () + pure (S.Completed Leaf)) >>= right + putMVar bRef b + a <- S.spawn group False True [] (\child _ -> do + _ <- S.spawn child False False [] (\grandchild _ -> do + target <- wait bRef + putMVar aReady () + result <- S.join grandchild target + case result of Left e -> R.category e @?= "join-cycle"; Right _ -> pure () + pure (S.Completed Leaf)) >>= right + pure (S.Completed Leaf)) >>= right + putMVar aRef a + pure (S.Completed Leaf) + case outcome of + S.Completed Leaf -> pure () -- Explicit join rejected the last edge. + S.Failed message [] -> message @?= ofString "task join cycle" + _ -> assertFailure "cycle was not settled" + , testCase "cancellation waits for lexical release and preserves its error" $ + withGroup (\runtime _ group -> do + entered <- newEmptyMVar + proceed <- newEmptyMVar + interrupted <- newIORef False + ref <- S.spawn group True True [] (\child _ -> S.nested child (\inner -> do + _ <- R.acquire runtime (S.scopeOf inner) (pure (R.Reader (const (pure BS.empty)) + ((putMVar entered () >> takeMVar proceed >> ioError (userError "release")) + `catch` (\(e :: AsyncException) -> writeIORef interrupted True >> throwIO e)))) >>= right + pure (S.Completed Leaf))) >>= right + wait entered + S.cancel group ref (ofString "shutdown") >>= (@?= Right ()) + putMVar proceed () + S.join group ref >>= right >>= \outcome -> case outcome of + S.Cancelled reason errors -> do + reason @?= ofString "shutdown" + length errors @?= 1 + _ -> assertFailure "lexical cleanup/cancellation lost" + readIORef interrupted >>= (@?= False) + pure (S.Completed Leaf)) >>= completed + , testCase "concurrent ticket claims have exactly one consumer" $ + withGroup (\_ _ group -> do + ready <- newEmptyMVar + finish <- newEmptyMVar + loser <- newEmptyMVar + target <- S.spawn group True True [] (\_ _ -> putMVar ready () >> wait finish >> pure (S.Completed Leaf)) >>= right + wait ready + let consumer child _ = do + result <- S.join child target + case result of + Right (S.Completed Leaf) -> pure (S.Completed (Stem Leaf)) + Left e -> do + R.category e @?= "stale-task" + putMVar loser () + pure (S.Completed Leaf) + _ -> assertFailure "unexpected join outcome" >> pure (S.Failed Leaf []) + a <- S.spawn group True True [] consumer >>= right + b <- S.spawn group True True [] consumer >>= right + wait loser + putMVar finish () + x <- S.join group a >>= right + y <- S.join group b >>= right + case (x,y) of + (S.Completed Leaf, S.Completed (Stem Leaf)) -> pure () + (S.Completed (Stem Leaf), S.Completed Leaf) -> pure () + _ -> assertFailure "ticket consumed more or less than once" + pure (S.Completed Leaf)) >>= completed + , testCase "lexical fault preserves every release diagnostic without duplicate attachment" $ do + outcome <- withGroup $ \runtime _ group -> S.nested group $ \inner -> do + replicateM_ 3 $ void $ R.acquire runtime (S.scopeOf inner) + (pure (R.Reader (const (pure BS.empty)) (ioError (userError "lexical release")))) >>= right + pure (S.Failed (ofString "lexical fault") []) + case outcome of + S.Failed message errors -> do + message @?= ofString "lexical fault" + length errors @?= 3 + _ -> assertFailure "lexical failure lost" + , testCase "nested lexical scope cancellation settles inner resources" $ do + ready <- newEmptyMVar + blocked <- newEmptyMVar + released <- newIORef False + outcome <- withGroup $ \runtime _ group -> do + _ <- S.spawn group False False [] (\_ _ -> wait ready >> pure (S.Failed Leaf [])) + S.nested group $ \inner -> do + _ <- R.acquire runtime (S.scopeOf inner) (pure (R.Reader (const (pure BS.empty)) (writeIORef released True))) >>= right + putMVar ready () + takeMVar blocked + pure (S.Completed Leaf) + case outcome of S.Failed Leaf [] -> pure (); _ -> assertFailure "nested interruption changed primary" + readIORef released >>= (@?= True) + ] diff --git a/test/IOV2Tests.hs b/test/IOV2Tests.hs @@ -0,0 +1,356 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IOV2Tests (ioV2Tests) where + +import qualified IOV2.Resources as R +import qualified IOV2.Driver as V +import IODriver (runIO) +import Runtime +import Control.Concurrent +import Control.Concurrent.STM +import Control.Exception +import Control.Monad +import qualified Data.ByteString as BS +import Data.IORef +import System.Directory (listDirectory) +import System.IO.Temp (withSystemTempDirectory) +import qualified System.Timeout as Timeout +import Test.Tasty +import Test.Tasty.HUnit + +-- Independent wire construction: no production encoder/catalogue expectations. +request :: String -> String -> T -> T +request group operation arguments = Fork (ofNumber 10) + (Fork (ofString group) (Fork (ofNumber 1) (Fork (ofString operation) arguments))) + +right :: Show e => Either e a -> IO a +right (Right value) = pure value +right (Left e) = assertFailure (show e) >> error "assertion failed" +categoryIs :: String -> Either R.Problem a -> Assertion +categoryIs expected result = case result of + Left p -> R.category p @?= expected + Right _ -> assertFailure ("expected " ++ expected) + +withRuntime :: (R.Runtime -> IO a) -> IO a +withRuntime = bracket R.newRuntime (\r -> void (R.closeScope r R.rootScope)) +spawn :: IO a -> IO (ThreadId, MVar (Either SomeException a)) +spawn action = do + result <- newEmptyMVar + tid <- forkIO (try action >>= putMVar result) + pure (tid, result) +wait :: MVar a -> IO a +wait result = Timeout.timeout 3000000 (takeMVar result) >>= maybe (assertFailure "thread timeout" >> error "timeout") pure + +ioV2Tests :: TestTree +ioV2Tests = localOption (mkTimeout 30000000) $ testGroup "IO v2 resource/file foundation" + [ testCase "bounded binary copy, EOF, no grants" $ + withSystemTempDirectory "v2-copy" $ \dir -> withRuntime $ \runtime -> do + let bytes = BS.pack (take 20003 (cycle [0..255])) + BS.writeFile (dir++"/in") bytes + child <- R.childScope runtime 0 >>= right + reader <- R.openReader runtime child (dir++"/in") >>= right + writer <- R.openWriter runtime child (dir++"/out") >>= right + let copy = do + part <- R.readSome runtime child reader 997 >>= right + unless (BS.null part) $ do + n <- R.writeSome runtime child writer part >>= right + n @?= BS.length part + copy + copy + R.readSome runtime child reader 5 >>= (@?= Right BS.empty) + R.closeScope runtime child >>= (@?= []) + BS.readFile (dir++"/out") >>= (@?= bytes) + R.liveCounts runtime >>= (@?= (1,3)) + R.readSome runtime 0 reader 1 >>= categoryIs "stale-handle" + , testCase "scope ancestry, wrong kind, bounds and cross-run handles" $ + withRuntime $ \runtime -> withRuntime $ \other -> do + a <- R.childScope runtime 0 >>= right + b <- R.childScope runtime 0 >>= right + child <- R.childScope runtime a >>= right + reader <- R.acquire runtime a (pure (R.Reader (const (pure BS.empty)) (pure ()))) >>= right + R.readSome runtime child reader 1 >>= (@?= Right BS.empty) + R.readSome runtime b reader 1 >>= categoryIs "out-of-scope" + R.readSome runtime 0 reader 1 >>= categoryIs "out-of-scope" + R.readSome other 0 reader 1 >>= categoryIs "stale-handle" + R.writeSome runtime a reader (BS.singleton 1) >>= categoryIs "wrong-kind" + forM_ [0,4097] $ \n -> R.readSome runtime a reader n >>= categoryIs "invalid-bound" + R.closeResource runtime a reader >>= (@?= Right ()) + R.closeResource runtime a reader >>= categoryIs "stale-handle" + , testCase "second acquisition failure releases first at scope exit" $ + withSystemTempDirectory "v2-open" $ \dir -> withRuntime $ \runtime -> do + released <- newIORef False + scope <- R.childScope runtime 0 >>= right + _ <- R.acquire runtime scope (pure (R.Reader (const (pure BS.empty)) (writeIORef released True))) >>= right + R.openReader runtime scope (dir++"/missing") >>= categoryIs "not-found" + R.closeScope runtime scope >>= (@?= []) + readIORef released >>= (@?= True) + , testCase "release order and cleanup errors do not skip remaining resources" $ + withRuntime $ \runtime -> do + order <- newIORef ([] :: [Int]) + scope <- R.childScope runtime 0 >>= right + forM_ [1,2,3] $ \i -> do + _ <- R.acquire runtime scope (pure (R.Reader (const (pure BS.empty)) + (modifyIORef' order (++[i]) >> when (i == 2) (ioError (userError "close failure"))))) >>= right + pure () + errors <- R.closeScope runtime scope + map R.operation errors @?= ["close"] + readIORef order >>= (@?= [3,2,1]) + R.liveCounts runtime >>= (@?= (1,3)) + , testCase "pending lease is busy; close waits; cancellation releases lease" $ + withRuntime $ \runtime -> do + entered <- newEmptyMVar + blocked <- newEmptyMVar + released <- newEmptyMVar + token <- R.acquire runtime 0 (pure (R.Reader (\_ -> putMVar entered () >> takeMVar blocked) + (putMVar released ()))) >>= right + (reader, readDone) <- spawn (R.readSome runtime 0 token 1) + wait entered + R.readSome runtime 0 token 1 >>= categoryIs "busy" + (_, closeDone) <- spawn (R.closeResource runtime 0 token) + -- Wait for closing admission, not a timing guess about the close worker. + let closing = do + result <- R.readSome runtime 0 token 1 + case result of + Left p | R.category p == "closed-handle" -> pure () + Left p | R.category p == "busy" -> yield >> closing + _ -> assertFailure "unexpected lease state" + (Timeout.timeout 3000000 closing >>= (@?= Just ())) `onException` killThread reader + tryTakeMVar released >>= (@?= Nothing) + killThread reader + readResult <- wait readDone + case readResult of Left e -> assertBool "async cancellation" (case fromException e :: Maybe AsyncException of Just ThreadKilled -> True; _ -> False) + Right _ -> assertFailure "cancelled read resumed" + wait closeDone >>= right >>= (@?= Right ()) + wait released + R.liveCounts runtime >>= (@?= (1,3)) + , testCase "scope closure drains an acquisition already admitted" $ + withRuntime $ \runtime -> do + entered <- newEmptyMVar + proceed <- newEmptyMVar + released <- newEmptyMVar + scope <- R.childScope runtime 0 >>= right + (_, acquired) <- spawn (R.acquire runtime scope (putMVar entered () >> takeMVar proceed >> + pure (R.Reader (const (pure BS.empty)) (putMVar released ())))) + wait entered + (_, closed) <- spawn (R.closeScope runtime scope) + putMVar proceed () + wait acquired >>= right >>= right >>= \_ -> pure () + wait closed >>= right >>= (@?= []) + wait released + R.liveCounts runtime >>= (@?= (1,3)) + , testCase "cancelled acquisition leaves no admission or resource behind" $ + withRuntime $ \runtime -> do + entered <- newEmptyMVar + gate <- newEmptyMVar + scope <- R.childScope runtime 0 >>= right + (worker, finished) <- spawn (R.acquire runtime scope (putMVar entered () >> takeMVar gate)) + wait entered + killThread worker + outcome <- wait finished + case outcome of Left _ -> pure (); Right _ -> assertFailure "acquisition resumed" + R.closeScope runtime scope >>= (@?= []) + R.liveCounts runtime >>= (@?= (1,3)) + , testCase "partial write progress and invalid backend progress" $ + withRuntime $ \runtime -> do + writer <- R.acquire runtime 0 (pure (R.Writer (\_ -> pure 2) (pure ()))) >>= right + R.writeSome runtime 0 writer (BS.pack [1,2,3]) >>= (@?= Right 2) + badWriter <- R.acquire runtime 0 (pure (R.Writer (\_ -> pure 0) (pure ()))) >>= right + result <- R.writeSome runtime 0 badWriter (BS.singleton 1) + categoryIs "io-error" result + case result of Left p -> R.progress p @?= "unknown"; _ -> assertFailure "bad progress accepted" + , testCase "driver host cancellation closes resources and never resumes continuation" $ + withSystemTempDirectory "v2-cancel" $ \dir -> do + runtime <- R.newRuntime + entered <- newEmptyMVar + gate <- newEmptyMVar + released <- newIORef False + token@(R.Token identity key) <- R.acquire runtime 0 + (pure (R.Writer (\_ -> putMVar entered () >> takeMVar gate) (writeIORef released True))) >>= right + let encoded = ofList [ofString "io.v2.resource", ofNumber identity, ofNumber key] + scope = ofList [ofString "io.v2.scope", ofNumber identity, Leaf] + write = request "stream" "write" (Fork encoded (ofBytes (BS.singleton 1))) + -- If resumed, this would create an observable file. + next = Fork Leaf (request "file" "open-writer" (Fork scope (ofString (dir++"/SHOULD-NOT-EXIST")))) + program = Fork (ofNumber 1) (Fork write next) + (worker, finished) <- spawn (V.runInRuntime runtime program) + wait entered + killThread worker + outcome <- wait finished + case outcome of Left e -> assertBool "async outcome not converted to Result" (case fromException e :: Maybe AsyncException of Just ThreadKilled -> True; _ -> False) + Right _ -> assertFailure "cancelled driver returned normally" + readIORef released >>= (@?= True) + R.liveCounts runtime >>= (@?= (0,0)) + R.writeSome runtime 0 token (BS.singleton 1) >>= categoryIs "stale-handle" + files <- listDirectory dir + files @?= [] + , testCase "driver preserves primary failure, aggregates bounded cleanup errors" $ do + runtime <- R.newRuntime + closed <- newIORef (0 :: Int) + replicateM_ 40 $ do + _ <- R.acquire runtime 0 (pure (R.Reader (const (pure BS.empty)) + (modifyIORef' closed (+1) >> throwIO (ErrorCall "backend close fault")))) >>= right + pure () + outcome <- V.runInRuntime runtime (Fork (ofNumber 2) (error "primary diagnostic forced")) + case outcome of + V.Failed _ errors -> do + length errors @?= 17 + R.category (last errors) @?= "cleanup-omitted" + R.detail (last errors) @?= "24" + _ -> assertFailure "primary failure lost" + readIORef closed >>= (@?= 40) + R.liveCounts runtime >>= (@?= (0,0)) + , testCase "successful body becomes failed when cleanup fails" $ do + runtime <- R.newRuntime + _ <- R.acquire runtime 0 (pure (R.Reader (const (pure BS.empty)) (ioError (userError "close")))) >>= right + outcome <- V.runInRuntime runtime (Fork (ofNumber 0) Leaf) + case outcome of V.Failed _ [] -> pure (); _ -> assertFailure "cleanup failure silently discarded" + , testCase "repeated lexical scopes do not retain closed resources or scope history" $ + withRuntime $ \runtime -> replicateM_ 1000 $ do + scope <- R.childScope runtime 0 >>= right + _ <- R.acquire runtime scope (pure (R.Reader (const (pure BS.empty)) (pure ()))) >>= right + R.closeScope runtime scope >>= (@?= []) + R.liveCounts runtime >>= (@?= (1,3)) + , testCase "closing admission preserves child borrowing and child-local acquisition" $ + withRuntime $ \runtime -> do + released <- newIORef (0 :: Int) + let backend = pure (R.Reader (const (pure BS.empty)) (modifyIORef' released (+1))) + token <- R.acquire runtime 0 backend >>= right + child <- R.childScope runtime 0 >>= right + atomically (R.beginCloseScopeSTM runtime 0) + R.acquire runtime 0 backend >>= categoryIs "closed-scope" + R.childScope runtime 0 >>= categoryIs "closed-scope" + R.readSome runtime child token 1 >>= (@?= Right BS.empty) + local <- R.acquire runtime child backend >>= right + grandchild <- R.childScope runtime child >>= right + R.readSome runtime grandchild local 1 >>= (@?= Right BS.empty) + readIORef released >>= (@?= 0) + R.closeScope runtime child >>= (@?= []) + readIORef released >>= (@?= 1) + R.readSome runtime 0 token 1 >>= (@?= Right BS.empty) + R.closeScope runtime 0 >>= (@?= []) + readIORef released >>= (@?= 2) + R.liveCounts runtime >>= (@?= (0,0)) + , testCase "handoff rekeys aliases and preserves acquisition order, not argument order" $ + withRuntime $ \runtime -> do + order <- newIORef ([] :: [Int]) + let backend n = pure (R.Reader (const (pure (BS.singleton (fromIntegral n)))) + (modifyIORef' order (++[n]))) + first <- R.acquire runtime 0 (backend 1) >>= right + second <- R.acquire runtime 0 (backend 2) >>= right + (child, fresh) <- atomically (R.reserveChildSTM runtime 0 [second, first]) >>= right + case fresh of + [newSecond, newFirst] -> do + forM_ [first, second] $ \old -> do + R.readSome runtime child old 1 >>= categoryIs "stale-handle" + R.closeResource runtime 0 old >>= categoryIs "stale-handle" + R.readSome runtime 0 newFirst 1 >>= categoryIs "out-of-scope" + R.readSome runtime child newFirst 1 >>= (@?= Right (BS.singleton 1)) + R.readSome runtime child newSecond 1 >>= (@?= Right (BS.singleton 2)) + _ -> assertFailure "wrong transfer arity" + _ <- R.acquire runtime child (backend 3) >>= right + R.closeScope runtime child >>= (@?= []) + readIORef order >>= (@?= [3,2,1]) + R.liveCounts runtime >>= (@?= (1,3)) + , testCase "failed transfer validates all handles without partial movement" $ + withRuntime $ \runtime -> withRuntime $ \other -> do + let backend = pure (R.Reader (const (pure BS.empty)) (pure ())) + token <- R.acquire runtime 0 backend >>= right + foreignToken <- R.acquire other 0 backend >>= right + child <- R.childScope runtime 0 >>= right + local <- R.acquire runtime child backend >>= right + let rejected owner tokens expected = do + before <- R.liveCounts runtime + atomically (R.reserveChildSTM runtime owner tokens) >>= categoryIs expected + R.liveCounts runtime >>= (@?= before) + R.readSome runtime 0 token 1 >>= (@?= Right BS.empty) + R.readSome runtime child local 1 >>= (@?= Right BS.empty) + rejected 0 [token, token] "duplicate-handle" + rejected 0 [token, R.Token (-1) 999] "stale-handle" + rejected 0 [token, foreignToken] "stale-handle" + rejected 0 [token, local] "out-of-scope" + rejected child [local, token] "not-owner" + atomically (R.beginCloseScopeSTM runtime child) + rejected child [local] "closed-scope" + , testCase "handoff transaction rolls back with task-publication failure" $ + withRuntime $ \runtime -> do + token <- R.acquire runtime 0 (pure (R.Reader (const (pure BS.empty)) (pure ()))) >>= right + publication <- newTVarIO False + result <- try $ atomically $ do + reserved <- R.reserveChildSTM runtime 0 [token] + case reserved of + Left _ -> error "reservation should succeed" + Right _ -> writeTVar publication True >> throwSTM (ErrorCall "publication failed") + case result of Left (_ :: ErrorCall) -> pure (); Right () -> assertFailure "expected rollback" + readTVarIO publication >>= (@?= False) + R.liveCounts runtime >>= (@?= (1,4)) + R.readSome runtime 0 token 1 >>= (@?= Right BS.empty) + R.closeResource runtime 0 token >>= (@?= Right ()) + , testCase "handoff rejects a leased or closing resource, with no partial movement" $ + withRuntime $ \runtime -> do + entered <- newEmptyMVar + proceed <- newEmptyMVar + released <- newIORef False + first <- R.acquire runtime 0 (pure (R.Reader (const (pure BS.empty)) (pure ()))) >>= right + busy <- R.acquire runtime 0 (pure (R.Reader (\_ -> putMVar entered () >> takeMVar proceed) + (writeIORef released True))) >>= right + (_, reading) <- spawn (R.readSome runtime 0 busy 1) + wait entered + atomically (R.reserveChildSTM runtime 0 [first, busy]) >>= categoryIs "busy" + R.liveCounts runtime >>= (@?= (1,5)) + (_, closing) <- spawn (R.closeResource runtime 0 busy) + let marked = do + result <- atomically (R.reserveChildSTM runtime 0 [first, busy]) + case result of + Left e | R.category e == "closed-handle" -> pure () + Left e | R.category e == "busy" -> yield >> marked + _ -> assertFailure "unexpected transfer outcome" + Timeout.timeout 3000000 marked >>= (@?= Just ()) + R.readSome runtime 0 first 1 >>= (@?= Right BS.empty) + readIORef released >>= (@?= False) + putMVar proceed BS.empty + wait reading >>= right >>= (@?= Right BS.empty) + wait closing >>= right >>= (@?= Right ()) + R.liveCounts runtime >>= (@?= (1,4)) + , testCase "close versus handoff has one winner and one backend release" $ + replicateM_ 200 $ withRuntime $ \runtime -> do + released <- newIORef (0 :: Int) + token <- R.acquire runtime 0 (pure (R.Reader (const (pure BS.empty)) + (atomicModifyIORef' released (\n -> (n+1, ()))))) >>= right + gate <- newTVarIO False + let start = atomically (readTVar gate >>= check) + (_, moved) <- spawn (start >> atomically (R.reserveChildSTM runtime 0 [token])) + (_, closed) <- spawn (start >> R.closeResource runtime 0 token) + atomically (writeTVar gate True) + handoff <- wait moved >>= right + close <- wait closed >>= right + case handoff of + Right (child, [fresh]) -> do + categoryIs "stale-handle" close + readIORef released >>= (@?= 0) + R.readSome runtime child fresh 1 >>= (@?= Right BS.empty) + R.closeScope runtime child >>= (@?= []) + Left e -> do + assertBool "close won" (R.category e `elem` ["stale-handle", "closed-handle"]) + close @?= Right () + _ -> assertFailure "wrong handoff arity" + readIORef released >>= (@?= 1) + R.liveCounts runtime >>= (@?= (1,3)) + , testCase "strict octet decode stops without forcing excess element" $ do + V.decodeChunk (ofBytes (BS.pack [0..255])) @?= Right (BS.pack [0..255]) + assertBool "noncanonical zero rejected" (case V.decodeChunk (ofList [Fork Leaf Leaf]) of Left _ -> True; _ -> False) + let tooLong = foldr Fork (Fork (error "excess head forced") (error "excess suffix forced")) (replicate 4096 Leaf) + V.decodeChunk tooLong @?= Left "chunk exceeds bound" + , testCase "v2 dispatch, ordinary errors, lazy failure and version rejection" $ do + let action code x = Fork (ofNumber code) x + envelope version x = Fork (ofString "tricuIO") (Fork (ofNumber version) x) + runIO (envelope 2 (action 0 (ofNumber 7))) >>= (@?= Right (ofNumber 7)) + failed <- V.runDetailed (action 2 (error "diagnostic was forced")) + case failed of V.Failed _ [] -> pure (); _ -> assertFailure "terminal failure lost" + runIO (envelope 99 (action 0 Leaf)) >>= (@?= Left "Unsupported tricuIO version") + let badVersion = Fork Leaf (Fork (Stem Leaf) (Fork Leaf Leaf)) + runIO (Fork (ofString "tricuIO") (Fork badVersion (error "invalid version body forced"))) + >>= (@?= Left "invalid IO envelope version") + runIO (Fork (ofString "tricuIO") (Fork (ofNumber 65536) (error "excess version body forced"))) + >>= (@?= Left "invalid IO envelope version") + ordinary <- V.runDetailed (request "stream" "read" Leaf) + case ordinary of V.Completed (Fork Leaf _) -> pure (); _ -> assertFailure "argument failure not ordinary Result" + ] diff --git a/test/IOV2UTF8Tests.hs b/test/IOV2UTF8Tests.hs @@ -0,0 +1,77 @@ +module IOV2UTF8Tests (ioV2UTF8Tests) where + +import qualified IOV2.UTF8 as U +import qualified IOV2.Resources as R +import qualified IOV2.Driver as V +import Runtime (T(..), ofList, ofNumber, ofString, ofBytes) +import qualified Data.ByteString as BS +import Data.Char (chr) +import qualified Data.Text as Text +import qualified Data.Text.Encoding as E +import Control.Monad (forM_) +import Test.Tasty +import Test.Tasty.HUnit + +category wanted value = case value of Left p -> R.category p @?= wanted; Right _ -> assertFailure wanted +request op payload = Fork (ofNumber 10) (Fork (ofString "utf8") (Fork (ofNumber 1) (Fork (ofString op) payload))) +bad op action = V.runDetailed action >>= \result -> case result of + V.Completed (Fork Leaf (Fork problem Leaf)) -> problem @?= ofList (map ofString ["invalid-argument","utf8/"++op,"","none"]) + _ -> assertFailure "expected ordinary argument failure" + +ioV2UTF8Tests = localOption (mkTimeout 30000000) $ testGroup "Strict bounded UTF-8" + [ testCase "independent boundary vectors, NUL, BOM and noncharacters" $ do + forM_ [(0,[0]),(0x7f,[0x7f]),(0x80,[0xc2,0x80]),(0x7ff,[0xdf,0xbf]), + (0x800,[0xe0,0xa0,0x80]),(0xd7ff,[0xed,0x9f,0xbf]),(0xe000,[0xee,0x80,0x80]), + (0xfeff,[0xef,0xbb,0xbf]),(0xffff,[0xef,0xbf,0xbf]), + (0x10000,[0xf0,0x90,0x80,0x80]),(0x10ffff,[0xf4,0x8f,0xbf,0xbf])] $ \(scalar,octets) -> do + let bytes = BS.pack octets + U.encode [chr scalar] @?= Right bytes + U.decode True bytes @?= Right ([chr scalar],BS.empty) + forM_ [1..BS.length bytes-1] $ \split -> do + U.decode False (BS.take split bytes) @?= Right ([],BS.take split bytes) + U.decode True (BS.take split bytes) `seq` category "invalid-data" (U.decode True (BS.take split bytes)) + , testCase "every Unicode scalar agrees with the standard UTF-8 encoder" $ do + forM_ ([0..0xd7ff] ++ [0xe000..0x10ffff]) $ \scalar -> do + let text = [chr scalar] + bytes = E.encodeUtf8 (Text.pack text) + U.decode True bytes @?= Right (text,BS.empty) + , testCase "incomplete valid suffix is explicit and composes at every split" $ do + let text = "A\0λ😀\r\n\xfeff" + bytes = E.encodeUtf8 (Text.pack text) + forM_ [0..BS.length bytes] $ \split -> do + case U.decode False (BS.take split bytes) of + Left p -> assertFailure (show p) + Right (first,carry) -> do + assertBool "oversized carry" (BS.length carry <= 3) + U.decode True (carry <> BS.drop split bytes) @?= Right (drop (length first) text,BS.empty) + case U.decode True (carry <> BS.drop split bytes) of + Right (last,_) -> first ++ last @?= text + _ -> assertFailure "split decode failed" + , testCase "malformed and noncanonical prefixes fail even before final input" $ do + forM_ [[0x80],[0xc0],[0xc1],[0xf5],[0xff],[0xe0,0x80],[0xed,0xa0], + [0xf0,0x80],[0xf4,0x90],[0xe2,0x28],[0xf0,0x90,0x7f], + [0xc2,0x7f],[0xe0,0xa0,0x7f],[0xf0,0x90,0x80,0xc0]] $ \bytes -> + forM_ [False,True] $ \final -> category "invalid-data" (U.decode final (BS.pack bytes)) + case U.decode False ("good" <> BS.singleton 0xff) of + Left p -> R.detail p @?= "invalid UTF-8 leading byte at byte 4" + _ -> assertFailure "invalid suffix accepted" + , testCase "bounds and surrogate controls do not demand excess characters" $ do + U.encode (replicate 1024 '\x10ffff') @?= Right (BS.concat (replicate 1024 (BS.pack [0xf4,0x8f,0xbf,0xbf]))) + category "invalid-bound" (U.encode (replicate 1024 'a' ++ (error "excess scalar":error "excess tail"))) + forM_ [0xd800,0xdfff] $ \n -> category "invalid-data" (U.encode [chr n]) + category "invalid-bound" (U.decode False (BS.replicate 4097 0)) + U.decode True BS.empty @?= Right ([],BS.empty) + U.encode [] @?= Right BS.empty + , testCase "wire scalar controls reject aliases, surrogates, overflow and excess" $ do + forM_ [Fork Leaf Leaf,ofNumber 0xd800,ofNumber 0x110000] $ \scalar -> + bad "encode" (request "encode" (ofList [scalar])) + let excess = foldr Fork (Fork (error "excess wire scalar") (error "excess wire tail")) (replicate 1024 (ofNumber 97)) + bad "encode" (request "encode" excess) + bad "decode" (request "decode" (ofList [ofNumber 2,error "invalid final forced bytes"])) + bad "decode" (request "decode" (ofList [ofNumber 1,ofList [ofNumber 256]])) + , testCase "wire empty text and NUL are valid, distinct values" $ do + forM_ [(Leaf,BS.empty),(ofList [Leaf],BS.singleton 0)] $ \(text,bytes) -> + V.runDetailed (request "encode" text) >>= \result -> case result of + V.Completed value -> value @?= Fork (Stem Leaf) (Fork (ofBytes bytes) Leaf) + _ -> assertFailure "valid text rejected" + ] diff --git a/test/IndexedWriterTests.hs b/test/IndexedWriterTests.hs @@ -0,0 +1,137 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module IndexedWriterTests (indexedWriterTests) where + +import qualified IOV2.Codec as C +import qualified IOV2.Resources as R +import ContentStore.Indexed +import ContentStore.Object +import Runtime (T(..)) +import Wire +import Control.Concurrent +import Control.Concurrent.STM (atomically) +import Control.Exception +import Control.Monad (forM_, when) +import qualified Data.ByteString as BS +import qualified Data.Text as Text +import Data.IORef +import Test.Tasty +import Test.Tasty.HUnit +import System.Timeout (timeout) + +pin = Text.unpack . hashObject indexedTreeDomain +category wanted reply = case reply of + Left p -> R.category p @?= wanted + Right _ -> assertFailure ("expected " ++ wanted) + +indexedWriterTests = localOption (mkTimeout 30000000) $ testGroup "Bounded indexed writer" + [ testCase "chunked emission matches historical canonical bytes at every boundary" $ do + forM_ [Leaf, Stem Leaf, Fork Leaf (Stem Leaf), iterate Stem Leaf !! 1000] $ \tree -> do + bundle <- buildBundleShared [("root",tree)] + let expected = encodeBundle bundle + bundleEncodedLength bundle @?= toInteger (BS.length expected) + forM_ [1,7,13,4096] $ \bound -> do + chunks <- newIORef [] + emitBundle bound bundle $ \bytes -> do + assertBool "bad chunk" (not (BS.null bytes) && BS.length bytes <= bound) + modifyIORef' chunks (bytes:) + actual <- BS.concat . reverse <$> readIORef chunks + actual @?= expected + , testCase "native hash preserves shared identity and canonical domain" $ do + let shared = iterate (\value -> Fork value value) Leaf !! 200 + bytes <- encodeIndexedTree shared + C.hashIndexed 65536 shared >>= (@?= Right (toInteger (BS.length bytes),pin bytes)) + C.hashIndexed (toInteger (BS.length bytes)-1) shared >>= category "invalid-bound" + C.hashIndexed (-1) (error "invalid bound forced tree") >>= category "invalid-bound" + , testCase "partial native writes produce exactly the canonical frame" $ do + runtime <- R.newRuntime + chunks <- newIORef [] + let tree = iterate Stem Leaf !! 1000 + bytes <- encodeIndexedTree tree + Right writer <- R.acquire runtime R.rootScope $ pure $ R.Writer (\chunk -> do + assertBool "write exceeded chunk bound" (BS.length chunk <= 4096) + let accepted = BS.take 7 chunk + modifyIORef' chunks (accepted:) + pure (BS.length accepted)) (pure ()) + C.writeIndexed runtime R.rootScope writer 65536 (pin bytes) tree + >>= (@?= Right (toInteger (BS.length bytes),pin bytes)) + (BS.concat . reverse <$> readIORef chunks) >>= (@?= bytes) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "preflight failures write nothing and invalid headers do not force trees" $ do + runtime <- R.newRuntime + touched <- newIORef False + Right writer <- R.acquire runtime R.rootScope $ pure $ R.Writer + (\bytes -> writeIORef touched True >> pure (BS.length bytes)) (pure ()) + bytes <- encodeIndexedTree Leaf + C.writeIndexed runtime R.rootScope writer 65536 (replicate 64 '0') Leaf >>= category "integrity-failed" + C.writeIndexed runtime R.rootScope writer 0 (pin bytes) Leaf >>= category "invalid-bound" + C.writeIndexed runtime R.rootScope writer (-1) (pin bytes) (error "bad limit forced tree") >>= category "invalid-bound" + C.writeIndexed runtime R.rootScope writer 65536 "bad" (error "bad hash forced tree") >>= category "invalid-argument" + Right reader <- R.acquire runtime R.rootScope (pure (R.Reader (\_ -> pure BS.empty) (pure ()))) + C.writeIndexed runtime R.rootScope reader 65536 (pin bytes) (error "wrong kind forced tree") >>= category "wrong-kind" + readIORef touched >>= (@?= False) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "invalid backend progress stops without replay" $ do + forM_ [0,-1,65536] $ \badCount -> do + runtime <- R.newRuntime + calls <- newIORef (0 :: Int) + Right writer <- R.acquire runtime R.rootScope $ pure $ R.Writer + (\_ -> modifyIORef' calls (+1) >> pure badCount) (pure ()) + bytes <- encodeIndexedTree Leaf + C.writeIndexed runtime R.rootScope writer 65536 (pin bytes) Leaf >>= \reply -> case reply of + Left p -> do R.category p @?= "io-error"; R.progress p @?= "unknown" + Right _ -> assertFailure "invalid progress accepted" + readIORef calls >>= (@?= 1) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "failure after acknowledged chunks never retries or reports rollback" $ do + runtime <- R.newRuntime + calls <- newIORef (0 :: Int) + output <- newIORef [] + let tree = iterate Stem Leaf !! 1000 + bytes <- encodeIndexedTree tree + Right writer <- R.acquire runtime R.rootScope $ pure $ R.Writer (\chunk -> do + n <- atomicModifyIORef' calls (\n -> (n+1,n)) + if n == 0 then modifyIORef' output (chunk:) >> pure (BS.length chunk) + else ioError (userError "injected uncertain write")) (pure ()) + C.writeIndexed runtime R.rootScope writer 65536 (pin bytes) tree >>= \reply -> case reply of + Left p -> R.progress p @?= "unknown" + Right _ -> assertFailure "failure swallowed" + readIORef calls >>= (@?= 2) + (BS.concat . reverse <$> readIORef output) >>= (@?= BS.take 4096 bytes) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "exact verification accepts fragmented frames without consuming following data" $ do + runtime <- R.newRuntime + bytes <- encodeIndexedTree (Stem Leaf) + input <- newIORef (bytes <> "next") + Right reader <- R.acquire runtime R.rootScope $ pure $ R.Reader (\wanted -> + atomicModifyIORef' input (\rest -> let (chunk,after) = BS.splitAt (min wanted 3) rest in (after,chunk))) (pure ()) + C.verifyIndexed runtime R.rootScope reader 65536 (Stem Leaf) + >>= (@?= Right (toInteger (BS.length bytes),pin bytes)) + readIORef input >>= (@?= "next") + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "exact verification rejects unequal bytes and truncation" $ do + bytes <- encodeIndexedTree Leaf + forM_ [BS.init bytes, "X" <> BS.drop 1 bytes] $ \bad -> do + runtime <- R.newRuntime + input <- newIORef bad + Right reader <- R.acquire runtime R.rootScope $ pure $ R.Reader (\wanted -> + atomicModifyIORef' input (\rest -> let (chunk,after) = BS.splitAt wanted rest in (after,chunk))) (pure ()) + C.verifyIndexed runtime R.rootScope reader 65536 Leaf >>= category "integrity-failed" + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "whole-write lease excludes other writers; cancellation releases it" $ do + runtime <- R.newRuntime + entered <- newEmptyMVar; gate <- newEmptyMVar; done <- newEmptyMVar + Right writer <- R.acquire runtime R.rootScope $ pure $ R.Writer + (\_ -> putMVar entered () >> takeMVar gate) (pure ()) + bytes <- encodeIndexedTree Leaf + worker <- forkFinally (C.writeIndexed runtime R.rootScope writer 65536 (pin bytes) Leaf) (putMVar done) + takeMVar entered + R.writeSome runtime R.rootScope writer "x" >>= category "busy" + atomically (R.reserveChildSTM runtime R.rootScope [writer]) >>= category "busy" + killThread worker + timeout 3000000 (takeMVar done) >>= \reply -> case reply of + Just (Left err) -> case fromException err of Just ThreadKilled -> pure (); _ -> assertFailure (show err) + _ -> assertFailure "cancellation lost" + moved <- atomically (R.reserveChildSTM runtime R.rootScope [writer]) + case moved of Right _ -> pure (); Left p -> assertFailure (show p) + R.closeScope runtime R.rootScope >>= (@?= []) + ] diff --git a/test/InteractionInspectionTests.hs b/test/InteractionInspectionTests.hs @@ -46,7 +46,7 @@ interactionInspectionTests = testGroup "Interaction inspection" , testCase "bad envelope, unsupported version, unknown and malformed actions" $ do let bad = inspect defaultLimits [("main",Leaf)] occurrenceStatus (head (occurrences bad)) @?= "invalid" - let version = inspect defaultLimits [("main",Fork (ofString "tricuIO") (Fork (ofNumber 2) Leaf))] + let version = inspect defaultLimits [("main",Fork (ofString "tricuIO") (Fork (ofNumber 3) Leaf))] occurrenceStatus (head (occurrences version)) @?= "unknown" occurrenceStatus (rows (action 999 Leaf) !! 1) @?= "unknown" let cutUnknown = inspect (Limits 1 100 80) [("main",io (action 999 Leaf))] @@ -107,6 +107,31 @@ interactionInspectionTests = testGroup "Interaction inspection" assertBool "absent export" (isLeft (inspectBundle defaultLimits "missing" b)) assertBool "negative limit" (isLeft (inspectBundle (Limits (-1) 1 1) "main" b)) assertBool "zero limit" (isLeft (inspectBundle (Limits 1 0 1) "main" b)) + , testCase "v2 named headers, opaque arguments and bind continuations" $ do + let req = action 10 (Fork (ofString "file") (Fork (ofNumber 1) (Fork (ofString "open-writer") (action 26 (ofString "DO-NOT-DELETE"))))) + envelope = Fork (ofString "tricuIO") . Fork (ofNumber 2) + os = occurrences (inspect defaultLimits [("main",envelope (action 1 (Fork req req)))]) + map occurrenceSummary (take 3 os) @?= ["tricuIO v2","Bind","Facility file@1/open-writer"] + map occurrenceStatus (drop 3 os) @?= ["opaque","opaque"] + assertBool "no payload preview" (not (any (Text.isInfixOf "DO-NOT-DELETE" . occurrenceSummary) os)) + , testCase "v2 does not inherit permissive v1 tag decoding or numeric facilities" $ do + let envelope = Fork (ofString "tricuIO") . Fork (ofNumber 2) + status t = occurrenceStatus (occurrences (inspect defaultLimits [("main",envelope t)]) !! 1) + status (Fork (Fork Leaf Leaf) Leaf) @?= "invalid" + status (action 20 Leaf) @?= "unknown" + status (action 10 (Fork (ofString "file") (Fork Leaf (Fork (ofString "read") Leaf)))) @?= "invalid" + , testCase "v2 callbacks and huge shared arguments remain opaque" $ do + let huge = iterate (\t -> Fork t t) Leaf !! 120 + req = action 10 (Fork (ofString "future") (Fork (ofNumber 7) (Fork (ofString "work") huge))) + envelope = Fork (ofString "tricuIO") . Fork (ofNumber 2) + bundle <- W.buildBundleShared [("main",envelope req)] + case inspectBundle defaultLimits "main" bundle of + Left e -> assertFailure e + Right r -> do + length (occurrences r) @?= 3 + occurrenceStatus (last (occurrences r)) @?= "opaque" + let os = occurrences (inspect defaultLimits [("main",envelope (action 3 (action 20 Leaf)))]) + occurrenceSummary (last os) @?= "scope callback (not applied)" , testCase "JSON schema and safe terminal quoting" $ do let r = inspect defaultLimits [("main",io (action 10 (ofString "x\n\ESC[2J\x202e"))), ("unsafe\n\ESC",io (action 10 (ofString "x\n\ESC[2J\x202e")))] output = renderOutline r @@ -114,7 +139,7 @@ interactionInspectionTests = testGroup "Interaction inspection" assertBool "no terminal escapes" (not (Text.any (== '\ESC') output)) assertBool "no bidi" (not (Text.any (== '\x202e') output)) (eitherDecode (encode json) :: Either String Value) @?= Right json - assertBool "schema" ("tricu.interaction-inspection.v1" `Text.isInfixOf` Text.pack (BL.unpack (encode json))) + assertBool "schema" ("tricu.interaction-inspection.v2" `Text.isInfixOf` Text.pack (BL.unpack (encode json))) ] where checkPrimitive (tag,label,fields) = do @@ -148,4 +173,9 @@ interactionInspectionTests = testGroup "Interaction inspection" , (71,"CloseSocket",[sock]), (72,"BindSocket",sock:address), (73,"Listen",[sock,("backlog",n)]) , (74,"Accept",[sock]), (75,"Connect",sock:address), (76,"Recv",[sock,("maxBytes",n)]) , (77,"Send",[sock,bytes]), (78,"GetSocketName",[sock]) + , (90,"UploadBegin",[("directory",str),("expected",n)]) + , (91,"UploadAppend",[("handle",Leaf),("chunk",ofList [ofNumber 0,ofNumber 255])]) + , (92,"UploadSeal",[("handle",Leaf)]) + , (93,"UploadCommit",[("handle",Leaf),("destination",str)]) + , (94,"UploadAbort",[("handle",Leaf)]) ] diff --git a/test/LibraryValueTests.hs b/test/LibraryValueTests.hs @@ -4,7 +4,7 @@ module LibraryValueTests (byteListUtilities, httpParsingTests) where import Runtime import Control.Monad (forM_) -import IODriver (runIO, defaultPerms) +import IODriver (runIO) import qualified Data.Map as Map import Test.Tasty import Test.Tasty.HUnit @@ -134,8 +134,8 @@ byteListUtilities env base = testGroup "Byte List Utility Tests" call "equal?" = foldl apply (base Map.! "equal?") call name = foldl apply (env Map.! name) -httpParsingTests :: Env -> TestTree -httpParsingTests env = testGroup "HTTP Parsing Tests" +httpParsingTests :: Env -> Env -> TestTree +httpParsingTests env transport = testGroup "HTTP Parsing Tests" [ testCase "HTTP support uses checked native parser exports" $ do Map.member "httpLineReply" env @?= True Map.member "httpHeaderLineReply" env @?= True @@ -304,15 +304,15 @@ httpParsingTests env = testGroup "HTTP Parsing Tests" forM_ [([header "transfer-encoding" "chunked"], 400, "Bad Request\n"), ([header "content-length" "1", header "content-length" "1"], 400, "Bad Request\n"), ([header "content-length" "1048577"], 413, "Request body too large\n")] $ \(headers, code, message) -> - call "readBody" [Leaf, ofList headers, ofString "body"] @?= + foldl apply (transport Map.! "readBody") [Leaf, ofList headers, ofString "body"] @?= pairT (ofNumber 0) (parserErr (ofNumber code) (ofString message)) , testCase "buffered body selection preserves exact suffix with no socket access" $ do forM_ [([], "body", "", "body"), ([header "content-length" "0"], "body", "", "body"), ([header "content-length" "3"], "abcXYZ", "abc", "XYZ")] $ \(headers, input, body, rest) -> do - let action = call "readBody" [Leaf, ofList headers, ofString input] - envelope = Fork (ofString "tricuIO") (Fork (ofNumber 1) action) - runIO defaultPerms envelope >>= (@?= Right (parserOk (ofString body) (ofString rest))) + let action = foldl apply (transport Map.! "readBody") [Leaf, ofList headers, ofString input] + envelope = Fork (ofString "tricuIO") (Fork (ofNumber 2) action) + runIO envelope >>= (@?= Right (parserOk (ofString body) (ofString rest))) , testCase "parsed Content-Length header validates after framing" $ do -- Test the pipeline directly; the ignored remainder is not source syntax. let parsedLength raw = case call "parseHeaders" [ofString raw] of diff --git a/test/PortableCompilerTests.hs b/test/PortableCompilerTests.hs @@ -11,7 +11,7 @@ import Control.Monad (forM_) import Data.List (isInfixOf) import qualified Data.ByteString as BS import qualified Data.Text as Text -import System.Directory (canonicalizePath) +import System.Directory (canonicalizePath, doesFileExist) import System.FilePath ((</>)) import System.IO.Temp (withSystemTempDirectory) import qualified System.Timeout as Timeout @@ -34,6 +34,33 @@ portableCompilerTests = testGroup "portable compiler supervision" rejected "--compile-timeout" (buildPortableModule (defaults {compilerSeconds = seconds}) Nothing source) let invalid = defaults {compilerToolStore = Just (unStorePath store)} rejected "--tool-store requires --toolchain" (checkPortableCompiler invalid store) + , testCase "compiler protocol selection rejects unsupported versions and packaged relabeling" $ fresh $ \store source -> do + forM_ [0,3,-1,maxBound] $ \version -> + rejected "--toolchain-io-version must be" (buildPortableResult + (defaults {compilerIOVersion = version}) (Just (unStorePath store)) source) + rejected "IO v1 compilers require a retained v1 runtime" (checkPortableCompiler + (defaults {compilerIOVersion = 1}) store) + , testCase "explicit v2 compiler uses OS authority without v1 grants or fallback" $ fresh $ \store source -> do + manifest <- putManifest store (ModuleManifest [] []) + receipt <- putObject store receiptDomain "v2 independent receipt" + let remove = Fork (ofNumber 10) (Fork (ofString "path") + (Fork (ofNumber 1) (Fork (ofString "remove-file") (ofString source)))) + action = Fork (ofNumber 1) (Fork remove (Fork Leaf (pureAction (success manifest receipt)))) + options <- worker store action + rejected "retained v1 runtime" (buildPortableResult + (options {compilerIOVersion = 1}) (Just (unStorePath store)) source) + doesFileExist source >>= (@?= True) + checkPortableCompiler (options {compilerIOVersion = 2}) store + doesFileExist source >>= (@?= True) -- startup verification is inert + (_,actual,actualReceipt) <- buildPortableResult (options {compilerIOVersion = 2}) + (Just (unStorePath store)) source + (actual,actualReceipt) @?= (manifest,receipt) + doesFileExist source >>= (@?= False) + , testCase "v2 framing never retries retired v1 operations" $ fresh $ \store source -> do + options <- worker store (Fork (ofNumber 20) (ofString source)) + rejected "Compiler IO error" (buildPortableResult (options {compilerIOVersion = 2}) + (Just (unStorePath store)) source) + doesFileExist source >>= (@?= True) , testCase "explicit missing compiler never falls back to packaged data" $ fresh $ \store source -> do let options = defaults {compilerManifest = Just (Text.unpack missing)} rejected "Module manifest not found" (checkPortableCompiler options store) @@ -47,7 +74,7 @@ portableCompilerTests = testGroup "portable compiler supervision" , testCase "compiler Results and malformed actions are checked" $ fresh $ \store source -> do forM_ [(pureAction Leaf, "Invalid portable compiler Result"), (pureAction (Fork Leaf (Fork (ofString "test failure") Leaf)), "Portable compilation failed"), - (Leaf, "invalid action")] $ \(action, diagnostic) -> do + (Leaf, "IO v2 task failed")] $ \(action, diagnostic) -> do options <- worker store action rejected diagnostic (buildPortableModule options (Just (unStorePath store)) source) , testCase "returned manifest and receipt are authenticated" $ fresh $ \store source -> do @@ -55,7 +82,8 @@ portableCompilerTests = testGroup "portable compiler supervision" receipt <- putObject store receiptDomain "receipt" let check m r diagnostic = do options <- worker store (pureAction (success m r)) - rejected diagnostic (buildPortableModule options (Just (unStorePath store)) source) + rejected diagnostic (buildPortableModule + options (Just (unStorePath store)) source) check missing receipt "Module manifest not found" check manifest missing "Compiler receipt missing" check manifest "../escape" "Invalid compiler receipt hash" @@ -63,15 +91,16 @@ portableCompilerTests = testGroup "portable compiler supervision" check manifest receipt "Compiler receipt digest mismatch" BS.writeFile (objectPath store manifest) "tampered manifest" check manifest receipt "Module manifest digest mismatch" - , testCase "compiler cannot overwrite source files" $ fresh $ \store source -> do + , testCase "retained v1 compiler cannot overwrite source files" $ fresh $ \store source -> do before <- BS.readFile source options <- worker store (Fork (ofNumber 21) (Fork (ofString source) (ofString "overwritten"))) - rejected "permission denied" (buildPortableModule options (Just (unStorePath store)) source) + rejected "retained v1 runtime" (buildPortableModule (options {compilerIOVersion = 1}) (Just (unStorePath store)) source) BS.readFile source >>= (@?= before) , testCase "compiler execution remains inside the deadline" $ fresh $ \store source -> do -- Sleep 60 seconds: scheduler effects are interrupted, not just loading. - options <- worker store (Fork (ofNumber 63) (ofNumber 60000)) + options <- worker store (Fork (ofNumber 10) (Fork (ofString "clock") + (Fork (ofNumber 1) (Fork (ofString "sleep") (ofNumber 60000))))) -- Startup verifies an inert worker; it must not execute this sleep. ready <- Timeout.timeout 5000000 (checkPortableCompiler options store) ready @?= Just () @@ -88,7 +117,7 @@ fresh action = withSystemTempDirectory "portable-supervisor" $ \dir -> do action (StorePath (dir </> "store")) source defaults :: CompilerOptions -defaults = CompilerOptions Nothing Nothing 10 False +defaults = CompilerOptions Nothing Nothing 10 False 2 missing :: Text.Text missing = Text.replicate 64 "0" @@ -109,7 +138,7 @@ worker store action = do root <- putTreeTerm store (iterate (Fork Leaf) action !! 4) pin <- putManifest store (ModuleManifest [] [ModuleExport "sourceBuildGraph" (ObjectRef (unDomain treeTermDomain) root) "arboricx.abi.tree.v1"]) - pure defaults {compilerManifest = Just (Text.unpack pin)} + pure defaults {compilerManifest = Just (Text.unpack pin), compilerIOVersion = 2} rejected :: String -> IO a -> Assertion rejected needle action = do diff --git a/test/ReferenceFixtures.hs b/test/ReferenceFixtures.hs @@ -54,7 +54,7 @@ supportFixture = unsafePerformIO $ do metadata <- lines <$> readFile "./test/fixtures/support-library.v1" bytes <- BS.readFile "./test/fixtures/support-library.arboricx" compilerBytes <- BS.readFile "./test/fixtures/compiler-library.arboricx" - unless (take 1 metadata `elem` [["tricu.support-library-fixture.v1"], ["tricu.support-library-fixture.v2"], ["tricu.support-library-fixture.v3"]]) $ + unless (take 1 metadata `elem` [["tricu.support-library-fixture.v1"], ["tricu.support-library-fixture.v2"], ["tricu.support-library-fixture.v3"], ["tricu.support-library-fixture.v4"], ["tricu.support-library-fixture.v5"]]) $ error "invalid support fixture metadata" let rows = [(legacy, alias, path, digest) | row <- tail metadata, ["module", legacy, alias, path, digest] <- [words row]] @@ -63,7 +63,7 @@ supportFixture = unsafePerformIO $ do error "support fixture bundle digest mismatch" unless (["compiler-fixture", objectHash "tricu.compiler-library-fixture.v1" compilerBytes] `elem` map words metadata) $ error "support fixture dependency mismatch" - if take 1 metadata `elem` [["tricu.support-library-fixture.v2"], ["tricu.support-library-fixture.v3"]] + if take 1 metadata `elem` [["tricu.support-library-fixture.v2"], ["tricu.support-library-fixture.v3"], ["tricu.support-library-fixture.v4"], ["tricu.support-library-fixture.v5"]] then verifyMixedSupport metadata bytes compilerBytes else pure () _ <- evaluate (length (fst compilerFixture)) diff --git a/test/ScriptedEffects.hs b/test/ScriptedEffects.hs @@ -1,28 +0,0 @@ -module ScriptedEffects (runScriptedEffects) where - -import Runtime -import Test.Tasty.HUnit - --- Small independent host interpreter for authenticated HTTP/registry actions. --- Never opens a socket. Every primitive must match its complete expected action; --- no unused script entries, implicit cleanup or unbounded EOF loops are allowed. -runScriptedEffects :: T -> [(T, T)] -> IO T -runScriptedEffects initial expected = go (256 :: Int) initial [] expected - where - go 0 _ _ _ = assertFailure "scripted effect trace exceeded fuel" - go fuel (Fork tag payload) stack script = case toNumber tag of - Right 0 -> case stack of - next : rest -> go (fuel - 1) (apply next payload) rest script - [] -> do - assertBool "effect script left unused entries" (null script) - pure payload - Right 1 -> case payload of - Fork action continuation -> go (fuel - 1) action (continuation : stack) script - _ -> assertFailure "malformed scripted Bind" - _ -> case script of - (wanted, reply) : rest -> do - Fork tag payload @?= wanted - go (fuel - 1) (Fork (ofNumber 0) reply) stack rest - [] -> assertFailure ("unscripted effect: " ++ show (toNumber tag)) - go _ _ _ _ = assertFailure "malformed scripted action" - diff --git a/test/SnapshotTests.hs b/test/SnapshotTests.hs @@ -0,0 +1,306 @@ +{-# LANGUAGE ScopedTypeVariables #-} +module SnapshotTests (snapshotTests) where + +import qualified IOV2.Driver as Driver +import qualified IOV2.Codec as Codec +import qualified IOV2.Resources as R +import ContentStore.Snapshot +import ContentStore.Indexed +import Wire +import Runtime (T(..), ofList, ofString, ofNumber) +import qualified Data.Sequence as Seq +import qualified Data.Text as Text +import System.Mem.StableName (makeStableName) +import ContentStore.Object +import Control.Concurrent +import Control.Concurrent.STM (atomically) +import Control.Exception +import Control.Monad (forM_, when) +import qualified Data.ByteString as BS +import Data.IORef +import Data.List (isInfixOf) +import System.Directory (listDirectory) +import System.FilePath ((</>)) +import System.IO +import System.IO.Temp (withSystemTempDirectory) +import System.Timeout (timeout) +import Test.Tasty +import Test.Tasty.HUnit + +showDigest = Text.unpack . hashObject domain + +domain = Domain "arboricx.indexed-tree.v1" +withTemp = withSystemTempDirectory "tricu-snapshot-test" +stream bytes = do + input <- newIORef bytes + pure $ \count -> atomicModifyIORef' input $ \rest -> + let (chunk, after) = BS.splitAt count rest in (after, chunk) +expect fault action = do + reply <- try action + case reply of + Left actual -> actual @?= fault + Right _ -> assertFailure ("expected " ++ show fault) +run directory bytes reader consume = withAuthenticatedSnapshot directory + (toInteger (BS.length bytes)) (toInteger (BS.length bytes)) domain (hashObject domain bytes) reader consume + +snapshotTests = localOption (mkTimeout 30000000) $ testGroup "Authenticated codec snapshots" + [ testCase "empty frame authenticates without reading; no named temporary survives" $ withTemp $ \directory -> do + run directory BS.empty (\_ -> assertFailure "empty frame read input" >> pure BS.empty) $ \snapshot -> do + snapshotLength snapshot @?= 0 + readSnapshot snapshot 0 4096 >>= (@?= BS.empty) + listDirectory directory >>= (@?= []) + listDirectory directory >>= (@?= []) + , testCase "fragmented binary frame is exact and independently authenticated" $ withTemp $ \directory -> do + let bytes = BS.pack [0..255] + -- Independently calculated SHA-256(domain UTF-8 + NUL + bytes). + digest = "22a44bda752e0deff262ff8cc11de009209bb196484becdae0fb4995d99c6383" + input <- newIORef (bytes <> "next frame") + requests <- newIORef [] + let receive count = do + modifyIORef' requests (count:) + atomicModifyIORef' input $ \rest -> + let (chunk,after) = BS.splitAt (min 7 count) rest in (after,chunk) + withAuthenticatedSnapshot directory 256 256 domain digest receive $ \snapshot -> do + readSnapshot snapshot 0 4096 >>= (@?= bytes) + readSnapshot snapshot 200 56 >>= (@?= BS.drop 200 bytes) + readSnapshot snapshot 256 1 >>= (@?= BS.empty) + listDirectory directory >>= (@?= []) + readIORef input >>= (@?= "next frame") + readIORef requests >>= assertBool "positive bounded reads" . all (\n -> n > 0 && n <= 256) + , testCase "bad bounds and digests reject before filesystem or reader IO" $ do + let unopened = "/nonexistent-tricu-snapshot-directory" + receive _ = assertFailure "invalid request read input" >> pure BS.empty + consume _ = assertFailure "invalid request called decoder" + forM_ [(-1,0),(0,-1),(1,2),(2^(63::Int),0)] $ \(limit,size) -> + expect InvalidSnapshotBound (withAuthenticatedSnapshot unopened limit size domain + (hashObject domain BS.empty) receive consume) + forM_ ["", "ABCD", "../bad"] $ \digest -> + expect InvalidSnapshotDigest (withAuthenticatedSnapshot unopened 1 1 domain digest receive consume) + , testCase "digest mismatch never invokes decoder" $ withTemp $ \directory -> do + reader <- stream "bad" + expect SnapshotDigestMismatch $ run directory "abc" reader (\_ -> assertFailure "unauthenticated decoder") + listDirectory directory >>= (@?= []) + , testCase "domain separation is included in authentication" $ withTemp $ \directory -> do + reader <- stream "abc" + expect SnapshotDigestMismatch $ withAuthenticatedSnapshot directory 3 3 domain + (hashObject (Domain "other") "abc") reader (\_ -> assertFailure "wrong domain accepted") + , testCase "truncation and oversized backend chunks fail before decode" $ withTemp $ \directory -> do + reader <- stream "ab" + expect SnapshotTruncated $ run directory "abc" reader (\_ -> assertFailure "truncated decode") + expect SnapshotReaderExceededBound $ run directory "abc" (\_ -> pure "abcd") + (\_ -> assertFailure "oversized decode") + listDirectory directory >>= (@?= []) + , testCase "changing the original inode cannot change authenticated snapshot" $ withTemp $ \directory -> do + let path = directory </> "input" + BS.writeFile path "original" + bracket (openBinaryFile path ReadMode) hClose $ \input -> do + run directory "original" (BS.hGetSome input) $ \snapshot -> do + -- Close the public input, then overwrite THAT inode, not a pathname replacement. + hClose input + BS.writeFile path "modified" + readSnapshot snapshot 0 4096 >>= (@?= "original") + BS.readFile path >>= (@?= "modified") + , testCase "decoder errors close the snapshot, and escaped readers cannot use it" $ withTemp $ \directory -> do + escaped <- newIORef Nothing + reader <- stream "abc" + result <- try $ run directory "abc" reader $ \snapshot -> do + writeIORef escaped (Just snapshot) + throwIO (ErrorCall "decoder failure") + case result of Left (ErrorCall message) -> message @?= "decoder failure"; Right () -> assertFailure "lost fault" + Just snapshot <- readIORef escaped + closed <- try (readSnapshot snapshot 0 1) + case closed of Left (_ :: IOException) -> pure (); Right _ -> assertFailure "snapshot leaked" + listDirectory directory >>= (@?= []) + , testCase "cancellation during receipt closes unlinked file without decoding" $ withTemp $ \directory -> do + entered <- newEmptyMVar; gate <- newEmptyMVar; done <- newEmptyMVar + worker <- forkFinally (run directory "abc" + (\_ -> putMVar entered () >> takeMVar gate) + (\_ -> assertFailure "cancelled decode")) (putMVar done) + takeMVar entered + listDirectory directory >>= (@?= []) + killThread worker + timeout 3000000 (takeMVar done) >>= \reply -> case reply of + Just (Left err) -> case fromException err of + Just ThreadKilled -> pure () + _ -> assertFailure (show err) + _ -> assertFailure "cancellation did not settle" + listDirectory directory >>= (@?= []) + , testCase "cancellation in decoder closes the authenticated snapshot" $ withTemp $ \directory -> do + entered <- newEmptyMVar; gate <- newEmptyMVar; done <- newEmptyMVar + reader <- stream "abc" + worker <- forkFinally (run directory "abc" reader $ \snapshot -> + putMVar entered snapshot >> takeMVar gate) (putMVar done) + snapshot <- takeMVar entered + killThread worker + timeout 3000000 (takeMVar done) >>= \reply -> case reply of + Just (Left _) -> pure () + _ -> assertFailure "decoder cancellation did not settle" + closed <- try (readSnapshot snapshot 0 1) + case closed of Left (_ :: IOException) -> pure (); Right _ -> assertFailure "snapshot leaked" + , testCase "indexed decoding preserves exponentially shared trees and exact encoding" $ withTemp $ \directory -> do + let tree = iterate (\child -> Fork child child) Leaf !! 200 + bytes <- encodeIndexedTree tree + reader <- stream bytes + run directory bytes reader $ \snapshot -> do + decoded <- decodeIndexedSnapshot 64 65536 snapshot + case decoded of + Left message -> assertFailure message + Right value -> do + let check Leaf = pure () + check (Fork left right) = do + l <- evaluate left >>= makeStableName + r <- evaluate right >>= makeStableName + assertBool "lost physical sharing" (l == r) + check left + check _ = assertFailure "wrong tree" + check value + encodeIndexedTree value >>= (@?= bytes) + , testCase "authenticated malformed DAGs retain independent validation controls" $ withTemp $ \directory -> do + original <- buildBundleShared [("root", Stem Leaf)] + let invalid = + [ original { bundleNodes = Seq.fromList [BNLeaf, BNLeaf] } + , original { bundleNodes = Seq.fromList [BNLeaf, BNStem 1] } + , original { bundleNodes = Seq.fromList [BNLeaf, BNFork 0 99] } + , original { bundleNodes = Seq.empty } + , original { bundleNodes = Seq.singleton BNLeaf } + ] + rejected bytes = do + reader <- stream bytes + run directory bytes reader $ \snapshot -> do + decoded <- decodeIndexedSnapshot 64 65536 snapshot + case decoded of Left _ -> pure (); Right _ -> assertFailure "malformed DAG accepted" + forM_ invalid (rejected . encodeBundle) + let bytes = encodeBundle original + replace offset value = BS.take offset bytes <> value <> BS.drop (offset + BS.length value) bytes + forM_ [replace 24 (BS.replicate 8 255), replace 44 (BS.replicate 8 255), + replace 52 (BS.replicate 8 255), replace 12 (BS.replicate 4 255), + BS.take 31 bytes, BS.init bytes] rejected + , testCase "metadata bounds and small trees agree with whole-value decoder" $ withTemp $ \directory -> do + forM_ [Leaf, Stem Leaf, Fork Leaf (Stem Leaf), iterate Stem Leaf !! 1000] $ \tree -> do + bytes <- encodeIndexedTree tree + reader <- stream bytes + run directory bytes reader $ \snapshot -> do + decodeIndexedSnapshot 64 65536 snapshot >>= (@?= decodeIndexedTree bytes) + forM_ [(1,65536),(64,0),(-1,65536),(64,-1)] $ \(sections,metadata) -> do + decoded <- decodeIndexedSnapshot sections metadata snapshot + case decoded of Left _ -> pure (); Right _ -> assertFailure "metadata limit ignored" + , testCase "native framed reader cleans each snapshot and preserves following data" $ withTemp $ \directory -> do + runtime <- R.newRuntime + bytes <- encodeIndexedTree (Stem Leaf) + receive <- stream (bytes <> "next") + Right reader <- R.acquire runtime R.rootScope (pure (R.Reader receive (pure ()))) + counts <- R.liveCounts runtime + Codec.readIndexed runtime R.rootScope reader directory 65536 (toInteger (BS.length bytes)) + (showDigest bytes) >>= (@?= Right (Stem Leaf)) + R.liveCounts runtime >>= (@?= counts) + R.readSome runtime R.rootScope reader 4 >>= (@?= Right "next") + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "native digest failure is ordinary, consumes exactly its frame, and cleans spool" $ withTemp $ \directory -> do + runtime <- R.newRuntime + receive <- stream "badnext" + Right reader <- R.acquire runtime R.rootScope (pure (R.Reader receive (pure ()))) + counts <- R.liveCounts runtime + Codec.readIndexed runtime R.rootScope reader directory 3 3 (showDigest "abc") >>= \reply -> case reply of + Left p -> do R.category p @?= "integrity-failed"; R.progress p @?= "unknown" + _ -> assertFailure "bad digest accepted" + R.liveCounts runtime >>= (@?= counts) + R.readSome runtime R.rootScope reader 4 >>= (@?= Right "next") + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "native whole-frame lease survives chunk boundaries and cancellation settles lexically" $ withTemp $ \directory -> do + runtime <- R.newRuntime + entered <- newEmptyMVar; gate <- newEmptyMVar; done <- newEmptyMVar + Right reader <- R.acquire runtime R.rootScope (pure (R.Reader + (\_ -> putMVar entered () >> takeMVar gate) (pure ()))) + worker <- forkFinally (Codec.readIndexed runtime R.rootScope reader directory 3 3 (showDigest "abc")) (putMVar done) + takeMVar entered + let R.Token identity key = reader + guessed = R.Token identity (key+1) + R.readSome runtime R.rootScope guessed 1 >>= \reply -> case reply of + Left p -> R.category p @?= "wrong-kind" + _ -> assertFailure "private snapshot exposed" + R.closeResource runtime R.rootScope guessed >>= \reply -> case reply of + Left p -> R.category p @?= "internal-resource" + _ -> assertFailure "private snapshot closed" + atomically (R.reserveChildSTM runtime R.rootScope [guessed]) >>= \reply -> case reply of + Left p -> R.category p @?= "internal-resource" + _ -> assertFailure "private snapshot transferred" + R.readSome runtime R.rootScope reader 1 >>= \reply -> case reply of + Left p -> R.category p @?= "busy" + _ -> assertFailure "lost frame lease" + killThread worker + timeout 3000000 (takeMVar done) >>= \reply -> case reply of + Just (Left err) -> case fromException err of + Just ThreadKilled -> pure () + _ -> assertFailure (show err) + _ -> assertFailure "lost cancellation" + -- The private snapshot remains registered for lexical release; cancellation + -- must not run an untracked finalizer that can replace its primary fault. + R.liveCounts runtime >>= (@?= (1,5)) + R.closeScope runtime R.rootScope >>= (@?= []) + R.liveCounts runtime >>= (@?= (0,0)) + listDirectory directory >>= (@?= []) + , testCase "native cleanup errors preserve ordinary primary diagnostics" $ withTemp $ \directory -> do + runtime <- R.newRuntime + receive <- stream "bad" + Right reader <- R.acquire runtime R.rootScope (pure (R.Reader receive (pure ()))) + let release handle = hClose handle >> ioError (userError "injected close failure") + Codec.readIndexedWith release runtime R.rootScope reader directory 3 3 (showDigest "abc") >>= \reply -> case reply of + Left p -> do + R.category p @?= "integrity-failed" + assertBool "primary lost" ("CAS digest mismatch" `isInfixOf` R.detail p) + assertBool "cleanup lost" ("injected close failure" `isInfixOf` R.detail p) + _ -> assertFailure "release failure lost" + R.liveCounts runtime >>= (@?= (1,4)) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "cancelled native snapshot retains failing release for scope diagnostics" $ withTemp $ \directory -> do + runtime <- R.newRuntime + entered <- newEmptyMVar; gate <- newEmptyMVar; done <- newEmptyMVar + Right reader <- R.acquire runtime R.rootScope (pure (R.Reader + (\_ -> putMVar entered () >> takeMVar gate) (pure ()))) + let release handle = hClose handle >> ioError (userError "injected close failure") + worker <- forkFinally (Codec.readIndexedWith release runtime R.rootScope reader directory 3 3 (showDigest "abc")) (putMVar done) + takeMVar entered + killThread worker + timeout 3000000 (takeMVar done) >>= \reply -> case reply of + Just (Left err) -> case fromException err of + Just ThreadKilled -> pure () + _ -> assertFailure (show err) + _ -> assertFailure "cleanup replaced cancellation" + errors <- R.closeScope runtime R.rootScope + case errors of + [p] -> assertBool "missing cleanup diagnostic" ("injected close failure" `isInfixOf` R.detail p) + _ -> assertFailure (show errors) + R.liveCounts runtime >>= (@?= (0,0)) + , testCase "snapshot acquisition fails before consumption; invalid bounds allocate nothing" $ withTemp $ \directory -> do + runtime <- R.newRuntime + Right reader <- R.acquire runtime R.rootScope (pure (R.Reader + (\_ -> assertFailure "invalid acquisition consumed input" >> pure BS.empty) (pure ()))) + forM_ [(directory </> "missing",3,3,"not-found"), (directory,2,3,"invalid-bound")] $ \(path,limit,size,category) -> + Codec.readIndexed runtime R.rootScope reader path limit size (showDigest "abc") >>= \reply -> case reply of + Left p -> R.category p @?= category + _ -> assertFailure "invalid acquisition succeeded" + R.liveCounts runtime >>= (@?= (1,4)) + R.closeScope runtime R.rootScope >>= (@?= []) + , testCase "wire digest bound rejects excess without demanding its element or tail" $ do + let digest = foldr Fork (Fork (error "excess digest character") (error "excess digest tail")) + (replicate 64 (ofNumber 97)) + reader = ofList [ofString "io.v2.resource", ofNumber 0, ofNumber 1] + action = Fork (ofNumber 10) (Fork (ofString "indexed-tree") (Fork (ofNumber 1) + (Fork (ofString "read") (ofList [reader,ofString ".",ofNumber 1,ofNumber 1,digest])))) + Driver.runDetailed action >>= \outcome -> case outcome of + Driver.Completed value -> value @?= Fork Leaf (Fork + (ofList (map ofString ["invalid-argument","indexed-tree/read","","none"])) Leaf) + _ -> assertFailure "invalid digest performed IO" + , testCase "increasing frames retain bounded receive and snapshot reads" $ withTemp $ \directory -> do + forM_ [4095,4096,4097,1048576] $ \size -> do + let bytes = BS.replicate size 173 + reader <- stream bytes + run directory bytes (\count -> do + assertBool "unbounded receive" (count > 0 && count <= 4096) + reader count) $ \snapshot -> do + forM_ [0,4096..size-1] $ \offset -> + readSnapshot snapshot (toInteger offset) 4096 >>= (@?= BS.take 4096 (BS.drop offset bytes)) + forM_ [(-1,1),(0,-1),(0,4097),(toInteger size+1,1)] $ \(offset,count) -> + expect InvalidSnapshotRange (readSnapshot snapshot offset count) + listDirectory directory >>= (@?= []) + ] diff --git a/test/Spec.hs b/test/Spec.hs @@ -4,8 +4,23 @@ import WorkspaceTests (workspaceTests) import IOBoundaryTests (ioBoundaryTests) import PortableCompilerTests (portableCompilerTests) import ExecutableLoadingTests (executableLoadingTests) -import ScriptedEffects (runScriptedEffects) import IODriverTests (ioDriverTests) +import IOV2UTF8Tests (ioV2UTF8Tests) +import IOV2SyncTests (ioV2SyncTests) +import IndexedWriterTests (indexedWriterTests) +import SnapshotTests (snapshotTests) +import IOV2ResolverTests (ioV2ResolverTests) +import IOV2NetworkTests (ioV2NetworkTests) +import IOV2FilesystemTests (ioV2FilesystemTests) +import IOV2PositionTests (ioV2PositionTests) +import IOV2StandardTests (ioV2StandardTests) +import IOV2StorageTests (ioV2StorageTests) +import IOV2TaskTests (ioV2TaskTests) +import IOV2ClockTests (ioV2ClockTests) +import IOV2ProcessTests (ioV2ProcessTests) +import IOV2ContextTests (ioV2ContextTests) +import IOV2ProtocolTests (ioV2ProtocolTests) +import IOV2Tests (ioV2Tests) import LibraryProgramTests (maybeTests, providedLibraries, contractsTests, arithmetic) import qualified ParserValueTests import qualified LibraryValueTests @@ -18,12 +33,12 @@ import Runtime import TreeObservation (forceTreeShared) import qualified NodeTable as Nodes import Wire -import SupportFixture (canonicalEnvs, verifyReuseProviders, verifySupportSources, verifyRelocatedSupportSources, +import SupportFixture (canonicalEnvs, verifyReuseProviders, verifySupportSources, verifyRelocatedSupportSources, verifyIOV2SupportSources, verifyCompilerIOSupportSources, verifySupportExports, verifySupportDependencies, readFixtureSourceBytes, readTestSource, readPinnedSource) import ContentStore import ContentStore.Bundle import Module.Manifest -import IODriver (IOPermissions(..), checkIOSentinel, runIO, runIOWithEnv, runIOWith, unsafePerms, defaultPerms) +import IODriver (runIO) import Control.Exception (bracket, evaluate, try, SomeException) import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar) @@ -77,7 +92,6 @@ tests = testGroup "Tricu Tests" , byteMarshallingTests , sourceObjectCodecTests , sharedTreeDecodingTests - , verifiedTreeLoadingTests , indexedTreeTransportTests , verifiedTreePublicationTests , wireTests @@ -97,14 +111,28 @@ tests = testGroup "Tricu Tests" , gateUnitTests , gateResolutionTests , gateCompilationTests - , LibraryValueTests.httpParsingTests (fixtureExports "http") - , httpPipelineTests + , LibraryValueTests.httpParsingTests (fixtureExports "http") (fixtureExports "io_v2_http") , registryContractTests - , registryPipelineTests , localControlTests , contentStoreTests , artifactSafetyTests , ioDriverTests + , ioV2Tests + , ioV2ClockTests + , ioV2ProcessTests + , ioV2ContextTests + , ioV2ProtocolTests + , ioV2TaskTests + , ioV2UTF8Tests + , ioV2SyncTests + , indexedWriterTests + , snapshotTests + , ioV2ResolverTests + , ioV2NetworkTests + , ioV2FilesystemTests + , ioV2PositionTests + , ioV2StandardTests + , ioV2StorageTests ] @@ -423,248 +451,29 @@ indexedTreeTransportTests = testGroup "indexed tree transport" (encodeBundle (buildBundle [("publicName", tree)])) name @?= "publicName" pin @?= hashObject indexedTreeDomain encoded - , testCase "hash, no-replace publication, repeat and read exact inert tree" $ - withSystemTempDirectory "tricu-indexed-io" $ \dir -> do - let tree = Fork (Stem (Fork Leaf Leaf)) (Fork Leaf (Stem Leaf)) - path = dir </> "object" - payload <- encodeIndexedTree tree - let pin = hashObject indexedTreeDomain payload - action unsafePerms 51 tree >>= (@?= Right (ioOkResult (ofString (unpack pin)))) - writeIndexed unsafePerms path pin tree >>= (@?= Right (ioOkResult (Stem Leaf))) - BS.readFile path >>= (@?= payload) - writeIndexed unsafePerms path pin tree >>= (@?= Right (ioOkResult Leaf)) - readIndexed unsafePerms path pin >>= (@?= Right (ioOkResult tree)) - , testCase "read checks permission, canonical digest, and domain" $ - withSystemTempDirectory "tricu-indexed-denied" $ \dir -> do - let path = dir </> "object" - payload <- encodeIndexedTree Leaf - BS.writeFile path payload - let pin = hashObject indexedTreeDomain payload - readIndexed defaultPerms path pin >>= (@?= Right (ioErrResult "permission denied")) - readIndexed unsafePerms path "BAD" >>= (@?= Right (ioErrResult "invalid CAS digest")) - readIndexed unsafePerms path (hashObject treeTermDomain payload) - >>= (@?= Right (ioErrResult "CAS digest mismatch")) - , testCase "verification precedes malformed indexed payload decoding" $ - withSystemTempDirectory "tricu-indexed-bad" $ \dir -> do - let path = dir </> "object" - payload = "bad bundle" - BS.writeFile path payload - readIndexed unsafePerms path (pack (replicate 64 '0')) - >>= (@?= Right (ioErrResult "CAS digest mismatch")) - readIndexed unsafePerms path (hashObject indexedTreeDomain payload) - >>= (@?= Right (ioErrResult "invalid indexed tree")) - let named = encodeBundle (buildBundle [("other", Leaf)]) - BS.writeFile path named - readIndexed unsafePerms path (hashObject indexedTreeDomain named) - >>= (@?= Right (ioErrResult "invalid indexed tree")) - , testCase "publication needs both permissions and never overwrites corruption" $ - withSystemTempDirectory "tricu-indexed-publish" $ \dir -> do - let path = dir </> "object" - payload <- encodeIndexedTree Leaf - let pin = hashObject indexedTreeDomain payload - forM_ [defaultPerms { allowRead = [dir] }, defaultPerms { allowWrite = [dir] }] $ \perms -> - writeIndexed perms path pin Leaf >>= (@?= Right (ioErrResult "permission denied")) - doesFileExist path >>= (@?= False) - writeIndexed unsafePerms path (pack (replicate 64 '0')) Leaf - >>= (@?= Right (ioErrResult "CAS digest mismatch")) - doesFileExist path >>= (@?= False) - BS.writeFile path "existing corruption" - writeIndexed unsafePerms path pin Leaf - >>= (@?= Right (ioErrResult "CAS object exists with different bytes")) - BS.readFile path >>= (@?= "existing corruption") ] where trees 0 = [Leaf] trees n = let xs = trees (n - 1) in Leaf : map Stem xs ++ [Fork a b | a <- xs, b <- xs] - action perms tag payload = runIO perms $ - Fork (ofString "tricuIO") (Fork (ofNumber 1) (Fork (ofNumber tag) payload)) - readIndexed perms path pin = action perms 50 (Fork (ofString path) (ofString (unpack pin))) - writeIndexed perms path pin tree = action perms 52 - (Fork (ofString path) (Fork (ofString (unpack pin)) tree)) - --- Small host/unit codec gate, not compiler-closure compilation. Raw trees --- must be inspected/encoded without invoking them as functions. -verifiedTreeLoadingTests :: TestTree -verifiedTreeLoadingTests = testGroup "verified tree loading" - [ testCase "exact inert tree, including function-shaped forks" $ - withSystemTempDirectory "tricu-tree-load" $ \dir -> do - let value = Fork (Stem (Fork Leaf Leaf)) (Fork Leaf (Stem Leaf)) - payload = encodeTreeTerm value - path = dir </> "object" - BS.writeFile path payload - readTerm (defaultPerms { allowRead = [dir] }) path - (unpack (hashObject treeTermDomain payload)) >>= (@?= Right (ioOkResult value)) - , testCase "read denied even with write permission" $ - withSystemTempDirectory "tricu-tree-denied" $ \dir -> do - let payload = BS.singleton 0 - path = dir </> "object" - BS.writeFile path payload - readTerm (defaultPerms { allowWrite = [dir] }) path - (unpack (hashObject treeTermDomain payload)) >>= (@?= Right (ioErrResult "permission denied")) - , testCase "symlink does not bypass read permission" $ - withSystemTempDirectory "tricu-tree-link" $ \dir -> do - let allowed = dir </> "allowed" - denied = dir </> "outside" - link = allowed </> "link" - payload = BS.singleton 0 - createDirectory allowed - BS.writeFile denied payload - createFileLink denied link - readTerm (defaultPerms { allowRead = [allowed] }) link - (unpack (hashObject treeTermDomain payload)) >>= (@?= Right (ioErrResult "permission denied")) - , testCase "missing file" $ - withSystemTempDirectory "tricu-tree-missing" $ \dir -> - readTerm (defaultPerms { allowRead = [dir] }) (dir </> "missing") - (replicate 64 '0') >>= (@?= Right (ioErrResult "does not exist")) - , testCase "mismatched digest precedes decoding" $ - withSystemTempDirectory "tricu-tree-mismatch" $ \dir -> do - let path = dir </> "object" - BS.writeFile path (BS.singleton 255) - readTerm unsafePerms path (replicate 64 '0') >>= (@?= Right (ioErrResult "CAS digest mismatch")) - , testCase "wrong domain rejected" $ - withSystemTempDirectory "tricu-tree-domain" $ \dir -> do - let payload = BS.singleton 0 - path = dir </> "object" - BS.writeFile path payload - readTerm unsafePerms path (unpack (hashObject (Domain "wrong.domain") payload)) - >>= (@?= Right (ioErrResult "CAS digest mismatch")) - , testCase "digest must be canonical lowercase hexadecimal" $ - forM_ ["bad", replicate 64 'G', replicate 64 'A', replicate 63 '0'] $ \digest -> - readTerm unsafePerms "/definitely-missing" digest >>= (@?= Right (ioErrResult "invalid CAS digest")) - , testCase "malformed canonical payloads rejected after valid digest" $ - withSystemTempDirectory "tricu-tree-malformed" $ \dir -> - forM_ [BS.empty, BS.pack [1], BS.pack [2,0], BS.pack [3], BS.pack [0,0]] $ \payload -> do - let path = dir </> "object" - BS.writeFile path payload - readTerm unsafePerms path (unpack (hashObject treeTermDomain payload)) - >>= (@?= Right (ioErrResult "invalid canonical tree-term")) - , testCase "invalid argument strings" $ do - runAction unsafePerms (Stem Leaf) (ofString (replicate 64 '0')) - >>= (@?= Right (ioErrResult "invalid string")) - , testCase "readTreeTerm library constructor" $ - withSystemTempDirectory "tricu-tree-constructor" $ \dir -> do - let value = Stem Leaf - payload = encodeTreeTerm value - path = dir </> "object" - BS.writeFile path payload - let io = fixtureExports "io" - action = apply (io Map.! "io") (foldl apply (io Map.! "readTreeTerm") - [ofString path, ofString (unpack (hashObject treeTermDomain payload))]) - runIO (defaultPerms { allowRead = [dir] }) action >>= (@?= Right (ioOkResult value)) - ] - where - readTerm perms path digest = runAction perms (ofString path) (ofString digest) - runAction perms path digest = runIO perms $ - Fork (ofString "tricuIO") (Fork (ofNumber 1) (Fork (ofNumber 34) (Fork path digest))) +-- Indexed snapshot/writer/staging suites cover current native transport. Flat +-- v1 file actions and grant-based publication are deliberately retired. verifiedTreePublicationTests :: TestTree -verifiedTreePublicationTests = testGroup "verified tree publication" - [ testCase "canonical hashing agrees with independent bytes" $ do - let value = Fork (Stem Leaf) (Fork Leaf (Stem Leaf)) - golden = BS.pack [2,1,0,2,0,1,0] - encodeTreeTerm value @?= golden - hashTerm value >>= (@?= Right (ioOkResult (ofString (unpack (hashObject treeTermDomain golden))))) - , testCase "new publication has exact bytes and no temporary residue" $ - withSystemTempDirectory "tricu-tree-publish" $ \dir -> do - let path = dir </> "object" - tree = Fork (Stem Leaf) Leaf - payload = BS.pack [2,1,0,0] - publish (permissions dir) path (digest payload) tree >>= (@?= Right (ioOkResult (Stem Leaf))) - BS.readFile path >>= (@?= payload) - listDirectory dir >>= (@?= ["object"]) - , testCase "existing identical object returns false" $ - withSystemTempDirectory "tricu-tree-existing" $ \dir -> do - let path = dir </> "object" - tree = Stem Leaf - payload = BS.pack [1,0] - BS.writeFile path payload - publish (permissions dir) path (digest payload) tree >>= (@?= Right (ioOkResult Leaf)) - BS.readFile path >>= (@?= payload) - , testCase "corrupt winner is not replaced" $ - withSystemTempDirectory "tricu-tree-corrupt" $ \dir -> do - let path = dir </> "object" - old = BS.pack [255] - BS.writeFile path old - publish (permissions dir) path (digest (BS.singleton 0)) Leaf - >>= (@?= Right (ioErrResult "CAS object exists with different bytes")) - BS.readFile path >>= (@?= old) - listDirectory dir >>= (@?= ["object"]) - , testCase "mismatch fails before publication" $ - withSystemTempDirectory "tricu-tree-wrong" $ \dir -> do - publish (permissions dir) (dir </> "object") (replicate 64 '0') Leaf - >>= (@?= Right (ioErrResult "CAS digest mismatch")) - listDirectory dir >>= (@?= []) - , testCase "write denied without forcing tree" $ - withSystemTempDirectory "tricu-tree-write-denied" $ \dir -> do - publish (defaultPerms { allowRead = [dir] }) (dir </> "object") - (digest (BS.singleton 0)) (error "denied write must not force tree") - >>= (@?= Right (ioErrResult "permission denied")) - listDirectory dir >>= (@?= []) - , testCase "read denied without forcing tree" $ - withSystemTempDirectory "tricu-tree-read-denied" $ \dir -> do - publish (defaultPerms { allowWrite = [dir] }) (dir </> "object") - (digest (BS.singleton 0)) (error "denied read must not force tree") - >>= (@?= Right (ioErrResult "permission denied")) - listDirectory dir >>= (@?= []) - , testCase "symlink does not bypass write permission" $ - withSystemTempDirectory "tricu-tree-write-link" $ \dir -> do - let allowed = dir </> "allowed" - outside = dir </> "outside" - link = allowed </> "link" - payload = BS.singleton 0 - createDirectory allowed - BS.writeFile outside payload - createFileLink outside link - publish (permissions allowed) link (digest payload) Leaf - >>= (@?= Right (ioErrResult "permission denied")) - BS.readFile outside >>= (@?= payload) - , testCase "invalid digest fails without filesystem effects" $ - withSystemTempDirectory "tricu-tree-bad-digest" $ \dir -> do - forM_ ["bad", replicate 64 'G', replicate 64 'A'] $ \bad -> - publish (permissions dir) (dir </> "object") bad Leaf - >>= (@?= Right (ioErrResult "invalid CAS digest")) - listDirectory dir >>= (@?= []) - , testCase "parent creation remains explicit" $ - withSystemTempDirectory "tricu-tree-parent" $ \dir -> do - publish (permissions dir) (dir </> "missing" </> "object") (digest (BS.singleton 0)) Leaf - >>= (@?= Right (ioErrResult "does not exist")) - listDirectory dir >>= (@?= []) - , testCase "concurrent publishers have one winner" $ - withSystemTempDirectory "tricu-tree-race" $ \dir -> do - let tree = iterate (\t -> Fork t t) Leaf !! 12 - bytes = encodeTreeTerm tree - path = dir </> "object" - action = publish (permissions dir) path (digest bytes) tree - a <- newEmptyMVar - b <- newEmptyMVar - _ <- forkIO (action >>= putMVar a) - _ <- forkIO (action >>= putMVar b) - results <- sequence [takeMVar a, takeMVar b] - length (filter (== Right (ioOkResult (Stem Leaf))) results) @?= 1 - length (filter (== Right (ioOkResult Leaf)) results) @?= 1 - BS.readFile path >>= (@?= bytes) - listDirectory dir >>= (@?= ["object"]) - , testCase "sourceObjectPutTree creates canonical shards and reuses objects" $ +verifiedTreePublicationTests = testGroup "Source-owned indexed publication" + [ testCase "sourceObjectPutTree creates canonical shards and reuses objects" $ withSystemTempDirectory "tricu-tree-library" $ \dir -> do env <- evaluateCompilerFile "./lib/source/object.tri" let root = dir </> "cas" tree = Fork Leaf (Stem Leaf) payload = encodeBundle (buildBundle [("root", tree)]) expected = unpack (hashObject indexedTreeDomain payload) - action = sentinel (apply (apply (env Map.! "sourceObjectPutTree") (ofString root)) tree) + action = Fork (ofString "tricuIO") (Fork (ofNumber 2) + (apply (apply (env Map.! "sourceObjectPutTree") (ofString root)) tree)) path = root </> "objects" </> take 3 expected </> expected - runIO (permissions dir) action >>= (@?= Right (ioOkResult (ofString expected))) - runIO (permissions dir) action >>= (@?= Right (ioOkResult (ofString expected))) + runIO action >>= (@?= Right (ioOkResult (ofString expected))) + runIO action >>= (@?= Right (ioOkResult (ofString expected))) BS.readFile path >>= (@?= payload) ] - where - digest = unpack . hashObject treeTermDomain - permissions dir = defaultPerms { allowRead = [dir], allowWrite = [dir] } - sentinel action = Fork (ofString "tricuIO") (Fork (ofNumber 1) action) - hashTerm tree = runIO defaultPerms (sentinel (Fork (ofNumber 35) tree)) - publish perms path expected tree = runIO perms $ - sentinel (Fork (ofNumber 36) (Fork (ofString path) (Fork (ofString expected) tree))) - sourceObjectCodecTests :: TestTree sourceObjectCodecTests = testGroup "Portable tree object codec" [ testCase "canonical preorder bytes, suffix law and complete round trip" $ @@ -785,7 +594,16 @@ decoding = testGroup "Decoding Tests" byteMarshallingTests :: TestTree byteMarshallingTests = testGroup "Byte Marshalling Tests" - [ testCase "ofByte / toByte round-trip: 0" $ do + [ testCase "shared byte encodings equal canonical numerals for all octets" $ do + mapM_ (\n -> ofByte (fromIntegral n) @?= ofNumber n) [0..255] + + , testCase "byte buffer preserves order and multiplicity across repeated octets" $ do + let values = concat (replicate 17 [0..255]) :: [Word8] + bytes = BS.pack values + ofBytes bytes @?= ofList (map (ofNumber . toInteger) values) + toBytes (ofBytes bytes) @?= Right bytes + + , testCase "ofByte / toByte round-trip: 0" $ do let w8 = (0 :: Word8) toByte (ofByte w8) @?= Right w8 @@ -1625,7 +1443,7 @@ digestHexTests = testGroup "guarded digest hex" compilerFixtureTests :: TestTree compilerFixtureTests = testGroup "self-built reference compiler fixture" [ testCase "matches all recorded reference source inputs" $ do - length (fst compilerFixture) @?= 28 + length (fst compilerFixture) @?= 31 assertBool "fixture-only patterns still in compiler closure" (all (\(_, alias, _) -> alias /= "patterns") (fst compilerFixture)) assertBool "patterns missing from support cohort" @@ -1788,6 +1606,51 @@ compilerFixtureTests = testGroup "self-built reference compiler fixture" (case verifyRelocatedSupportSources old bad (Set.singleton "patterns") of Left _ -> True; _ -> False) verifyRelocatedSupportSources old current Set.empty @?= Left "invalid patterns cohort relocation" verifySupportSources old current (Set.singleton "patterns") @?= Left "support cohort identity changed" + , testCase "IO-v2 support additions require declarations, exact membership and rebuilds" $ do + original <- map words . lines <$> readFile "test/fixtures/support-origin.v1" + current <- map words . lines <$> readFile "test/fixtures/support-cohort-v4.v1" + let built = Set.fromList [name | ["rebuilt",name,_,_,_] <- current] + declarations = [row | row <- current, take 1 row == ["added"]] + length declarations @?= 10 + verifyIOV2SupportSources original current built @?= Right () + forM_ [filter ((/= ["added"]) . take 1) current, + current ++ [head declarations], + current ++ [["module","unexpected","unexpected","lib/unexpected.tri","pin"]], + map (\row -> if take 3 row == ["module","io_v2","io_v2"] + then ["module","io_v2","io_v2","lib/wrong.tri","pin"] else row) current] $ \bad -> + assertBool "unauthorized support growth accepted" + (case verifyIOV2SupportSources original bad built of Left _ -> True; _ -> False) + assertBool "unrebuilt IO-v2 addition accepted" + (case verifyIOV2SupportSources original current (Set.delete "io_v2" built) of Left _ -> True; _ -> False) + , testCase "compiler IO cohort has exact disjoint ownership and rebuilt support" $ do + original <- map words . lines <$> readFile "test/fixtures/support-origin.v1" + old <- map words . lines <$> readFile "test/fixtures/support-cohort-v4.v1" + let moved = [("io_v2","lib/io-v2.tri"), ("io_v2_storage","lib/io-v2/storage.tri"), + ("io_v2_text","lib/io-v2/text.tri")] + names = map fst moved + keep row = case row of + ["module",_,name,_,_] -> name `notElem` names + ["added",name,_,_] -> name `notElem` names + _ -> True + ioRow = ["module","io","io","lib/io.tri","source"] + current = filter keep old ++ [ioRow, ["added","io","io","lib/io.tri"]] ++ + [["compiler-provider",name,path] | (name,path) <- moved] + compiler = [["module",name,name,path,"source"] | + (name,path) <- moved ++ [("compiler_io","lib/source/io-v2.tri")]] + built = Set.fromList [name | ["module",_,name,_,_] <- current] + check c rows b = verifyCompilerIOSupportSources c original rows b + check compiler current built @?= Right () + forM_ [tail compiler, compiler ++ [ioRow], compiler ++ [head compiler], + map (\row -> if take 3 row == ["module","io_v2","io_v2"] + then ["module","io_v2","io_v2","lib/wrong.tri","source"] else row) compiler] $ \bad -> + assertBool "incorrect compiler ownership accepted" (case check bad current built of Left _ -> True; _ -> False) + forM_ [filter ((/= ["compiler-provider"]) . take 1) current, + current ++ [["compiler-provider","io_v2","lib/io-v2.tri"]], + current ++ [["module","io_v2","io_v2","lib/io-v2.tri","source"]], + filter (/= ioRow) current] $ \bad -> + assertBool "incorrect support ownership accepted" (case check compiler bad built of Left _ -> True; _ -> False) + assertBool "unrebuilt general IO accepted" + (case check compiler current (Set.delete "io" built) of Left _ -> True; _ -> False) , testCase "rebuilt export sets follow the new manifest, not the old interface" $ do let manifest kind names = unlines ("arboricx.module-manifest.v1" : ["export " ++ name ++ " " ++ kind ++ " hash arboricx.abi.tree.v1" | name <- names]) @@ -3114,29 +2977,8 @@ parserErr code rest = Fork falseT (Fork code rest) -- IO driver tests -- -------------------------------------------------------------------------- --- A deliberately small interpreter: it records every read request, even if --- the supplied host reply is a failure. Unknown effects fail the test rather --- than being silently ignored. No filesystem or socket effects are executed. -runRegistryEffects :: T -> T -> IO ([String], T) -runRegistryEffects readReply = go (100 :: Int) - where - go 0 _ = assertFailure "registry effect trace exceeded fuel" >> pure ([], Leaf) - go fuel (Fork tag payload) = case toNumber tag of - Right 0 -> pure ([], payload) - Right 1 -> case payload of - Fork action continuation -> do - (before, value) <- go (fuel - 1) action - (after, answer) <- go (fuel - 1) (apply continuation value) - pure (before ++ after, answer) - _ -> assertFailure "malformed Bind" >> pure ([], Leaf) - Right 20 -> case toString payload of - Right path -> pure ([path], readReply) - Left message -> assertFailure message >> pure ([], Leaf) - other -> assertFailure ("unexpected registry effect: " ++ show other) >> pure ([], Leaf) - go _ _ = assertFailure "malformed registry action" >> pure ([], Leaf) - registryContractTests :: TestTree -registryContractTests = testCase "Registry hash contract: values, bounded rejection and read effects" $ +registryContractTests = testCase "Registry hash contract: bounded rejection before service dispatch" $ withSystemTempDirectory "registry-contract" $ \dir -> do env <- evaluateCompilerFile "./lib/arboricx/server.tri" let call name = foldl apply (env Map.! name) @@ -3176,119 +3018,23 @@ registryContractTests = testCase "Registry hash contract: values, bounded reject (Fork (error "extra byte bit inspected") (error "extra byte suffix inspected")) [Stem Leaf, Leaf, Leaf, Leaf, Leaf, Stem Leaf, Stem Leaf] call "bundleHexByte?" [malformedByte] @?= Leaf - let missing = err (ofString "missing") Leaf - present = ok (ofString "bundle") Leaf - get value = call "getBundleByHash" [root, value] - serve value = call "serveBundleHash" [root, value] - route value = call "getBundleRoute" - [root, ofString "GET", ofString ("/_arboricx/bundle/hash/" ++ value)] - router value = call "arboricxRouter" - [root, ofString "GET", ofString ("/_arboricx/bundle/hash/" ++ value), Leaf, Leaf] - rejected = http "badRequestResponse" [diagnostic] - forM_ invalid $ \bad -> do - runRegistryEffects present (get (ofString bad)) >>= (@?= ([], err (ofNumber 400) diagnostic)) - forM_ [serve (ofString bad), route bad, router bad] $ \action -> - runRegistryEffects present action >>= (@?= ([], rejected)) - runRegistryEffects present (get hash) >>= (@?= ([expectedPath], present)) - runRegistryEffects missing (get hash) >>= - (@?= ([expectedPath], err (ofNumber 404) (ofString "missing"))) - let response = ofString $ "HTTP/1.1 200 OK\r\n" ++ - "Content-Type: application/vnd.arboricx.bundle\r\n" ++ - "Content-Length: 6\r\nConnection: close\r\n\r\nbundle" - forM_ [serve hash, route valid, router valid] $ \action -> do - runRegistryEffects present action >>= (@?= ([expectedPath], response)) - runRegistryEffects missing action >>= - (@?= ([expectedPath], http "errorResponse" [ofNumber 404, ofString "missing"])) - forM_ [("/_arboricx/health", http "okResponse" [ofString "OK\n"]), - ("/", http "htmlResponse" [env Map.! "htmlLandingPage"])] $ \(target, expected) -> - runRegistryEffects present - (call "arboricxRouter" [root, ofString "GET", ofString target, Leaf, Leaf]) >>= - (@?= ([], expected)) - -registryPipelineTests :: TestTree -registryPipelineTests = testGroup "Registry publication and startup" - [ testCase "complete effects: atomic upload, traversal rejection, init and socket failures" $ do - env <- evaluateCompilerFile "./lib/arboricx/server.tri" - let call name = foldl apply (env Map.! name) - root = ofString "/registry" - bytes = ofBytes (BS.pack [0,255,1]) - hash = "47ffa3ea45a70b8a41c2c0825df323c00a8b7a01c1ea06083cc41dddcc001123" - primitive tag payload = Fork (ofNumber tag) payload - ok = parserOk Leaf Leaf - failed = parserErr (ofString "denied") (ofNumber 9) - digest = primitive 28 bytes - mkdir = primitive 25 (ofString "/registry/objects/47f") - publish = primitive 33 (Fork (ofString ("/registry/objects/47f/" ++ hash ++ ".arboricx")) bytes) - put = call "putBundle" [root, bytes] - prefix message = parserErr (ofString message) Leaf - forM_ ["putBundleWithHash", "putBundleWrite", "bundleTmpPath"] $ \name -> - assertBool ("unchecked/obsolete writer still exported: " ++ name) (Map.notMember name env) - runScriptedEffects put [(digest, parserOk (ofString hash) Leaf), (mkdir, ok), (publish, ok)] - >>= (@?= parserOk (ofString hash) Leaf) - runScriptedEffects put [(digest, failed)] >>= (@?= prefix "sha256Hex: denied") - runScriptedEffects put [(digest, parserOk (ofString hash) Leaf), (mkdir, failed)] - >>= (@?= prefix "createDirectory: denied") - runScriptedEffects put [(digest, parserOk (ofString hash) Leaf), (mkdir, ok), (publish, failed)] - >>= (@?= prefix "atomicWriteBytes: denied") - forM_ [ofString "../../../secret", ofString "/etc/passwd", ofString (replicate 65 'a'), Stem Leaf] $ \bad -> - runScriptedEffects put [(digest, parserOk bad Leaf)] >>= - (@?= prefix "sha256Hex: expected 64 lowercase hexadecimal hash characters") - runScriptedEffects (call "ensureStore" [root]) [(primitive 25 root, failed)] - >>= (@?= prefix "createDirectory: denied") - let sock = ofNumber 7 - create = primitive 70 Leaf - bindSocket = primitive 72 (Fork sock (Fork (ofString "127.0.0.1") (ofNumber 9050))) - listen = primitive 73 (Fork sock (ofNumber 128)) - close = primitive 71 sock - server = call "arboricxServer" [root, ofString "127.0.0.1", ofNumber 9050] - runScriptedEffects server [(create, failed)] >>= (@?= prefix "denied") - forM_ [ok, failed] $ \closed -> do - runScriptedEffects server [(create, parserOk sock Leaf), (bindSocket, failed), (close, closed)] - >>= (@?= prefix "denied") - runScriptedEffects server [(create, parserOk sock Leaf), (bindSocket, ok), (listen, failed), (close, closed)] - >>= (@?= prefix "denied") - , testCase "filesystem: initialization, binary publication, replacement and concurrent uploads" $ - withSystemTempDirectory "registry-publication" $ \dir -> do - env <- evaluateCompilerFile "./lib/arboricx/server.tri" - let call name = foldl apply (env Map.! name) - rootPath = dir </> "store" - root = ofString rootPath - payload = BS.pack [0,255,1] - hash = "47ffa3ea45a70b8a41c2c0825df323c00a8b7a01c1ea06083cc41dddcc001123" - shard = rootPath </> "objects" </> "47f" - path = shard </> (hash ++ ".arboricx") - perms = defaultPerms { allowRead = [dir], allowWrite = [dir] } - run action = runIO perms (Fork (ofString "tricuIO") (Fork (ofNumber 1) action)) - upload = run (call "putBundle" [root, ofBytes payload]) - expected = Right (parserOk (ofString hash) Leaf) - run (call "ensureStore" [root]) >>= (@?= Right (parserOk Leaf Leaf)) - run (call "ensureStore" [root]) >>= (@?= Right (parserOk Leaf Leaf)) - upload >>= (@?= expected) - BS.readFile path >>= (@?= payload) - run (call "getBundleByHash" [root, ofString hash]) >>= (@?= Right (parserOk (ofBytes payload) Leaf)) - -- Replacement is deliberate, not write-if-absent/corrupt-winner policy. - BS.writeFile path (BS.pack [7]) - upload >>= (@?= expected) - BS.readFile path >>= (@?= payload) - outputs <- forM [1,2 :: Int] $ \_ -> do - output <- newEmptyMVar - _ <- forkIO ((try upload :: IO (Either SomeException (Either String T))) >>= putMVar output) - pure output - completed <- Timeout.timeout 10000000 (mapM takeMVar outputs) - case completed of - Nothing -> assertFailure "concurrent registry uploads timed out" - Just results -> forM_ results $ \result -> case result of - Left problem -> assertFailure (show problem) - Right value -> value @?= expected - BS.readFile path >>= (@?= payload) - listDirectory shard >>= (@?= [hash ++ ".arboricx"]) - listDirectory (rootPath </> "tmp") >>= (@?= []) - -- An existing file cannot masquerade as an initialized directory. - let blocked = dir </> "blocked" - BS.writeFile blocked BS.empty - result <- run (call "ensureStore" [ofString blocked]) - result @?= Right (parserErr (ofString "createDirectory: already exists") Leaf) - ] + call "bundleObjectPath" [root, hash] @?= ofString expectedPath + let artifacts = fixtureExports "handler_artifacts" + handlers = fixtureExports "handlers" + observe value = apply (handlers Map.! "view") (apply (artifacts Map.! "load") value) + completed value = Fork (ofNumber 100) value + invalidReply = err (ofList [ofString "invalid-digest", diagnostic]) Leaf + -- Independent service-boundary oracle: invalid input performs no request; + -- valid input emits exactly one load and preserves both ordinary Results. + forM_ invalid $ \bad -> observe (ofString bad) @?= completed invalidReply + case observe hash of + Fork tag (Fork event (Fork labels (Fork continuation Leaf))) -> do + tag @?= ofNumber 106 + event @?= ofList [ofString "artifacts", ofString "load", hash] + labels @?= Leaf + forM_ [ok (ofString "bundle") Leaf, err (ofString "missing") Leaf] $ \reply -> + apply continuation reply @?= completed reply + _ -> assertFailure "expected one artifact-load service request" -- Tiny source probes run through the authenticated portable compiler fixture. -- Force complete generated trees before applying deliberately partial host data. @@ -3344,56 +3090,6 @@ compileLocalControl bindings source = do pure function other -> assertFailure ("local control compilation failed: " ++ show other) >> pure Leaf -httpPipelineTests :: TestTree -httpPipelineTests = testCase "HTTP reference pipeline: buffers, EOF, receive errors and cleanup" $ do - let env = fixtureExports "http" - call name = foldl apply (env Map.! name) - sock = ofNumber 7 - recv size = pairT (ofNumber 76) (pairT sock (ofNumber size)) - send bytes = pairT (ofNumber 77) (pairT sock (ofString bytes)) - close = pairT (ofNumber 71) sock - ok = parserOk Leaf Leaf - failed = parserErr (ofString "broken") Leaf - bad message = parserErr (ofNumber 400) (ofString message) - call "statusPhrase" [ofNumber 413] @?= ofString "Content Too Large" - forM_ [(0, "abc", "", "abc"), (2, "abc", "ab", "c")] $ \(count, input, body, rest) -> - runScriptedEffects (call "readBodyExact" [sock, ofNumber count, ofString input]) [] >>= - (@?= parserOk (ofString body) (ofString rest)) - runScriptedEffects (call "readBodyExact" [sock, ofNumber 3, ofString "a"]) - [(recv 1, parserOk (ofString "b") Leaf), (recv 1, parserOk (ofString "c") Leaf)] >>= - (@?= parserOk (ofString "abc") Leaf) - -- Exact-length invariant, including short reads and oversized initial buffers. - forM_ [0..8 :: Int] $ \count -> - forM_ [0..count+2] $ \initialSize -> do - let bytes = "AbCdEfGhIjK" - initial = take initialSize bytes - chunks = take (count - initialSize) (drop initialSize bytes) - script = [(recv 1, parserOk (ofString [byte]) Leaf) | byte <- chunks] - runScriptedEffects (call "readBodyExact" [sock, ofNumber (fromIntegral count), ofString initial]) script >>= - (@?= parserOk (ofString (take count bytes)) (ofString (drop count initial))) - runScriptedEffects (call "readBodyExact" [sock, ofNumber 3, ofString "a"]) - [(recv 1, parserOk Leaf Leaf)] >>= (@?= bad "Unexpected end of request body\n") - runScriptedEffects (call "readBodyExact" [sock, ofNumber 3, Leaf]) - [(recv 1, failed)] >>= (@?= bad "recv failed while reading body: broken") - runScriptedEffects (call "recvUntilMax" [sock, ofString "\r\n\r\n", ofNumber 20]) - [(recv 4096, parserOk (ofString "abc") Leaf), (recv 4096, failed)] >>= - (@?= parserErr (ofNumber 400) (ofString "abc")) - let message = "Request Header Fields Too Large\n" - response431 = "HTTP/1.1 431 Request Header Fields Too Large\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: " ++ - show (length message) ++ "\r\nConnection: close\r\n\r\n" ++ message - runScriptedEffects (call "httpHandleReceived" [Leaf, sock, parserErr (ofNumber 431) (ofString "raw")]) - [(send response431, ok), (close, ok)] >>= (@?= ok) - forM_ [ok, failed, parserErr (ofString "broken") (ofString "context")] $ \sent -> - forM_ [ok, failed] $ \closed -> - runScriptedEffects (call "respondAndClose" [sock, ofString "response"]) - [(send "response", sent), (close, closed)] >>= (@?= sent) - -- Host-side negative controls for missing, wrong and leftover effects. - forM_ [(recv 1, []), (recv 1, [(close, ok)]), (pairT (ofNumber 0) Leaf, [(close, ok)])] $ \(action, script) -> do - observed <- try (runScriptedEffects action script) :: IO (Either SomeException T) - case observed of - Left _ -> pure () - Right _ -> assertFailure "HTTP effect interpreter accepted a mismatched script" - containsAll :: String -> [String] -> Assertion containsAll text needles = forM_ needles $ \needle -> diff --git a/test/SupportFixture.hs b/test/SupportFixture.hs @@ -1,7 +1,7 @@ -- Test-only mixed-origin fixture verification. No source compilation/evaluation: -- complete provider identity, exact DAG interning and receipt authentication. module SupportFixture (verifyMixedSupport, verifyReuseProviders, verifySupportSources, - verifySupportExports, verifySupportDependencies, verifyRelocatedSupportSources, canonicalEnvs, + verifySupportExports, verifySupportDependencies, verifyRelocatedSupportSources, verifyIOV2SupportSources, verifyCompilerIOSupportSources, canonicalEnvs, readFixtureSourceBytes, readTestSource, readPinnedSource) where import Wire @@ -146,6 +146,57 @@ verifyRelocatedSupportSources original current rebuilt = do verifySupportSources (original ++ [row]) current rebuilt _ -> Left "invalid patterns cohort relocation" +-- Explicit closed IO-v2 cohort growth, not an ordinary membership rewrite. +-- All additions must be rebuilt; the immutable origin and patterns move retain +-- their original checks. No arbitrary added/reused alias is admitted. +verifyIOV2SupportSources :: [[String]] -> [[String]] -> S.Set String -> Either String () +verifyIOV2SupportSources = verifyExpandedSupportSources ioV2Additions + +ioV2Additions :: [(String, String)] +ioV2Additions = + [("arboricx_handlers", "lib/arboricx/handlers.tri"), + ("arboricx_streaming_v2", "lib/arboricx/streaming-v2.tri"), + ("handler_artifacts", "lib/handlers/artifacts.tri"), + ("handler_log", "lib/handlers/log.tri"), + ("handlers", "lib/handlers.tri"), + ("io_v2", "lib/io-v2.tri"), + ("io_v2_artifacts", "lib/io-v2/artifacts.tri"), + ("io_v2_http", "lib/io-v2/http-transport.tri"), + ("io_v2_storage", "lib/io-v2/storage.tri"), + ("io_v2_text", "lib/io-v2/text.tri")] + +-- The compiler owns its v2 core/text/storage providers; general IO belongs to +-- support. This closed layout preserves every source, with no duplicate owners. +verifyCompilerIOSupportSources :: [[String]] -> [[String]] -> [[String]] -> S.Set String -> Either String () +verifyCompilerIOSupportSources compiler original current rebuilt = do + let moved = [("io_v2", "lib/io-v2.tri"), ("io_v2_storage", "lib/io-v2/storage.tri"), + ("io_v2_text", "lib/io-v2/text.tri")] + required = moved ++ [("compiler_io", "lib/source/io-v2.tri")] + owns (name,path) = case [p | ["module",_,alias,p,_] <- compiler, alias == name] of + [p] -> p == path + _ -> False + additions = filter ((`notElem` map fst moved) . fst) ioV2Additions ++ [("io", "lib/io.tri")] + unless (all owns required && null [() | ["module",_,"io",_,_] <- compiler] && + filter ((== ["compiler-provider"]) . take 1) current == + [["compiler-provider",name,path] | (name,path) <- moved]) $ + Left "invalid compiler IO cohort ownership" + verifyExpandedSupportSources additions original current rebuilt + +verifyExpandedSupportSources :: [(String, String)] -> [[String]] -> [[String]] -> S.Set String -> Either String () +verifyExpandedSupportSources additions original current rebuilt = do + let names = S.fromList (map fst additions) + declarations = [["added",name,name,path] | (name,path) <- additions] + isNew ["module",_,name,_,_] = name `S.member` names + isNew _ = False + added = filter isNew current + patternsRow = [row | row@["module",_,"patterns",_,_] <- current] + unless (filter ((== ["added"]) . take 1) current == declarations && + map (take 4) added == [["module",name,name,path] | (name,path) <- additions] && + names `S.isSubsetOf` rebuilt && not (any isNew original)) $ + Left "invalid IO-v2 support expansion" + verifyRelocatedSupportSources original (filter (not . isNew) current) rebuilt + verifySupportSources (original ++ patternsRow ++ added) current rebuilt + -- The export set belongs to the new authenticated native manifest, not the -- historical module. Do not lose the full-set check when allowing API changes. verifySupportExports :: S.Set String -> String -> Either String () @@ -213,8 +264,13 @@ verifyMixedSupport metadata currentBytes compilerBytes = do S.fromList [takeWhile (/= '.') name | name <- M.keys ns] == aliases && S.fromList [takeWhile (/= '.') name | name <- M.keys rebuilt] == built) $ error "invalid mixed support cohort partition/export set" + compilerRows <- map words . lines <$> readFile "test/fixtures/compiler-library.v1" either error pure $ - if take 1 metadata == ["tricu.support-library-fixture.v3"] + if take 1 metadata == ["tricu.support-library-fixture.v5"] + then verifyCompilerIOSupportSources compilerRows oldRows (map words metadata) built + else if take 1 metadata == ["tricu.support-library-fixture.v4"] + then verifyIOV2SupportSources oldRows (map words metadata) built + else if take 1 metadata == ["tricu.support-library-fixture.v3"] then verifyRelocatedSupportSources oldRows (map words metadata) built else verifySupportSources oldRows (map words metadata) built forM_ [ (alias,path) | ["module",_,alias,path,_] <- map words metadata] $ \(alias,path) -> diff --git a/test/arboricx_deployment.py b/test/arboricx_deployment.py @@ -1,10 +1,19 @@ #!/usr/bin/env python3 -"""Haskell-only deployment runner smoke test (fixed port 8080 must be free).""" +"""Real registry runners: socket/security controls and opt-in size/lifetime probes. + +Fixed port 8080 must be free. --workload exercises 4 KiB, 64 KiB and 1 MiB +publication/readback plus mixed concurrent requests. --handler-policy also checks +intentional redaction; --fault-runner injects ordinary logging failure. Reports +are single-run observations, not statistically controlled timing comparisons. +""" +import argparse +from concurrent.futures import ThreadPoolExecutor +import json +import threading import hashlib from pathlib import Path import socket import subprocess -import sys import tempfile import time import urllib.error @@ -12,16 +21,37 @@ import urllib.request def main(): - runner = str(Path(sys.argv[1]).resolve()) + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('runner', type=Path) + parser.add_argument('--handler-policy', action='store_true') + parser.add_argument('--io-version', type=int, choices=[1,2], default=1, + help='explicit authority expectation; v2 has OS process authority') + parser.add_argument('--workload', action='store_true') + parser.add_argument('--fault-runner', type=Path) + parser.add_argument('--report', type=Path) + args = parser.parse_args() + runner = str(args.runner.resolve()) + measurements = [] + peak_kib = [0] + stop = threading.Event() + def watch(proc): + while not stop.wait(.02): + try: + for line in Path(f'/proc/{proc.pid}/status').read_text().splitlines(): + if line.startswith(('VmRSS:', 'VmHWM:')): + peak_kib[0] = max(peak_kib[0], int(line.split()[1])) + except (OSError, ValueError): + pass opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) def request(path, data=None, status=200): req = urllib.request.Request('http://127.0.0.1:8080' + path, data=data) try: - response = opener.open(req, timeout=10) + response = opener.open(req, timeout=180) except urllib.error.HTTPError as error: response = error with response: + assert response.headers.get('Connection', '').lower() == 'close' assert response.status == status, (path, response.status) return response.read() @@ -45,6 +75,8 @@ def main(): for iteration in range(2): with (root / 'server.log').open('w+') as log: proc = subprocess.Popen([runner], cwd=root, stdout=log, stderr=log) + stop.clear() + monitor = threading.Thread(target=watch, args=(proc,), daemon=True); monitor.start() try: for _ in range(100): if proc.poll() is not None: @@ -63,7 +95,40 @@ def main(): assert reply.strip() == digest.encode(), reply assert request(route) == payload request('/_arboricx/bundle/hash/not-a-hash', status=400) - request('/_arboricx/bundle/hash/' + '0' * 64, status=404) + missing = request('/_arboricx/bundle/hash/' + '0' * 64, status=404) + if args.handler_policy: + assert missing == b'Artifact unavailable\n' + # Wrong-digest symlinks never expose arbitrary bytes. V1 also + # confines paths; v2 instead authenticates bytes with OS authority. + outside = root / 'outside'; outside.write_bytes(b'PRIVATE') + denied_hash = 'b'*64 + denied = root / 'store/objects/bbb' / (denied_hash+'.arboricx') + denied.parent.mkdir(parents=True, exist_ok=True) + if not denied.exists(): + denied.symlink_to(outside) + denied_reply = request('/_arboricx/bundle/hash/'+denied_hash, status=404) + assert b'PRIVATE' not in denied_reply + if args.handler_policy: + assert denied_reply == b'Artifact unavailable\n' + # A v1 grant rejects publication through an escaping shard. + # V2 deliberately permits it under OS process authority. + outside_dir = root / 'outside-dir'; outside_dir.mkdir(exist_ok=True) + rejected_body = b'reject publication outside grant' + rejected_hash = hashlib.sha256(rejected_body).hexdigest() + shard = root / 'store/objects' / rejected_hash[:3] + if not shard.exists(): + shard.symlink_to(outside_dir, target_is_directory=True) + if args.io_version == 1: + upload_error = request('/_arboricx/bundle', rejected_body, status=400) + assert list(outside_dir.iterdir()) == [] + if args.handler_policy: + assert upload_error == b'Upload failed\n' + else: + # Explicitly retire the v1 confinement expectation, not + # digest authentication or raw target validation controls. + assert request('/_arboricx/bundle', rejected_body, status=201) == rejected_hash.encode() + assert (outside_dir/(rejected_hash+'.arboricx')).read_bytes() == rejected_body + assert request('/_arboricx/bundle/hash/'+rejected_hash) == rejected_body request('/_arboricx/package/../bad', status=400) package = root / 'store/packages/example' package.mkdir(parents=True, exist_ok=True) @@ -71,6 +136,42 @@ def main(): (package / '1.0').write_bytes(record) assert request('/_arboricx/package/example/1.0') == record request('/_arboricx/package/example/missing', status=404) + if (args.workload or args.handler_policy) and iteration == 0: + # Same live listener, mixed concurrent requests. No + # shared log-state collector is installed by the app. + tasks = [(route, 200, payload), ('/_arboricx/health', 200, b'OK\n'), + ('/_arboricx/bundle/hash/bad', 400, b'expected 64 lowercase hexadecimal hash characters')]*4 + with ThreadPoolExecutor(max_workers=4) as pool: + replies = list(pool.map(lambda task: request(task[0], status=task[1]), tasks)) + assert replies == [task[2] for task in tasks] + # Header/body policy is still the existing HTTP layer. + def raw_request(raw, wanted): + with socket.create_connection(('127.0.0.1', 8080), timeout=30) as client: + client.sendall(raw) + client.shutdown(socket.SHUT_WR) + chunks = [] + while True: + part = client.recv(65536) + if not part: break + chunks.append(part) + response = b''.join(chunks) + assert response.startswith(f'HTTP/1.1 {wanted} '.encode()), response[:200] + headers, body = response.split(b'\r\n\r\n', 1) + length = next(int(v.split(b':',1)[1]) for v in headers.split(b'\r\n') if v.lower().startswith(b'content-length:')) + assert len(body) == length + raw_request(b'NOT HTTP\r\n\r\n', 400) + raw_request(b'POST /_arboricx/bundle HTTP/1.1\r\nHost: local\r\nContent-Length: 1048577\r\n\r\n', 413) + for size in (4096, 65536, 1048576): + large = (bytes(range(256))*(size//256)) + hash = hashlib.sha256(large).hexdigest() + begin = time.monotonic() + assert request('/_arboricx/bundle', large, 201) == hash.encode() + uploaded = time.monotonic()-begin + begin = time.monotonic() + assert request('/_arboricx/bundle/hash/'+hash) == large + measurements.append(dict(bytes=size, upload_seconds=uploaded, + download_seconds=time.monotonic()-begin, observed_peak_kib=peak_kib[0])) + finally: proc.terminate() try: @@ -78,7 +179,39 @@ def main(): except subprocess.TimeoutExpired: proc.kill() proc.wait() - assert {p.name for p in root.iterdir()} == {'store', 'server.log'} + stop.set(); monitor.join(timeout=2) + assert {p.name for p in root.iterdir()} == {'store', 'server.log', 'outside', 'outside-dir'} + if args.fault_runner: + with (root / 'server.log').open('w+') as log: + proc = subprocess.Popen([str(args.fault_runner.resolve())], cwd=root, stdout=log, stderr=log) + try: + for _ in range(100): + if proc.poll() is not None: + raise AssertionError('fault runner exited') + try: + assert request('/_arboricx/health', status=500) == b'Internal Server Error\n' + break + except (OSError, urllib.error.URLError): + time.sleep(.1) + else: + raise AssertionError('fault runner startup timeout') + # Logger failure is not rollback: publication precedes log. + body = b'published before failed logging' + digest = hashlib.sha256(body).hexdigest() + assert request('/_arboricx/bundle', body, 500) == b'Internal Server Error\n' + assert (root/'store/objects'/digest[:3]/(digest+'.arboricx')).read_bytes() == body + assert request('/_arboricx/health', status=500) == b'Internal Server Error\n' + finally: + proc.terminate() + try: proc.wait(timeout=5) + except subprocess.TimeoutExpired: + proc.kill(); proc.wait() + if args.report: + args.report.write_text(json.dumps(dict(measurements=measurements, peak_kib=peak_kib[0], runner=runner, io_version=args.io_version, + handler_policy=args.handler_policy, + fault_runner=str(args.fault_runner.resolve()) if args.fault_runner else None, + limits=['single samples, not throughput estimates', 'Linux observed RSS/high-water mark, not allocation volume', + ('bounded FIFO window of 16 connections; fixed port 8080' if args.io_version == 2 else 'existing fork-per-connection scheduling; fixed port 8080'), 'termination is not a graceful-shutdown test']), indent=2)+'\n') print('PASS: deployment startup failures, health, binary upload/download, path controls, packages, restart') diff --git a/test/arboricx_server_inspection.py b/test/arboricx_server_inspection.py @@ -67,23 +67,26 @@ def main(): return exports['main'] assert main_pin(baseline) == main_pin(named), 'full bundle main tree changed' - expected = [ - 'main', 'startupAction', 'initializeThenServe', 'initializeStore', - 'ensureRootDirectory', 'createRootDirectory', 'storeRoot', - 'afterCreateDirectory', 'afterRootDirectory', 'afterInitialization', 'finishStartup', - ] + expected = ['main', 'startupAction', 'startupBody'] assert len(report['occurrences']) == len(plain['occurrences']) == len(expected) for before, after, alias in zip(plain['occurrences'], report['occurrences'], expected): assert alias in after['aliases'], (alias, after) for key in ('path', 'role', 'summary', 'status'): assert before[key] == after[key], (key, before, after) - if after['role'] == 'resume': - assert after['status'] == 'opaque' - serve = inspect('serve-view', named, 'serveView') - inspect('initialize-view', named, 'initializeView') - assert any(o['summary'] == 'Socket' and 'createSocket' in o['aliases'] for o in serve['occurrences']) - assert any(o['status'] == 'opaque' and 'afterSocketResult' in o['aliases'] for o in serve['occurrences']) - assert not any(o['summary'] in ('BindSocket', 'Listen', 'Accept') for o in serve['occurrences']) + assert [o['summary'] for o in report['occurrences']] == [ + 'tricuIO v2', 'Scope', 'scope callback (not applied)'] + assert report['occurrences'][-1]['status'] == 'opaque' + create = inspect('create-view', named, 'createView') + assert [o['summary'] for o in create['occurrences']] == [ + 'tricuIO v2', 'Facility directory@1/create', + 'arguments (not validated; backend availability not inferred)'] + assert 'createRootDirectory' in create['occurrences'][1]['aliases'] + assert 'storeRoot' in create['occurrences'][2]['aliases'] + assert create['occurrences'][2]['status'] == 'opaque' + initialize = inspect('initialize-view', named, 'initializeView') + assert any(o['summary'] == 'Facility directory@1/create' for o in initialize['occurrences']) + assert all(o['status'] == 'opaque' for o in initialize['occurrences'] if o['role'] == 'resume') + assert not (output / 'store').exists(), 'inspection executed store initialization' # Negative/measurement cohort: normal exports, with no structural projections. mappings = [] @@ -93,29 +96,25 @@ def main(): (output / 'tricu.workspace').write_text('tricu.workspace.v1\n' + '\n'.join(mappings) + '\n') candidates = { 'storeRoot': '"./store"', - 'createRootDirectory': 'createDirectory storeRoot', - 'ensureRootDirectory': 'Registry.ensureDir storeRoot', - 'initializeStore': 'Registry.ensureStore storeRoot', - 'serveRegistry': 'Registry.arboricxServer storeRoot "0.0.0.0" 8080', - 'initializeThenServe': 'onOk_ initializeStore (_ : serveRegistry)', - 'checkedStartup': 'onResult_ initializeThenServe abortIO pure', - 'rawEnsureStore': 'Registry.ensureStore', - 'rawEnsureDir': 'Registry.ensureDir', - 'rawServer': 'Registry.arboricxServer', - 'rawAbort': 'abortIO', - 'rawPure': 'pure', - 'rawOnOk': 'onOk_', - 'rawOnResult': 'onResult_', + 'createRootDirectory': 'IO.createDirectory storeRoot', + 'initializeStore': 'H.runIOV2 (Artifacts.prepareFilesystemV2 storeRoot)', + 'rawPrepare': 'Artifacts.prepareFilesystemV2', + 'rawListen': 'IO.listenTCP', + 'rawScope': 'IO.scope', + 'rawPure': 'IO.pure', } source = output / 'ordinary.tri' - source.write_text('!import arboricx_app as App\n!import arboricx_server as Registry\n!import io *\n!export main\n' + source.write_text('!import arboricx_app as App\n!import handler_artifacts as Artifacts\n' + '!import handlers as H\n!import io_v2 as IO\n!export main\n' + ''.join('!export ' + name + '\n' for name in candidates) + 'main = App.main\n' + ''.join(name + ' = ' + value + '\n' for name, value in candidates.items())) ordinary = compile_source('ordinary', source) observed = inspect('ordinary', ordinary) hits = {alias for o in observed['occurrences'] for alias in o['aliases']} - {'main'} - assert {'storeRoot', 'createRootDirectory', 'ensureRootDirectory', 'initializeStore'} <= hits + assert not hits, ('scope callback must remain opaque', hits) + assert main_pin(ordinary) == main_pin(baseline), 'ordinary aliases changed main' + assert not (output / 'store').exists(), 'ordinary inspection ran IO' measurements['ordinary']['matchedCandidateExports'] = sorted(hits) measurements['ordinary']['unmatchedCandidateExports'] = sorted(candidates.keys() - hits) for label, view in [('baseline', plain), ('ordinary', observed), ('named', report)]: @@ -125,7 +124,7 @@ def main(): measurements['mainTreeUnchanged'] = True (output / 'measurements.json').write_text(json.dumps(measurements, indent=2) + '\n') print(json.dumps(measurements, indent=2)) - print('PASS: exact main identity, named startup spine, opaque continuations, separate socket view; no application IO executed') + print('PASS: exact main identity, named v2 scope, opaque callbacks/continuations, separate initialization views; no application IO executed') if __name__ == '__main__': diff --git a/test/body_retention.py b/test/body_retention.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +"""Phase-separated real-socket body-cost investigation (Linux, port 8080 free). + +Pass a Nix-built v2 --runner. Each size/phase starts a +fresh server; repeated requests reuse that server. Exact outputs are required. +RSS is sampled with PID-namespace validation, including idle periods. GHCRTS=-s +requests allocation/maximum-live-heap statistics on orderly interrupt; missing +statistics remain null, never inferred from RSS. No forced GC. Upload regression +guards do not imply bounded whole-value downloads. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import re +import signal +import subprocess +import threading +import time +import urllib.error +import urllib.request + +from process_rss import ChildRSS + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--runner', type=Path, required=True) + parser.add_argument('--phases', nargs='+') + parser.add_argument('--bounded-streaming', action='store_true', + help='require streaming phase RSS <=128 MiB and RTS maximum live heap <=16 MiB') + parser.add_argument('--directory', type=Path, required=True) + parser.add_argument('--sizes', type=int, nargs='+', default=[65536, 1048576]) + parser.add_argument('--repeats', type=int, default=3) + args = parser.parse_args() + if not 1 <= args.repeats <= 5 or any(not 1 <= n <= 1048576 for n in args.sizes): + parser.error('bounded sizes 1..1048576, repeats 1..5 required') + directory = args.directory.resolve(); directory.mkdir(parents=True, exist_ok=False) + opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) + root = Path(__file__).resolve().parent.parent + sources = [Path(__file__).resolve(), root/'lib/http.tri', root/'src/Runtime/Value.hs', + root/'lib/handlers.tri'] + sources += sorted((root/'src/IOV2').glob('*.hs')) + sources += sorted((root/'lib/io-v2').glob('*.tri')) + sources += [root/'lib/io-v2.tri', root/'lib/arboricx/streaming-v2.tri', + root/'lib/arboricx/handlers.tri', root/'lib/handlers/artifacts.tri', + root/'lib/handlers/log.tri'] + report = dict(schema='tricu.body-retention.v1', io_version=2, rows=[], + source_sha256={str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in sources}, + ghcrts='-s', repeats=args.repeats, + runner_sha256={str(p.resolve()): hashlib.sha256(p.resolve().read_bytes()).hexdigest() + for p in [args.runner]}, + limits=['fresh process per phase/size, repeats share process', + 'RSS includes heap capacity; it is not live tree size', + 'RTS maximum residency is GC-sampled maximum live heap, not post-request live heap', + 'publication/download include existing contracts and HTTP policy, not matched obligations', + 'single sequential experiment, not concurrent capacity or controlled throughput']) + + def request(path, data=None, status=200): + req = urllib.request.Request('http://127.0.0.1:8080'+path, data=data) + try: + response = opener.open(req, timeout=180) + except urllib.error.HTTPError as exc: + response = exc + with response: + assert response.status == status, (path, response.status) + body = response.read() + assert len(body) == int(response.headers['Content-Length']) + return body + + def save(): + (directory/'report.json').write_text(json.dumps(report, indent=2)+'\n') + + phases = [('streaming-publish', args.runner), ('streaming-download', args.runner)] + if args.phases: + unknown = set(args.phases) - {phase for phase, _ in phases} + if unknown: + parser.error('unknown/unconfigured phases: '+str(sorted(unknown))) + phases = [(phase, executable) for phase, executable in phases if phase in args.phases] + for size in args.sizes: + body = (bytes(range(256))*((size+255)//256))[:size] + digest = hashlib.sha256(body).hexdigest() + for phase, executable in phases: + work = directory/f'{size}-{phase}'; work.mkdir() + object_path = work/'store/objects'/digest[:3]/(digest+'.arboricx') + if phase.endswith('download'): + object_path.parent.mkdir(parents=True); object_path.write_bytes(body) + row = dict(phase=phase, bytes=size, runner=str(executable.resolve()), samples=[], completed=False) + report['rows'].append(row) + stop = threading.Event(); samples = []; errors = [] + with (work/'server.log').open('w+') as log: + env = dict(os.environ, GHCRTS='-s') + proc = subprocess.Popen([str(executable.resolve())], cwd=work, stdout=log, stderr=log, env=env) + meter = ChildRSS(proc) + started = time.monotonic() + def watch(): + try: + while not stop.is_set(): + rss = meter.sample() + if rss is not None: + samples.append((time.monotonic()-started, rss)) + if rss > 3*1024*1024: + raise RuntimeError('3 GiB RSS guard exceeded') + stop.wait(.02) + except Exception as exc: + errors.append(str(exc)); proc.kill() + monitor = threading.Thread(target=watch, daemon=True); monitor.start() + try: + for attempt in range(100): + if proc.poll() is not None: + raise AssertionError('server exited: '+str(work/'server.log')) + try: + assert request('/_arboricx/health') == b'OK\n' + break + except (OSError, urllib.error.URLError): + time.sleep(.1) + else: + raise AssertionError('startup timeout') + time.sleep(1) + row['ready_rss_kib'] = meter.sample() + for repeat in range(args.repeats): + begin = time.monotonic(); index = len(samples) + if phase.endswith('download'): + assert request('/_arboricx/bundle/hash/'+digest) == body + elif phase.endswith('publish'): + assert request('/_arboricx/bundle', body, 201) == digest.encode() + assert object_path.read_bytes() == body + elapsed = time.monotonic()-begin + peak = max((rss for _, rss in samples[index:]), default=meter.sample()) + idle = [] + for delay in (0, 1, 2): + time.sleep(delay); idle.append(meter.sample()) + row['samples'].append(dict(seconds=elapsed, peak_rss_kib=peak, + idle_rss_kib=idle, idle_offsets_seconds=[0, 1, 3])) + save() + assert not errors, errors + row['completed'] = True + finally: + proc.send_signal(signal.SIGINT) if proc.poll() is None else None + try: + proc.wait(timeout=10) + row['shutdown'] = 'interrupt' + except subprocess.TimeoutExpired: + proc.kill(); proc.wait(); row['shutdown'] = 'killed' + stop.set(); monitor.join(timeout=2) + row['returncode'] = proc.returncode + row['sampling_errors'] = errors + (work/'rss.json').write_text(json.dumps(samples)+'\n') + log.flush(); log.seek(0); output = log.read() + def statistic(pattern): + match = re.search(pattern, output) + return int(match[1].replace(',', '')) if match else None + row['allocated_bytes'] = statistic(r'([\d,]+) bytes allocated in the heap') + row['maximum_live_bytes'] = statistic(r'([\d,]+) bytes maximum residency') + row['peak_rss_kib'] = max((rss for _, rss in samples), default=None) + save() + assert not errors, errors + if args.bounded_streaming: + selected = [row for row in report['rows'] if row['phase'] == 'streaming-publish'] + assert selected, '--bounded-streaming needs a streaming phase' + for row in selected: + assert row['completed'] and not row['sampling_errors'], row + assert row['peak_rss_kib'] is not None and row['peak_rss_kib'] <= 128*1024, row + assert row['maximum_live_bytes'] is not None and row['maximum_live_bytes'] <= 16*1024*1024, row + report['bounded_streaming_checks'] = 'passed' + save() + print(json.dumps(report, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/test/bootstrap_acceptance.py b/test/bootstrap_acceptance.py @@ -10,6 +10,7 @@ from pathlib import Path from process_rss import run_guarded import sys from bootstrap_generations import source_map, snapshot +from generation_io import version, versions, produced_adapter, check_pair, execution_flags, check_invocation REPO = Path(__file__).resolve().parent.parent ARTIFACTS = ('rebuilt-compiler.arboricx', 'build.arboricx', 'build-stable.arboricx') @@ -35,6 +36,21 @@ def verify_generation(run, runtime, source_root=None, execution_host='haskell'): raise ValueError('status/input provenance mismatch') if inputs.get('execution_host', 'haskell') != execution_host: raise ValueError('execution host mismatch') + execution_io, _ = versions(inputs) + execution_flags(execution_io, execution_host) + if 'produced_io_version' not in inputs and (run / 'produced-io-adapter.arboricx').exists(): + raise ValueError('missing generation IO metadata') + if 'produced_io_version' in inputs: + produced_adapter(run, inputs) + invocation = json.loads((run / 'invocation.json').read_text()) + check_invocation(inputs, invocation, execution_host) + if execution_host == 'zig': + request = json.loads((run / 'execution-request.json').read_text()) + if version(request.get('execution_io_version', 1)) != execution_io: + raise ValueError('execution request IO version mismatch') + for key in ('worker_export','compiler_bundle_sha256','adapter_sha256'): + if request.get(key) != inputs[key]: + raise ValueError('execution request identity mismatch: '+key) root = source_root or Path(inputs['source_root']) text, entries, _ = source_map(run / 'source-map.v1', root, 'source_map') actual = snapshot(text, entries, root) @@ -66,9 +82,10 @@ def verify(first, second, tricu, source_root=None): inputs, _, audit = verify_generation(run, tricu, source_root) audits.append(audit) records.append(inputs) - for key in ('sources', 'map_sha256', 'backend', 'root', 'runtime_sha256', 'adapter_sha256'): + for key in ('sources', 'map_sha256', 'backend', 'root', 'runtime_sha256'): if records[0][key] != records[1][key]: raise ValueError('generation inputs differ: ' + key) + check_pair(records[0], records[1]) if sha(first / 'rebuilt-compiler.arboricx') != records[1]['compiler_bundle_sha256']: raise ValueError('second generation did not execute the first rebuilt compiler') for name in ARTIFACTS: diff --git a/test/bootstrap_archive.py b/test/bootstrap_archive.py @@ -75,6 +75,19 @@ def select_exports(bundle, destination, names): destination.write_bytes(assembled(prefix + roots + entries + suffix, nodes)) +def recovery_profile(seed, inputs, adapter, accepted): + """Bind recovery execution to produced artifacts, never to interpreter success.""" + from generation_io import versions, produced_adapter + execution, output = versions(inputs) + if (execution, output) != (1, 1) and not accepted: + raise ValueError('IO v2 recovery requires an independently accepted pair') + if output == 2 and adapter.read_bytes() != produced_adapter(seed, inputs): + raise ValueError('recovery adapter differs from produced adapter') + if accepted and sha(adapter) != inputs.get('produced_adapter_sha256', inputs['adapter_sha256']): + raise ValueError('recovery adapter differs from accepted output') + return output + + def main(): p = argparse.ArgumentParser(description=__doc__) p.add_argument('--generation', type=Path, required=True) @@ -96,10 +109,11 @@ def main(): acceptance = verify(first, seed, tricu) assert recorded.get('accepted') and recorded['inputs'] == acceptance['inputs'] assert recorded['artifacts'] == acceptance['artifacts'], 'stale acceptance evidence' - assert sha(adapter) == acceptance['inputs'][1]['adapter_sha256'], 'adapter differs from accepted pair' status = json.loads((seed / 'status.json').read_text()) assert status['complete'] and status['root'] == 'source_map', 'incomplete seed' inputs = json.loads((seed / 'inputs.json').read_text()) + from generation_io import compiler_flags + io_version = recovery_profile(seed, inputs, adapter, bool(acceptance)) rows = [r.split('\t') for r in (seed / 'source-map.v1').read_text().splitlines()[1:]] for name in ('rebuilt-compiler.arboricx', 'build.arboricx', 'build-stable.arboricx', 'io-adapter.arboricx'): assert (seed / name).is_file(), name @@ -145,16 +159,18 @@ def main(): (payload / 'seed-audit.txt').write_text(audit) print(audit, end='', flush=True) fixtures = source / 'test/fixtures' - for row in (fixtures / 'compiler-library.v1').read_text().splitlines()[1:]: - fields = row.split('\t') - if fields[0] == 'module': - data = (source / fields[3]).read_bytes() - assert hashlib.sha256(b'arboricx.source.v1\0' + data).hexdigest() == fields[4], fields[3] - elif fields[0] == 'bundle': - assert hashlib.sha256(b'tricu.compiler-library-fixture.v1\0' + - (fixtures / 'compiler-library.arboricx').read_bytes()).hexdigest() == fields[1] + from fixture_sources import verify as verify_sources, current_mismatches + verify_sources(fixtures) + fixture_differences = current_mismatches(fixtures, source) + # Seed-only legacy packing still requires current compiler fixture sources. + if not acceptance: + from fixture_sources import source_rows + compiler_paths = {r[3] for r in source_rows(fixtures) if r[0] == 'compiler-library'} + if compiler_paths.intersection(fixture_differences): + raise ValueError('seed-only fixture compiler sources are not current') tools = payload / 'tools'; tools.mkdir() shutil.copy2(adapter, tools / 'io-adapter.arboricx') + shutil.copy2(seed / 'source-map.v1', tools / 'source-map.v1') store = work / 'fixture-store' selections = { 'build.arboricx': [('source_build.sourceBuildGraph', 'sourceBuildGraph')], @@ -177,9 +193,12 @@ def main(): (payload / 'runtime-paths.txt').write_text('\n'.join(closure) + '\n') with (payload / 'runtime.export').open('wb') as f: subprocess.run(['nix-store', '--export', *closure], stdout=f, check=True, timeout=600) - metadata = dict(format='tricu.bootstrap-recovery.v1', head=run('git', 'rev-parse', 'HEAD').strip(), + metadata = dict(format='tricu.bootstrap-recovery.v2' if io_version == 2 else 'tricu.bootstrap-recovery.v1', head=run('git', 'rev-parse', 'HEAD').strip(), seed_commit=a.seed_commit, runtime=str(runtime), runtime_sha256=sha(tricu), seed_inputs=inputs, selected_adapter_sha256=sha(adapter), + compiler_io_version=io_version, map_sha256=sha(tools / 'source-map.v1'), + fixture_source_scope='authenticated-reference', + working_source_differences=fixture_differences, fixture_bundle_sha256=sha(fixtures / 'compiler-library.arboricx'), accepted_stable_pair=bool(acceptance), accepted_artifacts=acceptance['artifacts'] if acceptance else None, @@ -283,7 +302,15 @@ current tracked maintenance/docs and the archive creator. Both full generation stores are retained, not just bundles. `tools/` contains copies of generation 2's compiler and application tools, plus its standard adapter. Use build-stable.arboricx for application authoring; direct recursion and native imports are supported. -Checked tricu fixtures remain under source/test/fixtures; historical SCP is archived. +Application tools use IO version {io_version}. For explicit compiler selection use +`--toolchain` and `--tool-store`{'; also pass `--toolchain-io-version 2` (OS process authority, no v1 grants)' if io_version == 2 else '; the historical runtime default is v1'}. +The selected source map is retained in tools/source-map.v1; do not substitute the +shipped v1 map for a v2 compiler rebuild. To reproduce frozen inputs instead of +editing current sources, select seed-source as --source-root. +Fixtures under source/test/fixtures are authenticated against their retained source +snapshots, not asserted to match the captured working tree. RECOVERY.json lists +working-source differences; they are not newly published/accepted fixtures. +Historical SCP is archived. SCP is an archived separate language experiment; its execution is not a restore gate. ## Next compiler change @@ -293,7 +320,7 @@ No old header bridge or historical scratch path is needed: ```sh python3 source/test/bootstrap_generations.py seed next-generation \\ --tricu "$TRICU" --source-root "$PWD/source" \\ - --map "$PWD/source/bootstrap/compiler-source-map.v1" \\ + --map "$PWD/tools/source-map.v1" \\ --backend stable --fresh --memory-mib 65536 --wall-seconds 7200 ``` @@ -352,7 +379,7 @@ Keep the OG archive too. reply = run(tricu, 'arboricx', 'import', '--file', recovered / ('tools/' + backend + '.arboricx'), '--store', toolstore, '--indexed') pin = re.search(r'Created module alias .* -> ([0-9a-f]{64})', reply)[1] - flags = ['--toolchain', pin, '--tool-store', toolstore, '--store', work / (backend + '-apps'), + flags = ['--toolchain', pin, *compiler_flags(io_version), '--tool-store', toolstore, '--store', work / (backend + '-apps'), '--compile-timeout', '120', '+RTS', '-N1', '-M4g', '-RTS'] answer = run(tricu, 'eval', probe / 'app.tri', '--format', 'number', *flags).strip() assert answer == '23', (backend, answer) @@ -366,6 +393,7 @@ Keep the OG archive too. # SCP is an archived, separate language experiment. Historical files may # remain in source/history, but no SCP execution gates tricu recovery. (output / 'RESTORE-VALIDATION.json').write_text(json.dumps(dict( + compiler_io_version=io_version, authority='os-process' if io_version == 2 else 'v1-grants', archive_sha256=sha(archive), files_checked=len(result.splitlines()), internal_checksums='passed', seed_audit=relocated_audit.strip(), git_bundle='verified', nix_closure='reimported into existing same-host store', diff --git a/test/bootstrap_cross_host.py b/test/bootstrap_cross_host.py @@ -16,7 +16,8 @@ import time from bootstrap_acceptance import ARTIFACTS, sha, verify, verify_generation from bootstrap_generations import source_map, snapshot -from bootstrap_inputs import load_kit +from bootstrap_inputs import load_kit, kit_io_version +from generation_io import versions, produced_adapter, cli_version, version from bootstrap_retention_lock import protect_generations from process_rss import run_guarded @@ -38,6 +39,8 @@ def verify_cross(first, second, control, zig, runtime, library, source): control_pair = verify(second, control, runtime, source) zi, zs, audit = verify_generation(zig, library, source, 'zig') hi = control_pair['inputs'][1] + if versions(zi) != versions(hi) or produced_adapter(zig,zi) != produced_adapter(control,hi): + raise ValueError('cross-host IO framing or produced adapter mismatch') for key in ('sources','map_sha256','backend','root','adapter_sha256', 'compiler_bundle_sha256','compiler_tree','compiler_tree_kind', 'worker_export','backend_options'): @@ -48,11 +51,14 @@ def verify_cross(first, second, control, zig, runtime, library, source): if zi.get('executor_sha256') != sha(source/'test/zig_generation_executor.py'): raise ValueError('execution launcher identity mismatch') request = json.loads((zig/'execution-request.json').read_text()) - if request != dict(worker_export=zi['worker_export'], + expected_request = dict(worker_export=zi['worker_export'], compiler_bundle_sha256=zi['compiler_bundle_sha256'], adapter_sha256=zi['adapter_sha256'], arguments=['/source','/work/generation/overlay','/work/generation/cas', - zi['compiler_bundle_sha256'],(zig/'source-map.v1').read_text(),'source_map']): + zi['compiler_bundle_sha256'],(zig/'source-map.v1').read_text(),'source_map']) + if 'execution_io_version' in request: + expected_request['execution_io_version'] = versions(zi)[0] + if request != expected_request: raise ValueError('isolated Zig execution request mismatch') for name in ARTIFACTS: if sha(second/name) != sha(zig/name): @@ -73,7 +79,48 @@ def verify_cross(first, second, control, zig, runtime, library, source): artifacts={name:sha(zig/name) for name in ARTIFACTS}) -def sandbox_command(source, kit, stage, runtime, library, host, root, memory, seconds): +def map_name(io_version): + return 'bootstrap/compiler-source-map' + ('.io-v2' if version(io_version)==2 else '') + '.v1' + + +def freeze_kit(selected, destination, io_version): + raw,payloads,_=load_kit(selected,'stable') + if kit_io_version(json.loads(raw)) != version(io_version): + raise ValueError('selected kit IO version mismatch') + if destination.exists(): + retained,contents,_=load_kit(destination,'stable') + if retained!=raw or contents!=payloads: + raise ValueError('frozen seed kit changed') + else: + destination.mkdir() + (destination/'bootstrap-kit.json').write_bytes(raw) + for role,name in [('compiler','rebuilt-compiler.arboricx'),('adapter','io-adapter.arboricx')]: + (destination/name).write_bytes(payloads[role]) + return sha(destination/'bootstrap-kit.json') + + +def kit_from_generation(run,kit): + record=json.loads((run/'inputs.json').read_text()) + output=versions(record)[1] + adapter=produced_adapter(run,record) + compiler=(run/'rebuilt-compiler.arboricx').read_bytes() + if kit.exists(): + raw,payloads,_=load_kit(kit,'stable') + if kit_io_version(json.loads(raw))!=output or payloads!={'compiler':compiler,'adapter':adapter}: + raise ValueError('retained kit differs from parent generation') + return kit + kit.mkdir() + artifacts={} + for role,name,export,payload in [('compiler','rebuilt-compiler.arboricx','sourceMapBuildStable',compiler), + ('adapter','io-adapter.arboricx','main',adapter)]: + (kit/name).write_bytes(payload) + artifacts[role]=dict(file=name,export=export,sha256=sha(kit/name)) + save(kit/'bootstrap-kit.json',dict(schema='tricu.bootstrap-kit.v2',backend='stable',io_version=output, + parent_generation=str(run),artifacts=artifacts)) + return kit + + +def sandbox_command(source, kit, stage, runtime, library, host, root, memory, seconds, io_version=1): command = ['unshare','--user','--map-root-user','--mount','--pid','--fork', '--kill-child','--mount-proc','--','bwrap','--die-with-parent','--unshare-all', '--ro-bind','/nix/store','/nix/store','--proc','/proc','--dev','/dev', @@ -82,7 +129,7 @@ def sandbox_command(source, kit, stage, runtime, library, host, root, memory, se '--chdir','/source','--setenv','HOME','/tmp','--setenv','TMPDIR','/tmp', '--setenv','PYTHONDONTWRITEBYTECODE','1', sys.executable,'/source/test/bootstrap_generations.py','/work/generation', - '--bootstrap-kit','/kit','--map','/source/bootstrap/compiler-source-map.v1', + '--bootstrap-kit','/kit','--map','/source/'+map_name(io_version), '--source-root','/source','--root',root,'--backend','stable','--fresh', '--tricu',str(runtime),'--execution-host',host,'--capabilities','8', '--memory-mib',str(memory),'--wall-seconds',str(seconds), @@ -101,6 +148,8 @@ def main(): p.add_argument('--probe',action='store_true',help='native_math on both hosts, 1 GiB/120s per job') p.add_argument('--verify-only',action='store_true') p.add_argument('--continue-experiment',action='store_true',help='re-audit completed jobs and retry pre-execution launch failures') + p.add_argument('--io-version',type=cli_version,default=1,help='explicit cohort framing; default historical v1') + p.add_argument('--bootstrap-kit',type=Path,help='explicit trusted seed; required for a new v2 experiment') args=p.parse_args() directory=args.directory.resolve() runtime,library=args.tricu.resolve(),args.zig_library.resolve() @@ -113,9 +162,15 @@ def main(): return commit=subprocess.check_output(['git','rev-parse',args.source_commit+'^{commit}'],cwd=ROOT,text=True).strip() archive=directory/'source.tar' + if args.io_version==2 and not args.bootstrap_kit and not args.continue_experiment: + p.error('new v2 experiments require --bootstrap-kit') + if args.bootstrap_kit: + raw,_,_=load_kit(args.bootstrap_kit,'stable') + if kit_io_version(json.loads(raw)) != args.io_version: + raise ValueError('selected kit IO version mismatch') if args.continue_experiment: original=json.loads((directory/'experiment.json').read_text()) - if (args.probe or original['source_commit']!=commit or original['source_archive_sha256']!=sha(archive) + if (args.probe or original.get('io_version',1)!=args.io_version or original['source_commit']!=commit or original['source_archive_sha256']!=sha(archive) or original['runtime']!=dict(path=str(runtime),sha256=sha(runtime)) or original['zig_library']!=dict(path=str(library),sha256=sha(library))): raise ValueError('continuation requires identical frozen sources and runtimes') @@ -135,7 +190,11 @@ def main(): # archives too. Keep all other extraction safety checks. tar.extractall(source,members=(m for m in tar if m.name!='ext/zig/result'),filter='data') # The sandbox also mounts this snapshot read-only; never use live sources. - map_text,entries,required=source_map(source/'bootstrap/compiler-source-map.v1',source,'source_map') + selected_seed=args.bootstrap_kit or (directory/'seed-kit' if args.continue_experiment and 'seed_manifest_sha256' in original else source/'bootstrap/rebuild') + seed_pin=freeze_kit(selected_seed,directory/'seed-kit',args.io_version) + if args.continue_experiment and seed_pin!=original.get('seed_manifest_sha256',seed_pin): + raise ValueError('continuation seed identity mismatch') + map_text,entries,required=source_map(source/map_name(args.io_version),source,'source_map') attempt='continuation-'+str(time.time_ns()) if args.continue_experiment else 'harness' harness=directory/attempt/'test' harness.mkdir(parents=True) @@ -143,6 +202,7 @@ def main(): tooling={path.name:sha(path) for path in harness.glob('*.py')} memory,seconds=(1024,120) if args.probe else (32768,3600) definition=dict(schema='tricu.cross-host-experiment.v1',source_commit=commit, + io_version=args.io_version,source_map=map_name(args.io_version),seed_manifest_sha256=seed_pin, source_archive_sha256=sha(archive), excluded_source_paths=['ext/zig/result'], runtime=dict(path=str(runtime),sha256=sha(runtime)), zig_library=dict(path=str(library),sha256=sha(library)), @@ -159,7 +219,9 @@ def main(): def job(label,kit,host): # Verify the kit before crossing the sandbox boundary; the shared runner # independently captures/authenticates it again inside. - load_kit(kit,'stable') + raw, _, _ = load_kit(kit,'stable') + if kit_io_version(json.loads(raw)) != args.io_version: + raise ValueError('cross-host kit IO version mismatch') stage=directory/label if args.continue_experiment and stage.exists(): if (stage/'generation/status.json').is_file(): @@ -175,7 +237,7 @@ def main(): stage.rename(directory/(label+'-launch-failed-'+str(time.time_ns()))) stage.mkdir() command=sandbox_command(source,kit,stage,runtime,library,host, - 'native_math' if args.probe else 'source_map',memory,seconds) + 'native_math' if args.probe else 'source_map',memory,seconds,args.io_version) save(stage/'sandbox-command.json',command) try: reply,_=run_guarded(command,memory_mib=memory,seconds=seconds+30) @@ -190,25 +252,10 @@ def main(): print('Completed isolated '+label,flush=True) return stage/'generation' def kit_from(run,label): - kit=directory/(label+'-kit') - if kit.exists(): - load_kit(kit,'stable') - for name in ('rebuilt-compiler.arboricx','io-adapter.arboricx'): - if (kit/name).read_bytes()!=(run/name).read_bytes(): - raise ValueError('retained kit differs from parent generation') - return kit - kit.mkdir() - artifacts={} - for role,name,export in [('compiler','rebuilt-compiler.arboricx','sourceMapBuildStable'), - ('adapter','io-adapter.arboricx','main')]: - shutil.copyfile(run/name,kit/name) - artifacts[role]=dict(file=name,export=export,sha256=sha(kit/name)) - save(kit/'bootstrap-kit.json',dict(schema='tricu.bootstrap-kit.v1',backend='stable', - parent_generation=str(run),artifacts=artifacts)) - return kit - first=job('H1',source/'bootstrap/rebuild','haskell') + return kit_from_generation(run,directory/(label+'-kit')) + first=job('H1',directory/'seed-kit','haskell') if args.probe: - zig=job('Z3',source/'bootstrap/rebuild','zig') + zig=job('Z3',directory/'seed-kit','zig') if inventory(first)!=inventory(zig): raise ValueError('probe complete object inventories differ') else: second=job('H2',kit_from(first,'H1'),'haskell') diff --git a/test/bootstrap_cross_host_controls.py b/test/bootstrap_cross_host_controls.py @@ -37,13 +37,20 @@ def main(): for key,value,message in [('execution_host','haskell','execution host mismatch'), ('runtime_sha256','0'*64,'runtime identity mismatch'), ('fresh_store',False,'fresh-store execution evidence'), - ('compiler_bundle_sha256','0'*64,'compiler bundle identity mismatch')]: + ('compiler_bundle_sha256','0'*64,'execution request identity mismatch')]: saved={name:(copy/name).read_bytes() for name in ('inputs.json','status.json')} for name,payload in saved.items(): record=json.loads(payload); record[key]=value (copy/name).write_text(json.dumps(record)) reject(key,message) for name,payload in saved.items(): (copy/name).write_bytes(payload) + request_path=copy/'execution-request.json' + request_bytes=request_path.read_bytes() + request=json.loads(request_bytes) + request['execution_io_version']=1 if request.get('execution_io_version',1)==2 else 2 + request_path.write_text(json.dumps(request)) + reject('request-io-version','execution request IO version mismatch') + request_path.write_bytes(request_bytes) status=json.loads((copy/'status.json').read_text()) digest=status['artifacts']['build-stable.arboricx']['tree'] obj=copy/'cas/objects'/digest[:3]/digest diff --git a/test/bootstrap_generations.py b/test/bootstrap_generations.py @@ -12,7 +12,8 @@ import subprocess import sys import time from generation_backend import BACKENDS, DEFAULT_BACKEND -from bootstrap_inputs import load_kit +from bootstrap_inputs import load_kit, kit_io_version +from generation_io import versions, produced_adapter, execution_flags, cli_version from process_rss import ChildRSS, run_guarded REPO = Path(__file__).resolve().parent.parent @@ -90,7 +91,10 @@ def main(): parser.add_argument('--backend', choices=BACKENDS, default=DEFAULT_BACKEND, help='generation lowering backend (default: %(default)s); never inferred from previous') parser.add_argument('--worker-bundle', type=Path, help='explicit pinned transition worker exporting the selected backend entrypoint; no fallback') - parser.add_argument('--adapter', type=Path, help='precompiled sourceBootstrapNativeAdapter bundle; otherwise reuse previous/io-adapter.arboricx') + parser.add_argument('--execution-io-version', type=cli_version, choices=(1, 2), help='explicit transition-worker framing; otherwise selected kit/previous produced metadata') + parser.add_argument('--produced-io-version', type=cli_version, choices=(1, 2), help='declared output cohort framing; defaults to execution version') + parser.add_argument('--produced-adapter', type=Path, help='explicit output-cohort adapter; required when changing IO version') + parser.add_argument('--adapter', type=Path, help='precompiled standard six-argument IO adapter bundle; otherwise select previous produced adapter') parser.add_argument('--bootstrap-kit', type=Path, help='explicit verified compiler/adapter kit; no previous store access') parser.add_argument('--map', type=Path, default=REPO / 'bootstrap/compiler-source-map.v1') parser.add_argument('--source-root', type=Path, default=REPO) @@ -132,6 +136,8 @@ def main(): map_text, entries, required = source_map(args.map.resolve(), source_root, args.root) inputs = snapshot(map_text, entries, source_root) worker_export, backend_options = BACKENDS[args.backend] + prior_inputs = json.loads((previous / 'inputs.json').read_text()) if previous and (previous / 'inputs.json').exists() else {} + versions(prior_inputs) if args.expect_identical and not args.validate_only: prior_inputs = json.loads((previous / 'inputs.json').read_text()) if (any(prior_inputs.get(key) != value for key, value in inputs.items()) @@ -152,11 +158,10 @@ def main(): if args.bootstrap_kit: kit_manifest, kit_payloads, kit_digest = load_kit(args.bootstrap_kit, args.backend) adapter_bytes = kit_payloads['adapter'] + selected_io = kit_io_version(json.loads(kit_manifest)) else: - adapter_input = (args.adapter or previous / 'io-adapter.arboricx').resolve() - if not adapter_input.is_file(): - parser.error('precompiled adapter required; use --adapter (no automatic bootstrap compilation)') - adapter_bytes = adapter_input.read_bytes() + selected_io = versions(prior_inputs)[1] + adapter_bytes = args.adapter.resolve().read_bytes() if args.adapter else produced_adapter(previous, prior_inputs) old_cas = previous / 'cas' _, _, manifest, _ = (old_cas / 'cache/source-build/source_map').read_text().split('\t') manifest_bytes = (old_cas / 'objects' / manifest[:3] / manifest).read_bytes() @@ -167,6 +172,25 @@ def main(): if target is None and not args.worker_bundle: parser.error('previous compiler lacks selected entrypoint ' + worker_export + '; supply an explicit --worker-bundle (no backend fallback)') + execution_io = args.execution_io_version if args.execution_io_version is not None else selected_io + if execution_io != selected_io and (args.bootstrap_kit or not args.worker_bundle): + parser.error('execution IO version disagrees with selected compiler metadata; select an explicit transition worker and adapter') + if execution_io != selected_io and not args.adapter: + parser.error('transition execution version requires an explicit matching adapter') + produced_io = args.produced_io_version if args.produced_io_version is not None else execution_io + grants = execution_flags(execution_io, args.execution_host) + if args.execution_host == 'zig': + from zig_generation_executor import validate_library + validate_library(args.zig_library.resolve(),execution_io) + if produced_io != execution_io and not args.produced_adapter: + parser.error('changed produced IO version requires --produced-adapter') + produced_adapter_bytes = args.produced_adapter.resolve().read_bytes() if args.produced_adapter else adapter_bytes + produced_pin = hashlib.sha256(produced_adapter_bytes).hexdigest() + if args.expect_identical: + if produced_io != versions(prior_inputs)[1] or execution_io != versions(prior_inputs)[1]: + parser.error('reproduction requires the previous produced IO version') + if produced_adapter_bytes != produced_adapter(previous, prior_inputs): + parser.error('reproduction requires the previous produced adapter') tricu = str(Path(args.tricu).resolve()) output.mkdir(parents=True, exist_ok=True) lock = (output / '.generation.lock').open('a') @@ -200,6 +224,7 @@ def main(): compiler = hashlib.sha256(bundle.read_bytes()).hexdigest() adapter = output / 'io-adapter.arboricx' adapter.write_bytes(adapter_bytes) + (output / 'produced-io-adapter.arboricx').write_bytes(produced_adapter_bytes) def imported(path, alias): reply = transport(['arboricx', 'import', '--store', str(output / 'compiler-cas'), '--file', str(path), '--module', alias, '--indexed']) @@ -214,7 +239,15 @@ def main(): # Transport object identity is distinct from the bundle identity used in receipts. argument_kind, argument_hash = argument_export[2:4] adapter_manifest = imported(adapter, 'io-adapter') - provenance = dict(source_root=str(source_root), root=args.root, backend=args.backend, + def require_adapter_entry(manifest): + data = (output / 'compiler-cas/objects' / manifest[:3] / manifest).read_text() + if not any(row.split('\t')[1] == 'main' for row in data.splitlines()[1:]): + parser.error('selected adapter lacks main export') + require_adapter_entry(adapter_manifest) + if produced_adapter_bytes != adapter_bytes: + require_adapter_entry(imported(output / 'produced-io-adapter.arboricx', 'produced-io-adapter')) + provenance = dict(execution_io_version=execution_io, produced_io_version=produced_io, + produced_adapter_sha256=produced_pin, source_root=str(source_root), root=args.root, backend=args.backend, fresh_store=fresh_store, worker_export=worker_export, backend_options=backend_options, compiler_tree=argument_hash, compiler_tree_kind=argument_kind, @@ -238,10 +271,10 @@ def main(): '--arg-export', compiler_manifest + ':' + worker_export, '--arg', str(source_root), '--arg', str(output / 'overlay'), '--arg', str(output / 'cas'), '--arg', compiler, '--arg', map_text, '--arg', args.root, - '--io', '--unsafe-io', '--result', '--format', 'decode', + '--io', *grants, '--result', '--format', 'decode', '+RTS', '-N' + str(args.capabilities), '-M' + str(args.memory_mib) + 'm', '-T', '-s', '-RTS'] if args.execution_host == 'zig': - request = dict(worker_export=worker_export, compiler_bundle_sha256=compiler, + request = dict(execution_io_version=execution_io, worker_export=worker_export, compiler_bundle_sha256=compiler, adapter_sha256=provenance['adapter_sha256'], arguments=[str(source_root), str(output / 'overlay'), str(output / 'cas'), compiler, map_text, args.root]) diff --git a/test/bootstrap_inputs.py b/test/bootstrap_inputs.py @@ -12,6 +12,7 @@ import re from generation_backend import BACKENDS SCHEMA = 'tricu.bootstrap-kit.v1' +SCHEMA_V2 = 'tricu.bootstrap-kit.v2' ROLES = {'compiler': 'rebuilt-compiler.arboricx', 'adapter': 'io-adapter.arboricx'} @@ -24,8 +25,9 @@ def load_kit(directory, backend): directory = Path(directory) raw = (directory / 'bootstrap-kit.json').read_bytes() record = json.loads(raw) - if not isinstance(record, dict) or record.get('schema') != SCHEMA: + if not isinstance(record, dict) or record.get('schema') not in (SCHEMA, SCHEMA_V2): raise ValueError('unsupported bootstrap kit schema') + kit_io_version(record) if backend not in BACKENDS or record.get('backend') != backend: raise ValueError('bootstrap kit backend mismatch') expected_exports = {'compiler': BACKENDS[backend][0], 'adapter': 'main'} @@ -47,3 +49,14 @@ def load_kit(directory, backend): raise ValueError('bootstrap kit digest mismatch: ' + role) payloads[role] = payload return raw, payloads, hashlib.sha256(raw).hexdigest() + + +def kit_io_version(record): + from generation_io import version + if record.get('schema') == SCHEMA: + if 'io_version' in record and version(record['io_version']) != 1: + raise ValueError('v1 bootstrap kits describe IO v1 only') + return 1 + if record.get('schema') != SCHEMA_V2 or 'io_version' not in record: + raise ValueError('explicit bootstrap kit IO version required') + return version(record['io_version']) diff --git a/test/bootstrap_retention.py b/test/bootstrap_retention.py @@ -141,6 +141,9 @@ def load_config(root, path): if sha(root / t['generation'] / name) != digest: raise ValueError('accepted artifact mismatch') covered(config['support']['preparation']) + if 'source' in config['support']: + covered(config['support']['source']) + check_support_source(root, config['support']) if config['support']['toolchain'] not in roles: raise ValueError('unknown support toolchain') if not config.get('sibling_users_checked') or not config.get('uncoordinated_jobs_stopped'): @@ -148,6 +151,20 @@ def load_config(root, path): return config +def check_support_source(root, support): + from fixture_sources import current_mismatches, verify + source = within(root, support['source']) if 'source' in support else REPO + if not source.is_dir(): + raise ValueError('support source must be a directory') + for _, _, _, path, _ in verify(REPO / 'test/fixtures'): + within(source, path) + within(source, 'bootstrap/compiler-source-map.v1') + changed = current_mismatches(REPO / 'test/fixtures', source) + if changed: + raise ValueError('selected support source differs from current fixture provenance: ' + ', '.join(changed)) + return source + + def object_directories(root, protected): found = [] for parent, dirs, _ in os.walk(root, followlinks=False): @@ -258,7 +275,8 @@ def isolated_checks(root, plan, out, phase): destination = out / (phase + '-' + str(time.time_ns())) destination.mkdir() command = ['bwrap', '--die-with-parent', '--unshare-pid', '--ro-bind', '/', '/', - '--proc', '/proc', '--tmpfs', '/tmp', '--tmpfs', str(root)] + '--proc', '/proc', '--tmpfs', '/tmp', '--setenv', 'TMPDIR', '/tmp', + '--setenv', 'TMP', '/tmp', '--setenv', 'TEMP', '/tmp', '--tmpfs', str(root)] for relative in plan['config']['protected']: command += ['--ro-bind', str(root / relative), str(root / relative)] command += ['--bind', str(out), str(out), '--setenv', 'PYTHONDONTWRITEBYTECODE', '1', @@ -297,8 +315,17 @@ def validate_check_reports(root, plan, out, phases): if phase + '_sha256' in plan and sha(report_path) != plan[phase + '_sha256']: raise ValueError('checkpoint check report changed') if not all(report.get(k) for k in ['complete', 'obsolete_stores_hidden', - 'support_receipts_reproduced', 'fixture_sources_current']): + 'support_receipts_reproduced']): raise ValueError('checkpoint verification incomplete') + support = plan['config']['support'] + if 'source' in support: + check_support_source(root, support) + if (not report.get('fixture_sources_match_selected') or + report.get('fixture_source_scope') != 'frozen-reference' or + report.get('fixture_source') != support['source']): + raise ValueError('checkpoint support source selection mismatch') + elif not report.get('fixture_sources_current'): + raise ValueError('checkpoint working-source freshness incomplete') expected_roles = {t['role'] for t in plan['config']['toolchains']} if {t['role'] for t in report['toolchains']} != expected_roles or len(report['toolchains']) != len(expected_roles): raise ValueError('checkpoint toolchain coverage mismatch') diff --git a/test/bootstrap_retention_probe.py b/test/bootstrap_retention_probe.py @@ -9,8 +9,9 @@ from pathlib import Path import subprocess import sys -from bootstrap_retention import REPO, sha, save +from bootstrap_retention import REPO, sha, save, check_support_source from bootstrap_acceptance import verify +from generation_io import versions def main(): @@ -36,11 +37,12 @@ def main(): # These cohorts still include first generation, so rerun full-pair # acceptance as well as the generation-two audit. Portable-only # retirement of first generation needs an explicitly different gate. + io_version = versions(accepted['inputs'][1])[1] fresh = verify(Path(accepted['generations'][0]), generation, Path(runtime), source) if fresh['artifacts'] != accepted['artifacts']: raise ValueError('retained acceptance artifacts differ') run(role + '-portable', [sys.executable, str(REPO / 'test/portable_cli.py'), runtime, - '--backend', 'stable', '--indexed', '--compiler-bundle', str(generation / 'build-stable.arboricx')]) + '--backend', 'stable', '--indexed', '--compiler-io-version', str(io_version), '--compiler-bundle', str(generation / 'build-stable.arboricx')]) run(role + '-smoke', [sys.executable, str(REPO / 'test/bootstrap_smoke.py'), runtime, '--generation', str(generation), '--acceptance', str(acceptance), '--source-root', str(source), '--directory', str(out / (role + '-smoke'))]) @@ -48,7 +50,7 @@ def main(): run(role + '-map', [sys.executable, str(REPO / 'test/bootstrap_generations.py'), str(generation), str(scoped), '--tricu', runtime, '--source-root', str(source), '--map', str(generation / 'source-map.v1'), '--root', 'native_math', '--backend', 'stable', - '--fresh', '--adapter', str(generation / 'io-adapter.arboricx'), '--capabilities', '2', + '--fresh', '--produced-io-version', str(io_version), '--capabilities', '2', '--memory-mib', '8192', '--wall-seconds', '900']) run(role + '-map-audit', [sys.executable, str(REPO / 'test/bootstrap_store_check.py'), str(scoped), '--source-root', str(source), '--root', 'native_math']) @@ -65,12 +67,17 @@ def main(): complete_results=len(smoke['cases']), scoped_map_manifest=new)) support = config['support'] runtime = next(t['runtime'] for t in config['toolchains'] if t['role'] == support['toolchain']) + source = check_support_source(root, support) publication = out / 'support-reproduced' command = [sys.executable, str(REPO / 'test/repair_support_fixture.py'), runtime, str(root / support['preparation']), '--output-directory', str(publication), - '--capabilities', '2', '--verification-memory-mib', '8192'] + '--source-root', str(source), '--capabilities', '2', '--verification-memory-mib', '8192'] if support.get('relocate_patterns'): command += ['--relocate-patterns'] + if support.get('compiler_io_cohort'): + command += ['--compiler-io-cohort'] + if support.get('add_io_v2_support'): + command += ['--add-io-v2-support'] run('support', command, seconds=1500) compared = {} for name in ['support-library.arboricx', 'support-rebuilt.arboricx', 'support-library.v1']: @@ -81,9 +88,15 @@ def main(): actual = publication / 'support-receipts' if {p.name: sha(p) for p in actual.iterdir()} != {p.name: sha(p) for p in expected.iterdir()}: raise ValueError('support receipt/manifests did not reproduce') - run('fixture-freshness', [sys.executable, str(REPO / 'test/fixture_sources.py'), 'current']) + run('fixture-freshness', [sys.executable, str(REPO / 'test/fixture_sources.py'), 'current', '--source-root', str(source)]) + from fixture_sources import current_mismatches + working_differences = current_mismatches(REPO / 'test/fixtures', REPO) + report.update(fixture_source_scope='frozen-reference' if 'source' in support else 'working-tree', + fixture_source=support.get('source'), + working_source_differences=working_differences) report.update(complete=True, support_sha256=compared, - support_receipts_reproduced=True, fixture_sources_current=True) + support_receipts_reproduced=True, fixture_sources_match_selected=True, + fixture_sources_current=not working_differences) finally: save(out / 'report.json', report) print('PASS isolated pair audits, CLI integrity/diagnostics, correctness smokes, scoped map-builds and support reproduction') diff --git a/test/bootstrap_retention_test.py b/test/bootstrap_retention_test.py @@ -32,6 +32,22 @@ class RetentionTests(unittest.TestCase): r.save(self.out / 'plan.json', plan) return plan + def test_selected_support_source_is_checked_and_fingerprinted(self): + source = self.keep / 'source'; (source/'bootstrap').mkdir(parents=True) + (source/'bootstrap/compiler-source-map.v1').write_text('map') + (source/'module.tri').write_text('source') + rows = [('compiler-library', 'module', 'module', 'module.tri', 'pin')] + with patch('fixture_sources.verify', return_value=rows), patch('fixture_sources.current_mismatches', return_value=[]): + self.assertEqual(r.check_support_source(self.root, {'source':'current/source'}), source) + plan = self.plan() + (source/'module.tri').write_text('drift') + with self.assertRaises(ValueError): r.validate_plan(self.root, plan, self.out) + (source/'module.tri').unlink() + (source/'module.tri').symlink_to(self.keep/'compiler') + with self.assertRaises(ValueError): r.check_support_source(self.root, {'source':'current/source'}) + with patch('fixture_sources.verify', return_value=[]), patch('fixture_sources.current_mismatches', return_value=['module.tri']): + with self.assertRaises(ValueError): r.check_support_source(self.root, {'source':'current/source'}) + def test_only_objects_and_preserved_evidence(self): (self.root / 'old/status.json').write_text('{"complete": true}') (self.root / 'old/source.tri').write_text('t') diff --git a/test/bootstrap_smoke.py b/test/bootstrap_smoke.py @@ -11,6 +11,7 @@ from base_workloads import encode as n from collections_batch import ProbeRunner, audit_workspace from http_headers import pair, result, expect from native_library_migration import digest +from generation_io import versions, compiler_flags ROOT = Path(__file__).resolve().parent.parent LIBRARY = '''!import prelude as B @@ -97,7 +98,7 @@ def main(): tool = runner.imported(artifact, 'generation-two') bundle = root/'program.arboricx' runner.run('compile', ['compile','--file',workspace/'program.tri', - '--toolchain',tool,'--tool-store',runner.store,'--store',runner.store, + '--toolchain',tool,*compiler_flags(versions(accepted['inputs'][1])[1]),'--tool-store',runner.store,'--store',runner.store, '--output',bundle,'--compile-timeout','180'], 210) raw = (runner.store/'objects'/tool[:3]/tool).read_bytes() assert digest('arboricx.module-manifest.v1', raw) == tool diff --git a/test/compiler_io_version.py b/test/compiler_io_version.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Explicit compiler protocol selection, through the real CLI and retained compiler. + +This is a synthetic worker boundary control, not a rebuilt v2 compiler generation. +""" +import argparse +import json +import subprocess +from pathlib import Path +from source_order import Runner + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + root = args.directory.resolve() + root.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, root) + # No imports: explicitly construct the bare current-scope core action. The + # returned scope token is deliberately not a compiler publication Result. + worker = root/'worker.tri' + worker.write_text('!export sourceBuildGraph\nsourceBuildGraph _ _ _ _ = t 4 t\n') + bundle = root/'worker.arboricx' + runner.run('compile-worker', ['compile','--file',worker,'--store',runner.store,'--output',bundle]) + pin = runner.imported(bundle, 'import-worker') + source = root/'input.tri' + source.write_text('!export main\nmain = 42\n') + base = ['eval',str(source),'--store',str(root/'application')] + selected = ['--toolchain',pin,'--tool-store',str(runner.store)] + controls = [] + def check(label, options, expected, success=False): + command = [str(runner.exe),*base,*options,'+RTS','-N1','-M256m','-RTS'] + completed = subprocess.run(command, capture_output=True, text=True, timeout=30) + output = completed.stdout + completed.stderr + assert (completed.returncode == 0) == success, (label, output) + assert expected in output, (label,output) + (root/f'{label}.log').write_text(output) + controls.append(dict(label=label, command=command, returncode=completed.returncode)) + check('packaged-v2',['--format','number'],'42',True) + check('v2-default',selected,'Invalid portable compiler Result') + check('explicit-v1',[*selected,'--toolchain-io-version','1'],'IO v1 compilers require a retained v1 runtime') + check('explicit-v2',[*selected,'--toolchain-io-version','2'],'Invalid portable compiler Result') + for version in ['0','3','-1','01','+1','18446744073709551617']: + check(f'invalid-version-{version}',[*selected,'--toolchain-io-version',version], + '--toolchain-io-version must be 1 or 2') + check('no-packaged-relabel',['--toolchain-io-version','1'],'IO v1 compilers require a retained v1 runtime') + assert source.read_text() == '!export main\nmain = 42\n' + (root/'report.json').write_text(json.dumps(dict(controls=controls, preparation=runner.records),indent=2)+'\n') + print(f'{len(controls)} compiler IO-version CLI controls passed') + +if __name__ == '__main__': + main() diff --git a/test/default_frontend.py b/test/default_frontend.py @@ -79,8 +79,8 @@ extra = 9 assert ('source\t' + hashlib.sha256(b'arboricx.source.v1\0' + unicode_source.read_bytes()).hexdigest() + '\n').encode() in unicode_receipt assert run('eval', '--store', store, '--format', 'ternary', input='!export main\nmain = "' + text + '"\n') == expected_tree - for raw, diagnostic in [(b'!export main\nmain = "\xc0\xaf"\n', 'invalid UTF-8 source'), - (b'\xef\xbb\xbf!export main\nmain = t\n', 'UTF-8 source BOM is not permitted')]: + for raw, diagnostic in [(b'!export main\nmain = "\xc0\xaf"\n', 'invalid UTF-8 leading byte at byte 21'), + (b'\xef\xbb\xbf!export main\nmain = t\n', 'initial source BOM is not permitted')]: bad_unicode = root / 'badunicode.tri' bad_unicode.write_bytes(raw) diagnostic_output = run('eval', bad_unicode, '--store', store, ok=False) @@ -150,6 +150,15 @@ extra = 9 exact.write_text(text) run('tree', 'write', exact, '--name', 'value', '--output', payload, '--store', store) assert payload.read_bytes() == bytes(map(int, expected)), vector + for script, arguments in [ + ('native_indexed_store.py', ['--generation', str(root / 'missing-generation'), + '--compiler', str(bundle), '--directory', str(root / 'retired-v1')]), + ('verified_tree_loading.py', [str(root / 'missing-previous'), str(root / 'missing-generation'), + '--compiler-bundle', str(bundle), '--directory', str(root / 'retired-v1')])]: + reply = subprocess.run([sys.executable, str(repo / 'test' / script), host, *arguments], + capture_output=True, text=True, timeout=10) + assert reply.returncode and 'Retired v1 source-splicing probe' in reply.stderr + assert not (root / 'retired-v1').exists() for script, arguments in [('stable_bundle.py', ['--compiler-bundle', str(bundle), '--directory', str(root / 'retired')]), ('stable_costs.py', [str(root / 'retired')])]: reply = subprocess.run([sys.executable, str(repo / 'test' / script), host, *arguments], diff --git a/test/effect_interfaces.py b/test/effect_interfaces.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +"""Opt-in source experiment, not a public effects API or compiler acceptance. + +nix build .# +python3 test/effect_interfaces.py result/bin/tricu --directory /tmp/effect-comparison + +Creates a fresh isolated store. Complete output trees are checked against Python +oracles, not against the other implementation. Retain samples.json and report.json +outside docs/. Benchmark scope includes interpretation and trace rendering; it is +not a measurement of handler dispatch alone. C uses explicit scope nodes and a +linear routing environment; D fuses routing and execution without changing lookup. +Application functions and physical adapters are shared. Every invocation is fresh. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import statistics +import re + +from http_headers import number, pair, result, tree_list +from source_order import Runner, end, field + +ROOT = Path(__file__).resolve().parent.parent + + +def tree(value): + if isinstance(value, bytes): + return value + if isinstance(value, int): + return number(value) + return tree_list([tree(v) for v in value]) + + +def ok(value): + return result(True, tree(value)) + + +def error(value): + return result(False, tree(value)) + + +def publication(store=11, artifact=7): + return [([store, 1, artifact], ok(0)), ([store, 2, artifact], ok(1)), + ([20, 3, 0], ok(42)), ([30, 4, [artifact, 42]], ok(1))] + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + parser.add_argument('--benchmark', action='store_true') + args = parser.parse_args() + if os.geteuid() == 0: + parser.error("OS permission-denial controls require a non-root user") + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + source = ROOT / 'test/effect_interfaces/workflow.tri' + bundle = directory / 'workflow.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, + '--output', bundle]) + pin = runner.imported(bundle, 'import') + passed = [] + + def check(label, export, script, expected, *, remaining=(), trace=None, prefix=()): + script = [list(row) for row in script] + if trace is None: + trace = [event for event, reply in script] + arguments = [] + for index, value in enumerate([*prefix, script]): + path = directory / f'{label}-{index}.tree' + path.write_bytes(tree(value)) + arguments += ['--arg-tree-file', path] + actual = runner.invoke(label, pin, export, arguments) + wanted = tree([expected, list(remaining), trace]) + if actual != wanted: + (directory / f'{label}-expected.tree').write_bytes(wanted) + raise AssertionError(f'{label}: complete output differs (see .tree files)') + passed.append(label) + + for variant in 'abcd': + check(variant+'-publish', variant+'Publish', publication(), ok(1)) + check(variant+'-exists', variant+'Publish', [([11, 1, 7], ok(1))], ok(0)) + check(variant+'-logged', variant+'Logged', [ + ([30, 4, [1, 7]], ok(1)), *publication()[:1], + ([30, 4, [2, 7]], ok(1)), *publication()[1:]], ok(1)) + check(variant+'-copy', variant+'Copy', [([11, 5, 7], ok(7)), + ([12, 2, 7], ok(1))], ok(1)) + check(variant+'-override', variant+'Override', publication(12), ok(1)) + check(variant+'-pinned', variant+'Pinned', publication(11), ok(1)) + check(variant+'-second-write-fails', variant+'Twice', [*publication(), + ([11, 1, 8], ok(0)), ([11, 2, 8], error([80, 11, 2]))], error([80, 11, 2])) + # Shape-malformed and domain-invalid responses: diagnostic deliberately + # names physical service identity for A, logical identity for B. + for label, reply in [('shape', number(42)), ('domain', ok(9))]: + check(variant+'-bad-'+label, variant+'Publish', [([11, 1, 7], reply)], + error([90, 11 if variant == 'a' else 1, 1])) + # An unconsumed sentinel ensures failure really stops subsequent effects. + script = [([11, 1, 7], error([80, 11, 1])), ([11, 2, 7], ok(1))] + check(variant+'-read-fails', variant+'Publish', script, error([80, 11, 1]), + remaining=[list(script[1])], trace=[[11, 1, 7]]) + check('a-missing', 'aMissing', [], error([91, 1, 1])) + check('a-malformed-bundle', 'aMalformedBundle', [], error([91, 1, 1])) + check('b-missing', 'bMissing', [], error([95, [1, 1, 7]]), trace=[]) + check('c-missing', 'cMissing', [], error([95, [1, 1, 7]]), trace=[]) + check('d-missing', 'dMissing', [], error([95, [1, 1, 7]]), trace=[]) + # Negative controls: no implementation may ignore exhaustion or mismatches. + for variant in 'abcd': + check(variant+'-exhausted-script', variant+'Publish', [], + error([93, [11, 1, 7]]), trace=[[11, 1, 7]]) + wrong = [([12, 1, 7], ok(0))] + check(variant+'-wrong-script', variant+'Publish', wrong, + error([92, [11, 1, 7], [12, 1, 7]]), + remaining=[list(wrong[0])], trace=[[11, 1, 7]]) + + for variant in 'bcd': + check(variant+'-escaped', variant+'Escaped', [([11, 1, 7], ok(0))], ok(0)) + check(variant+'-restore', variant+'Restore', + [([12, 1, 7], ok(0)), ([11, 1, 8], ok(1))], ok(1)) + check(variant+'-emission', variant+'Emission', [([11, 1, 7], ok(0))], ok(0)) + check(variant+'-chain', variant+'Chain', [([12, 1, 7], ok(0))], ok(0)) + check(variant+'-no-reentry', variant+'NoReentry', [([11, 1, 7], ok(0))], ok(0)) + check(variant+'-outer-log', variant+'OuterLog', publication(), ok(1)) + check(variant+'-forward', variant+'Forward', + [([11, 1, 7], ok(0)), ([12, 1, 8], ok(1))], ok(1)) + sentinel = [([11, 1, 7], ok(0))] + script = [([30, 4, [1, 7]], error([80, 30, 4])), *sentinel] + check(variant+'-handler-failure', variant+'Logged', script, error([80, 30, 4]), + remaining=[list(row) for row in sentinel], trace=[[30, 4, [1, 7]]]) + + # Real filesystem with a fixed clock; same adapter for both implementations. + for variant in 'abcd': + fs = directory / ('filesystem-' + variant) + fs.mkdir() + artifact, log = fs / 'artifact', fs / 'log' + def filesystem(label): + output = runner.run(label, ['run', '--module', pin, '--name', variant+'Filesystem', + '--store', runner.store, '--io', '--format', 'ternary', + '--arg', str(artifact), '--arg', str(log)]) + text = next(re.match(r'^([012]+)', line)[1] for line in output.splitlines() + if re.match(r'^[012]+', line)) + raw = bytes(map(int, text)) + return raw[:end(raw)] + # OS traversal denial replaces the retired source-path grant policy. + fs.chmod(0) + try: + denied = filesystem(variant+'-filesystem-denied') + finally: + fs.chmod(0o700) + assert field(denied, 0) == b'\0', 'permission denial must remain a failure' + assert not artifact.exists() and not log.exists() + assert filesystem(variant+'-filesystem-new') == ok(1) + assert artifact.read_bytes() == b'\x07' + assert log.read_text() == 'published 7 at 42\n' + log.write_text('sentinel: existing artifacts must not log again\n') + assert filesystem(variant+'-filesystem-existing') == ok(0) + assert artifact.read_bytes() == b'\x07' + assert log.read_text() == 'sentinel: existing artifacts must not log again\n' + passed.extend([variant+'-filesystem-denied', variant+'-filesystem-new', + variant+'-filesystem-existing']) + + benchmarks = [] + if args.benchmark: + for workload, export in [('clock', 'Bench'), ('publication', 'PublishBench')]: + for count in (10, 100): + items = [0]*count if workload == 'clock' else list(range(7, 7+count)) + script = ([([20, 3, 0], ok(42))]*count if workload == 'clock' else + [row for artifact in items for row in publication(artifact=artifact)]) + for depth in (0, 4, 16): + rows = {variant: [] for variant in 'acd'} + # Rotate order across repeats to reduce systematic timing bias. + for repeat, order in enumerate(('acd', 'cda', 'dac')): + for variant in order: + label = f'bench-{workload}-{variant}-{count}-{depth}-{repeat}' + check(label, variant+export, script, ok(0), + prefix=(items, list(range(1000, 1000+depth)))) + rows[variant].append(runner.records[-1]) + for variant, samples in rows.items(): + benchmarks.append(dict(workload=workload, variant=variant, + count=count, depth=depth, + seconds=statistics.median(r['invoke_seconds'] for r in samples), + allocated_bytes=statistics.median(r['allocated_bytes'] for r in samples))) + report = dict(passed=passed, benchmarks=benchmarks, + source_sha256=hashlib.sha256(source.read_bytes()).hexdigest(), + bundle_sha256=hashlib.sha256(bundle.read_bytes()).hexdigest(), + host=str(args.tricu.resolve()), + inputs={str(path.relative_to(ROOT)): hashlib.sha256(path.read_bytes()).hexdigest() + for path in [Path(__file__).resolve(), ROOT / 'test/effect_interfaces/tricu.workspace', + ROOT / 'lib/base.tri', + ROOT / 'lib/io-v2.tri', ROOT / 'bootstrap/build-stable.arboricx']}, + limits=['finite scripted clock/read codecs; filesystem smoke uses fixed clock', + 'no concurrency or resource scopes in the experimental effect protocol; physical files use IO v2 scopes', + 'benchmark includes trace construction and rendering', + 'A depth is a control: captured services bypass unrelated scopes', + 'C/D use the same linear environment lookup; B retained only for semantic checks']) + (directory / 'report.json').write_text(json.dumps(report, indent=2)+'\n') + print(json.dumps(report, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/test/effect_interfaces/README.md b/test/effect_interfaces/README.md @@ -0,0 +1,205 @@ +# Effect-interface comparison fixture + +This is an opt-in, source-level experiment, not a supported library API. It +compares closure-backed service bundles (`a*` exports), tree-rewriting handlers +(`b*` exports), environment-interpreted handler scopes (`c*` exports), and +fused routing/execution (`d*` exports). +All use the same physical request protocol and physical IO v2 filesystem adapter. +A/B/C share a scripted evaluator; D incorporates that evaluator's rules into its +routing machine. B/C/D reuse identical application functions. B is retained only +for semantic regression checks, not as a performance candidate. No compiler or +host implementation changes are required. Files are scoped resources; the +experimental effect protocol itself remains sequential. Its filesystem checks +include real OS denial and ensure existing artifacts do not produce another log. + +From the repository root: + +```sh +nix build .# +python3 test/effect_interfaces.py result/bin/tricu \ + --directory /tmp/effect-comparison --benchmark +``` + +The output directory must not exist. Omitting `--benchmark` still runs the +behavioral and filesystem checks. The runner compiles the checked-in source +using the packaged stable compiler, imports the bundle into an isolated store, +and invokes exports in fresh host processes. `report.json` records source and +bundle digests, checks, and benchmark medians; `samples.json` and invocation logs +retain individual measurements. All generated artifacts stay in that directory. +This is not compiler-generation acceptance or a general effect-system benchmark. + +## Protocol and scope + +The experimental tree tags are distinct from host IO and `base.exceptE`: + +- 100: return a value. +- 101: sequence an action and a continuation. +- 102: request `[instance operation argument]`, with a reply continuation. +- 103: terminate with a diagnostic. +- 104 (C/D): install `[instance handlerFunction]` around a body. + +Replies use the ordinary base Result representation with empty remainders. +Operations are existence (1), write (2), clock (3), log (4), and read (5). +Logical instances are default store (1), clock (2), logger (3), source store (4), +and destination store (5). Physical instances are stores 11/12, clock 20, logger +30. These integers are routing labels, not fresh identities or security tokens. + +The B handler fixture recursively transforms trees. Matching requests are lowered +to physical requests outside that handler; their continuations resume inside +it. Nonmatching requests retain the handler around their continuations. The +innermost matching handler wins. Handler ordering therefore matters. The logging +interposer must be inside the store-lowering handler to see logical store +requests. There is no multishot, resource-scope, or concurrency API. + +C interprets scope nodes with an immutable environment and continuation stack. +It normalizes only up to the next physical request, which the existing backend +then executes. Each stack frame saves a continuation and its caller environment; +scope exit restores the previous environment using an identity continuation. +Handler lookup returns both the handler and the environment suffix outside it. +A handler's own actions run in that outer environment, excluding itself and any +intervening inner scopes. Its request continuation resumes in the original +caller's environment. This preserves B's lexical routing semantics, rather than +silently substituting dynamic lookup for handler-emitted requests. + +Lookup is linear in active handler depth. Normalizing binds does not traverse +all handlers; installing a scope does not rewrite its body. C is a source-level +interpreter experiment, not a new host primitive or a constant-time dispatcher. +Constructors are trusted: neither representation validates arbitrary supplied +handler functions. Returning an inert action from a scope does not automatically +capture that scope; explicitly wrapping it in a handler is a separate operation. + +D combines environment routing and execution in one machine. It consumes +scripted physical requests directly instead of building normalized request trees +for a second interpreter. The IO variant invokes a supplied physical adapter +directly. Its handler lookup, scope frames, and handler-emission environment are +unchanged from C. The scripted implementation carries script/trace state through +the fused machine; fewer intermediate constructors do not by themselves promise +less allocation after compilation to tree calculus. + +A service bundle has exactly three fields: store, clock, and logger functions. +Constructed publishers capture this bundle. The positional representation is +intentionally small, not a recommendation for a public record API. Missing +services use an explicit failing proxy; a malformed bundle shape is tested +separately. Arbitrary malformed callable fields are not dynamically recognized +as functions or made safe by this representation. + +The fixed clock response is 42 and the read response is 7. This finite response +validation is an experimental domain contract, not a general time or storage +codec. Existence accepts 0/1; writes and logging accept 1. Malformed response +shapes and out-of-domain values terminate with a diagnostic. Failure responses +propagate, without rolling back prior requests. + +Diagnostic codes are 80 (scripted operational failure), 90 (invalid response, +with instance/operation), 91 (missing bundle service, with role/operation), +92 (script request mismatch), 93 (script exhaustion), 94 (invalid evaluator +input), and 95 (unhandled request). A-side response diagnostics name the physical +instance; B/C/D-side diagnostics name the logical role. These are structured test +values, not finished human-facing diagnostics. + +## Behavioral controls + +The Python runner constructs independent complete-tree expectations for all +implementations, including remaining script and ordered trace. It does not use +one implementation as the other's oracle. Cases include: + +- New/existing artifact, store-internal logging, and copying between stores. +- A contextual callback under a store override, and a callback that preserves + its original store despite that override. +- Two publications where the second write fails; the first publication's + effects remain in the trace and the failed publication does not log success. +- Invalid responses, missing service/handler, malformed bundle, mismatched and + exhausted scripts, and an unconsumed sentinel after early failure. +- B/C/D scope restoration, handler-emitted requests bypassing intervening inner + scopes, chained routing (including physical labels), no handler self-reentry, + ordering of logging interposition, forwarding with the caller scope retained, + handler failure before the underlying store operation, and returned inert + actions executed outside their construction scope. + +For the contextual callback, A supplies an unconfigured callback with new +services; B/C/D supply an ordinary request-producing callback under a handler. +For stable store identity, A captures its services and B/C/D wrap its callback's +computation in the original store interpretation. B/C/D pin only the store in this +fixture; A's captured bundle also pins clock/logger. The comparison checks store +routing, not equivalence of those broader dependency-binding choices. + +The filesystem smoke uses real permission-checked reads/writes and the same +fixed clock. It verifies denied access creates nothing, publication writes byte +7 and the expected log, and an existing artifact causes neither a rewrite nor +another log. It is deliberately not an atomic or concurrent publication protocol. + +## Measurement contract + +Benchmarks cover 10/100 clock requests and 10/100 complete publications of +distinct artifacts, each with 0/4/16 unrelated handler scopes. All publications +are scripted as new artifacts and perform existence/write/clock/log operations. +There are three fresh-process invocations per cell; variant order rotates across +repeats to reduce systematic timing bias. A's depth argument is a control: +captured services do not traverse unrelated scopes. C/D traverse the same +linear routing environment during request lookup. Only A/C/D are benchmarked; +B remains covered by the behavioral controls. Median time and allocated bytes cover +invocation through complete rendered output, including response validation, +script matching, trace construction, and rendering, but excluding bundle load. +Allocated bytes are allocation volume, not peak live memory. + +This measures the supplied implementations. In particular, growth with B's +handler depth does not establish an inherent cost of handler-scoped APIs: an +environment interpreter removes per-handler bind rewriting but retains linear +lookup and scope/continuation bookkeeping. Shared backend validation, script +matching, and output costs remain in all measurements. No production default +should be selected from timing alone. + + +## Artifact-server request slice + +```sh +python3 test/effect_server.py result/bin/tricu \ + --directory /tmp/effect-server --benchmark +``` + +`server.tri` adapts the production `GET /_arboricx/bundle/hash/HASH` path. It +imports the real server's hash contract, path builder and prefix parser, and its +HTTP response constructors. Invalid hashes return 400 before storage, reads return bundle bytes on success, +and read failures map to redacted 404s. Read-Result remainders are deliberately +discarded; arbitrary Result payloads are not made safe by adding handlers. + +The experiment adds completion logging of `[requestContext [hash status]]`. +This experimental log format is separate from production access logging. Logger failure terminates +the experiment's action rather than pretending that the response was delivered. +Only the selected bundle-GET route logs; unmatched routes retain the production +404 response without completion logging. Ops 6/7 represent raw read and log +Results and do not use the microbenchmark's finite response codecs. + +The captured-service version constructs a two-field request-specific bundle of +reader and logger functions and closes over it in the router. The handler version +installs storage and logging context around a router with no service arguments. +Both reuse the same `readBundle` validation/reply-mapping helper. Ordinary, +overridden, and pinned-store modes are exercised. The dual-component case uses +both a pinned primary-store reader and a contextual alternate-store reader under +one request context. The service version shares its context logger while choosing +reader closures; the handler version pins only the first component's store. + +The runner independently constructs complete HTTP response bytes, log events, +and scripted request traces. Checks include binary response bodies, malformed +read/log replies, ignored read remainders, invalid/traversal-like hashes, unmatched +methods/targets, logger failure, and context isolation across sibling requests. +Actual filesystem runs independently check success, malformed hashes, missing +files, OS permission denial, and failed-log attempt retention. The old differential +execution of the v1 route is retired; expectations are complete Python-built +responses and events, not results from another interpreter. File reads use a +64-KiB budget. OS denial tests require a non-root process and restore directory +permissions in `finally`; absence of grant flags is no longer a denial policy. The raw +physical adapter assumes its application has validated hashes; it is not a +security boundary for hostile hand-constructed requests. + +Server benchmarks cover 1/20 requests, with per-request scope/service setup and +three fresh-process observations per variant. Every complete HTTP response is +returned and rendered, including intermediate responses: discarding those values +would allow lazy evaluation to skip response construction. Scripted runs also +render complete traces. Filesystem runs use warm local reads and explicitly thread log state through +each source interpreter, then render responses and logs. There is no native +mutable state slot, implicit state inheritance, or shared child-task state. There is no listening socket, +network latency, scheduler contention, streaming, or server-throughput claim. + +The local workspace selects experiment imports to these fixtures and existing +production libraries. Neither runner modifies production server source, compiler +artifacts, or the host ABI. diff --git a/test/effect_interfaces/server.tri b/test/effect_interfaces/server.tri @@ -0,0 +1,169 @@ +!import base * +!import io_v2 as IO +!import io_v2_storage as Storage +!import effects as E +!import arboricx_server as Server +!import http as HTTP +!export * + +-- Adapt the actual GET /_arboricx/bundle/hash/HASH request path, not a toy +-- publication protocol. Reuse production hash validation, paths, target parsing, +-- and complete HTTP response construction. No socket/framing changes. +-- Ops 6/7 are raw file-read Result and completion-log Result, respectively. +-- This is an isolated experiment: production server source is not modified. + +-- Malformed hashes never reach storage; read failures +-- become redacted HTTP 404, and Result remainders are intentionally discarded here. +readBundle reader hash = case (checkContract Server.bundleHashC hash) of + ((t t), (value, _)) = (E.seq (reader value) (reply : case reply of + ((t t), (bytes, _)) = E.ret (ok bytes t) + (t, (message, _)) = E.ret (err 404 "Artifact unavailable\n") + _ = E.ret (err 404 "Invalid read result"))) + (t, (diagnostic, _)) = E.ret (err 400 diagnostic) + _ = E.ret (err 400 "Invalid hash contract result") + +replyStatus ((t t), _) = 200 +replyStatus (t, (status, _)) = status +replyStatus _ = 500 +renderReply ((t t), (bytes, _)) = Server.bundleResponse bytes +renderReply (t, (status, message)) = HTTP.errorResponse status message +renderReply _ = HTTP.errorResponse 500 "Invalid bundle result" + +-- Completion logging is an experimental addition, not existing server behavior. +-- A logger failure is terminal, not silently swallowed or reported as success. +logReply action = (E.seq action (reply : case reply of + ((t t), (1, t)) = E.ret 1 + (t, (message, t)) = E.stop message + _ = E.stop "Invalid completion-log response")) + +-- A: construct a request-specific bundle once; close over it in the router. +-- Leaf services retain identity; request context is captured in the logger. +storeService backend hash = E.request backend 6 hash +loggerService context event = logReply (E.request 30 7 [context event]) +serveA [reader logger] hash = (do E.seq + reply <- readBundle reader hash + logger [hash (replyStatus reply)] + E.ret (renderReply reply)) +serveA _ _ = E.stop "Missing request services" +routeA dependencies "GET" target = case (Server.stripPrefix Server.bundleHashPrefix target) of + (t hash) = serveA dependencies hash + _ = E.ret HTTP.notFoundResponse +routeA _ _ _ = E.ret HTTP.notFoundResponse +makeRouter backend context = routeA [(storeService backend) (loggerService context)] + +-- H: the router has no service arguments. Context and storage are installed at +-- the request boundary; deeper code emits logical requests through these APIs. +readCurrent hash = E.request 1 6 hash +logCurrent event = logReply (E.request 3 7 event) +serveH hash = (do E.seq + reply <- readBundle readCurrent hash + logCurrent [hash (replyStatus reply)] + E.ret (renderReply reply)) +routeH "GET" target = case (Server.stripPrefix Server.bundleHashPrefix target) of + (t hash) = serveH hash + _ = E.ret HTTP.notFoundResponse +routeH _ _ = E.ret HTTP.notFoundResponse +withContext context action = E.scope 3 (op arg : E.request 30 op [context arg]) action +requestScope backend context action = withContext context (E.cRoute 1 backend action) + +-- The same request entrypoint supports ordinary, overridden, and pinned-store +-- modes. A reconstructs a router to change its context/store configuration. +-- H wraps the callback's computation. Neither changes the business functions. +selectA 0 context = makeRouter 11 context +selectA 1 context = makeRouter 12 context +selectA 2 context = makeRouter 11 context +selectA _ context = makeRouter 11 context +selectH 0 context action = requestScope 11 context action +selectH 1 context action = requestScope 11 context (E.cRoute 1 12 action) +selectH 2 context action = requestScope 12 context (E.cRoute 1 11 action) +selectH _ context action = requestScope 11 context action + +aRequest mode context method target script = E.run (selectA mode context method target) script +cRequest mode context method target script = E.runC (selectH mode context (routeH method target)) script +dRequest mode context method target script = E.runD (selectH mode context (routeH method target)) script + +-- A pinned health/audit component fetched in the SAME request as a contextual +-- component. Pin only storage; both inherit that request's logging context. +-- The service version shares its context logger while selecting the reader. +dualA context target = (let + logger = loggerService context + pinned = routeA [(storeService 11) logger] + contextual = routeA [(storeService 12) logger] + in do E.seq + first <- pinned "GET" target + second <- contextual "GET" target + E.ret [first second]) +dualH target = (do E.seq + first <- E.cRoute 1 11 (routeH "GET" target) + second <- routeH "GET" target + E.ret [first second]) +aDual context target script = E.run (dualA context target) script +cDual context target script = E.runC (requestScope 12 context (dualH target)) script +dDual context target script = E.runD (requestScope 12 context (dualH target)) script + +-- Explicit sequential log state is threaded through the test interpreter. +-- Reads use scoped IO v2 and a 64-KiB budget; no native state or grant flags. +-- These raw reads test dependency selection, not content-address authentication. +backend root alternate events [11 6 hash] = IO.bind + (Storage.readBytes (Server.bundleObjectPath root hash) 65536) + (reply : IO.pure [reply events]) +backend root alternate events [12 6 hash] = IO.bind + (Storage.readBytes (Server.bundleObjectPath alternate hash) 65536) + (reply : IO.pure [reply events]) +backend _ _ events [30 7 event] = IO.pure [(ok 1 t) (pair event events)] +backend _ _ events event = IO.pure [(err [95 event] t) events] + +-- Unfused baseline retains its own action/continuation traversal; C feeds its +-- normalized tree through this evaluator. D retains fused lexical dispatch. +executeAdapter adapter state action stack = let + finish reply = IO.pure [reply state] + resume value [] = finish (ok value t) + resume value [k | rest] = executeAdapter adapter state (k value) rest + resume _ _ = finish (err [94] t) + in case action of + (100, value) = resume value stack + (101, (first, k)) = executeAdapter adapter state first (pair k stack) + (102, (event, k)) = IO.bind (adapter state event) (result : case result of + [reply nextState] = executeAdapter adapter nextState (k reply) stack + _ = IO.fail "Invalid test adapter state reply") + (103, diagnostic) = finish (err diagnostic t) + _ = finish (err [94] t) +withRecordedLogs action = IO.io (IO.bind action (result : case result of + [reply events] = IO.pure [reply (reverse events)] + _ = IO.fail "Invalid test interpreter state reply")) +aFilesystem root alternate mode context target = withRecordedLogs (executeAdapter (backend root alternate) [] (selectA mode context "GET" target) []) +cFilesystem root alternate mode context target = withRecordedLogs (executeAdapter (backend root alternate) [] (E.normalize (selectH mode context (routeH "GET" target)) [] []) []) +dFilesystem root alternate mode context target = withRecordedLogs (E.executeDStateIO (backend root alternate) [] (selectH mode context (routeH "GET" target)) [] []) + +-- A failed native logger still records its attempted event in source state. +-- Failure stops the application, not the test's observation of that state. +failingBackend _ _ events [30 7 event] = IO.pure [(err "log unavailable" t) (pair event events)] +failingBackend root alternate events event = backend root alternate events event +aFilesystemLogFailure root alternate mode context target = withRecordedLogs + (executeAdapter (failingBackend root alternate) [] (selectA mode context "GET" target) []) +cFilesystemLogFailure root alternate mode context target = withRecordedLogs + (executeAdapter (failingBackend root alternate) [] (E.normalize (selectH mode context (routeH "GET" target)) [] []) []) +dFilesystemLogFailure root alternate mode context target = withRecordedLogs + (E.executeDStateIO (failingBackend root alternate) [] (selectH mode context (routeH "GET" target)) [] []) + +-- Request-boundary setup is paid for EACH request in these benchmarks, including +-- its context installation. Complete response bytes are returned for observation, +-- not discarded (which would skip lazy HTTP rendering). This differs from the clock benchmark's one scope. +requestsA [] target = E.ret [] +requestsA [context | rest] target = (do E.seq + response <- makeRouter 11 context "GET" target + responses <- requestsA rest target + E.ret (pair response responses)) +requestsA _ _ = E.stop [94] +requestsH [] target = E.ret [] +requestsH [context | rest] target = (do E.seq + response <- requestScope 11 context (routeH "GET" target) + responses <- requestsH rest target + E.ret (pair response responses)) +requestsH _ _ = E.stop [94] +aRequests contexts target script = E.run (requestsA contexts target) script +cRequests contexts target script = E.runC (requestsH contexts target) script +dRequests contexts target script = E.runD (requestsH contexts target) script +aRequestsIO root alternate contexts target = withRecordedLogs (executeAdapter (backend root alternate) [] (requestsA contexts target) []) +cRequestsIO root alternate contexts target = withRecordedLogs (executeAdapter (backend root alternate) [] (E.normalize (requestsH contexts target) [] []) []) +dRequestsIO root alternate contexts target = withRecordedLogs (E.executeDStateIO (backend root alternate) [] (requestsH contexts target) [] []) diff --git a/test/effect_interfaces/tricu.workspace b/test/effect_interfaces/tricu.workspace @@ -0,0 +1,14 @@ +tricu.workspace.v1 +effects workflow.tri +arboricx_server ../../lib/arboricx/server.tri +base ../../lib/base.tri +bytes ../../lib/bytes.tri +conversions ../../lib/conversions.tri +http ../../lib/http.tri +io ../../lib/io.tri +native_math ../../lib/math/native.tri +parser ../../lib/parser.tri +prelude ../../lib/prelude.tri +socket ../../lib/socket.tri +io_v2 ../../lib/io-v2.tri +io_v2_storage ../../lib/io-v2/storage.tri diff --git a/test/effect_interfaces/workflow.tri b/test/effect_interfaces/workflow.tri @@ -0,0 +1,384 @@ +!import base * +!import io_v2 as IO +!export * + +-- Experimental protocol, deliberately NOT host IO or base.exceptE. +-- 100 return; 101 sequence; 102 request [instance operation argument] + reply k; +-- 103 terminal diagnostic. Replies use base Result, with empty remainder. +ret value = pair 100 value +seq action k = pair 101 (pair action k) +request instance operation argument = pair 102 (pair [instance operation argument] ret) +stop diagnostic = pair 103 diagnostic + +-- Validate response domains at the shared operation boundary. Preserve failures. +checked instance operation action = (seq action (reply : case reply of + ((t t), (value, t)) = checkValue instance operation value + (t, (message, t)) = stop message + _ = stop [90 instance operation])) +checkValue instance 1 0 = ret 0 +checkValue instance 1 1 = ret 1 +checkValue instance 2 1 = ret 1 +checkValue instance 3 42 = ret 42 +checkValue instance 4 1 = ret 1 +checkValue instance 5 7 = ret 7 +checkValue instance operation _ = stop [90 instance operation] +-- Clock/read domains are intentionally finite scripted domains, not public codecs. + +-- A: constructed services capture implementation identity. Application-facing +-- publishA is itself a closure after supplying its small three-service bundle. +service instance operation argument = checked instance operation (request instance operation argument) +services store = [ (service store) (service 20) (service 30) ] +storeOf [store clock logger] = store +storeOf _ = (operation argument : stop [91 1 operation]) +clockOf [store clock logger] = clock +clockOf _ = (operation argument : stop [91 2 operation]) +logOf [store clock logger] = logger +logOf _ = (operation argument : stop [91 3 operation]) + +publishA dependencies artifact = (do seq + exists <- storeOf dependencies 1 artifact + publishAbsentA dependencies artifact exists) +publishAbsentA dependencies artifact 0 = (do seq + storeOf dependencies 2 artifact + stamp <- clockOf dependencies 3 0 + logOf dependencies 4 [artifact stamp] + ret 1) +publishAbsentA _ _ _ = ret 0 + +-- Logging added INSIDE a store helper: only service construction changes. +loggedStore logger store operation argument = (do seq + logger 4 [operation argument] + store operation argument) +loggedServices store = [(loggedStore (service 30) (service store)) (service 20) (service 30)] + +copyA source destination artifact = (do seq + value <- source 5 artifact + destination 2 value + ret 1) +twiceA publisher = (do seq + publisher 7 + publisher 8) + +-- B: application functions emit logical requests. Handlers lower logical +-- instance IDs to physical ones. Handler-produced backend requests are outside +-- that handler; continuations resume inside it. Unknown requests are forwarded +-- WITH the handler reinstalled around their continuations. +operation instance op argument = checked instance op (request instance op argument) +publishB artifact = (do seq + exists <- operation 1 1 artifact + publishAbsentB artifact exists) +publishAbsentB artifact 0 = (do seq + operation 1 2 artifact + stamp <- operation 2 3 0 + operation 3 4 [artifact stamp] + ret 1) +publishAbsentB _ _ = ret 0 + +route wanted backend action = case action of + (100, value) = ret value + (101, (first, k)) = seq (route wanted backend first) (value : route wanted backend (k value)) + (102, ([instance op argument], k)) = (lazyBool + (_ : seq (request backend op argument) (reply : route wanted backend (k reply))) + (_ : pair 102 (pair [instance op argument] (reply : route wanted backend (k reply)))) + (equal? wanted instance)) + _ = action + +handlers store action = route 3 30 (route 2 20 (route 1 store action)) +-- Instrumentation is a handler, not a change to publishB or its callers. +logStore action = case action of + (100, value) = ret value + (101, (first, k)) = seq (logStore first) (value : logStore (k value)) + (102, ([1 op argument], k)) = (do seq + operation 3 4 [op argument] + reply <- request 1 op argument + logStore (k reply)) + (102, (event, k)) = pair 102 (pair event (reply : logStore (k reply))) + _ = action + +copyB artifact = (do seq + value <- operation 4 5 artifact + operation 5 2 value + ret 1) +twiceB publisher = (do seq + publisher 7 + publisher 8) + +-- Return [Result, remaining script, chronological trace]. Script rows are +-- [expected request, reply]. Neither implementation supplies its own oracle. +-- Stack-based evaluator avoids repeatedly rewriting the whole action for bind. +run action script = execute action [] script [] +execute action stack script reversed = (let + resume value [] script reversed = [(ok value t) script (reverse reversed)] + resume value [k | stack] script reversed = execute (k value) stack script reversed + resume _ _ script reversed = [(err [94] t) script (reverse reversed)] + consume event k stack [[expected reply] | script] reversed = (lazyBool + (_ : execute (k reply) stack script (pair event reversed)) + (_ : [(err [92 event expected] t) (pair [expected reply] script) (reverse (pair event reversed))]) + (equal? event expected)) + consume event _ _ script reversed = [(err [93 event] t) script (reverse (pair event reversed))] + dispatch event{[11 op arg]} k = consume event k stack script reversed + dispatch event{[12 op arg]} k = consume event k stack script reversed + dispatch event{[20 op arg]} k = consume event k stack script reversed + dispatch event{[30 op arg]} k = consume event k stack script reversed + dispatch event _ = [(err [95 event] t) script (reverse reversed)] + in case action of + (100, value) = resume value stack script reversed + (101, (first, k)) = execute first (pair k stack) script reversed + (102, (event, k)) = dispatch event k + (103, diagnostic) = [ (err diagnostic t) script (reverse reversed) ] + _ = [ (err [94] t) script (reverse reversed) ]) + +-- Workloads take independently supplied scripts from the Python harness. +aPublish script = run (publishA (services 11) 7) script +bPublish script = run (handlers 11 (publishB 7)) script +aLogged script = run (publishA (loggedServices 11) 7) script +bLogged script = run (handlers 11 (logStore (publishB 7))) script +aCopy script = run (copyA (service 11) (service 12) 7) script +bCopy script = run (route 5 12 (route 4 11 (copyB 7))) script +aTwice script = run (twiceA (publishA (services 11))) script +bTwice script = run (handlers 11 (twiceB publishB)) script +-- Contextual callback: A explicitly constructs/rebinds it for the override; +-- B interprets an ordinary callback under the inner handler. +withServices dependencies callback = callback dependencies 7 +withStore backend callback = route 1 backend (callback 7) +aOverride script = run (withServices (services 12) publishA) script +bOverride script = run (handlers 11 (withStore 12 publishB)) script +-- Stable callback: A captures its services; B captures its interpretation. +aPinned script = (let + publisher = publishA (services 11) + in run (withServices (services 12) (_ artifact : publisher artifact)) script) +bPinned script = (let + publisher artifact = route 1 11 (publishB artifact) + in run (handlers 11 (withStore 12 publisher)) script) +-- Missing dependency: valid A shape with an explicit missing-service proxy; +-- B omits the store handler, leaving the logical request unhandled. +aMalformedBundle script = run (publishA [] 7) script +aMissing script = run (publishA [(op arg : stop [91 1 op]) (service 20) (service 30)] 7) script +bMissing script = run (route 3 30 (route 2 20 (publishB 7))) script + +-- Repeated clock requests; depth is represented as a list of irrelevant IDs. +repeatA clock [] = ret 0 +repeatA clock [_ | rest] = seq (clock 3 0) (_ : repeatA clock rest) +repeatA _ _ = stop [94] +repeatB [] = ret 0 +repeatB [_ | rest] = seq (operation 2 3 0) (_ : repeatB rest) +repeatB _ = stop [94] +wrap [] action = action +wrap [id | rest] action = route id 99 (wrap rest action) +wrap _ action = action +aBench iterations depth script = run (repeatA (service 20) iterations) script +bBench iterations depth script = run (route 2 20 (wrap depth (repeatB iterations))) script + +-- Same thin physical adapter for both designs. Real filesystem, fixed clock, +-- one artifact domain. No atomic-publication/race-safety claim: this experiment +-- tests dependency composition, not the production content-addressed store. +-- Ordinary Results only; terminal diagnostics do not become host exceptions. +writeBytes path bytes = IO.scope (owner : do IO.onOk + writer <- IO.openWriter owner path + IO.writeAll writer bytes) +physical path logPath [11 1 7] = IO.bind (IO.statPath path) (reply : case reply of + ((t t), _) = IO.pure (ok 1 t) + (t, (["not-found" operation detail progress], t)) = IO.pure (ok 0 t) + _ = IO.pure reply) +physical path logPath [11 2 7] = IO.onOk (writeBytes path [7]) + (_ : IO.pure (ok 1 t)) +physical path logPath [20 3 0] = IO.pure (ok 42 t) +physical path logPath [30 4 [7 42]] = IO.onOk (IO.encodeUTF8 "published 7 at 42\n") + (bytes : IO.onOk (writeBytes logPath bytes) (_ : IO.pure (ok 1 t))) +physical _ _ event = IO.pure (err [95 event] t) + +executeIO path logPath action stack = (let + resumeIO value [] = IO.pure (ok value t) + resumeIO value [k | rest] = executeIO path logPath (k value) rest + resumeIO _ _ = IO.pure (err [94] t) + in case action of + (100, value) = resumeIO value stack + (101, (first, k)) = executeIO path logPath first (pair k stack) + (102, (event, k)) = (IO.bind (physical path logPath event) + (reply : executeIO path logPath (k reply) stack)) + (103, diagnostic) = IO.pure (err diagnostic t) + _ = IO.pure (err [94] t)) +aFilesystem path logPath = IO.io (executeIO path logPath (publishA (services 11) 7) []) +bFilesystem path logPath = IO.io (executeIO path logPath (handlers 11 (publishB 7)) []) + +-- C: explicit handler scopes, interpreted with an environment and continuation +-- stack. Application functions (publishB/copyB/repeatB) are unchanged. +-- 104 = scope ([logical instance, handler function], body). +-- Handler functions run in their installation environment (outside themselves +-- and intervening inner scopes); request continuations resume in the caller's +-- environment. This is the same lexical handling rule as B's tree rewriting. +scope instance handler body = pair 104 (pair [instance handler] body) +cRoute instance backend body = scope instance (op arg : request backend op arg) body +cHandlers store body = cRoute 3 30 (cRoute 2 20 (cRoute 1 store body)) +cLogStore body = scope 1 (op arg : do seq + operation 3 4 [op arg] + request 1 op arg) body + +-- Look up the nearest matching handler AND its outer environment. Returning +-- that suffix is essential: emitted requests must not re-enter this handler. +findHandler wanted [[instance handler] | outer] = (lazyBool + (_ : pair handler outer) + (_ : findHandler wanted outer) + (equal? wanted instance)) +findHandler _ _ = t + +-- Normalize only up to the next physical request. No recursive transformation +-- of the body at scope installation, nor per-handler rewriting of bind nodes. +-- Stack frames save [caller environment, continuation]. Scope exit uses ret as +-- an identity continuation to restore the outer environment even without bind. +normalize action environment stack = (let + resume value [] = ret value + resume value [[saved k] | rest] = normalize (k value) saved rest + resume _ _ = stop [94] + dispatch event{[instance op arg]} k = (case findHandler instance environment of + (handler, outer) = normalize (handler op arg) outer (pair [environment k] stack) + _ = pair 102 (pair event (reply : normalize (k reply) environment stack))) + dispatch _ _ = stop [94] + in case action of + (100, value) = resume value stack + (101, (first, k)) = normalize first environment (pair [environment k] stack) + (102, (event, k)) = dispatch event k + (103, diagnostic) = stop diagnostic + (104, (entry, body)) = normalize body (pair entry environment) (pair [environment ret] stack) + _ = stop [94]) +runC action script = run (normalize action [] []) script +cPublish script = runC (cHandlers 11 (publishB 7)) script +cLogged script = runC (cHandlers 11 (cLogStore (publishB 7))) script +cCopy script = runC (cRoute 5 12 (cRoute 4 11 (copyB 7))) script +cTwice script = runC (cHandlers 11 (twiceB publishB)) script +cOverride script = runC (cHandlers 11 (cRoute 1 12 (publishB 7))) script +cPinned script = (let + publisher artifact = cRoute 1 11 (publishB artifact) + in runC (cHandlers 11 (cRoute 1 12 (publisher 7))) script) +cMissing script = runC (cRoute 3 30 (cRoute 2 20 (publishB 7))) script +cWrap [] action = action +cWrap [id | rest] action = cRoute id 99 (cWrap rest action) +cWrap _ action = action +cBench iterations depth script = runC (cRoute 2 20 (cWrap depth (repeatB iterations))) script +cFilesystem path logPath = IO.io (executeIO path logPath (normalize (cHandlers 11 (publishB 7)) [] []) []) + +-- Scope controls, with independently specified traces in Python. B and C must +-- agree on routing semantics, not merely on the ordinary application workload. +bRestore script = run (handlers 11 (do seq + route 1 12 (operation 1 1 7) + operation 1 1 8)) script +cRestore script = runC (cHandlers 11 (do seq + cRoute 1 12 (operation 1 1 7) + operation 1 1 8)) script +-- The handler for 1 emits role 2 into its OUTER environment, not the caller's +-- inner role-2 override. This distinguishes lexical from dynamic dispatch. +bEmission script = run (route 2 11 (route 1 2 (route 2 12 (operation 1 1 7)))) script +cEmission script = runC (cRoute 2 11 (cRoute 1 2 (cRoute 2 12 (operation 1 1 7)))) script +bChain script = run (route 11 12 (route 1 11 (operation 1 1 7))) script +cChain script = runC (cRoute 11 12 (cRoute 1 11 (operation 1 1 7))) script +bNoReentry script = run (route 1 11 (route 1 1 (operation 1 1 7))) script +cNoReentry script = runC (cRoute 1 11 (cRoute 1 1 (operation 1 1 7))) script +-- Logging installed outside the lowering handler must NOT intercept it. +bOuterLog script = run (route 3 30 (logStore (handlers 11 (publishB 7)))) script +cOuterLog script = runC (cRoute 3 30 (cLogStore (cHandlers 11 (publishB 7)))) script +-- A physical request bypasses logical scopes but its continuation retains them. +bForward script = run (route 1 12 (do seq + service 11 1 7 + operation 1 1 8)) script +cForward script = runC (cRoute 1 12 (do seq + service 11 1 7 + operation 1 1 8)) script + +-- Application benchmark: distinct artifact IDs, all new in the supplied script. +-- Same loop and publisher functions; only dependency installation differs. +publishMany publisher [] = ret 0 +publishMany publisher [artifact | rest] = seq (publisher artifact) (_ : publishMany publisher rest) +publishMany _ _ = stop [94] +aPublishBench artifacts depth script = run (publishMany (publishA (services 11)) artifacts) script +bPublishBench artifacts depth script = run (handlers 11 (wrap depth (publishMany publishB artifacts))) script +cPublishBench artifacts depth script = runC (cHandlers 11 (cWrap depth (publishMany publishB artifacts))) script + +-- Returning an inert action does not capture the handler environment. The +-- action is later executed in the consumer's scope (unlike explicit pinning). +bEscaped script = run (handlers 11 (seq (route 1 12 (ret (operation 1 1 7))) (action : action))) script +cEscaped script = runC (cHandlers 11 (seq (cRoute 1 12 (ret (operation 1 1 7))) (action : action))) script + +-- D: routing and scripted execution fused in one machine. The environment, +-- handler functions, application trees, and physical request protocol are C's. +-- Linear lookup is deliberately unchanged to isolate the effect of fusion. +executeD action environment stack script reversed = (let + finish diagnostic = [(err diagnostic t) script (reverse reversed)] + resume value [] = [(ok value t) script (reverse reversed)] + resume value [[saved k] | rest] = executeD (k value) saved rest script reversed + resume _ _ = finish [94] + consume event k [[expected reply] | rest] = (lazyBool + (_ : executeD (k reply) environment stack rest (pair event reversed)) + (_ : [(err [92 event expected] t) script (reverse (pair event reversed))]) + (equal? event expected)) + consume event _ _ = [(err [93 event] t) script (reverse (pair event reversed))] + physicalD event{[11 op arg]} k = consume event k script + physicalD event{[12 op arg]} k = consume event k script + physicalD event{[20 op arg]} k = consume event k script + physicalD event{[30 op arg]} k = consume event k script + physicalD event _ = finish [95 event] + dispatch event{[instance op arg]} k = (case findHandler instance environment of + (handler, outer) = executeD (handler op arg) outer (pair [environment k] stack) script reversed + _ = physicalD event k) + dispatch _ _ = finish [94] + in case action of + (100, value) = resume value stack + (101, (first, k)) = executeD first environment (pair [environment k] stack) script reversed + (102, (event, k)) = dispatch event k + (103, diagnostic) = finish diagnostic + (104, (entry, body)) = executeD body (pair entry environment) (pair [environment ret] stack) script reversed + _ = finish [94]) +runD action script = executeD action [] [] script [] + +-- Explicit source state belongs to this sequential test interpreter, not a +-- mutable native slot. Every adapter returns [reply nextState]. No child-task +-- sharing or resource guarantees are implied by this testing accumulator. +executeDStateIO adapter state action environment stack = let + finish reply = IO.pure [reply state] + resume value [] = finish (ok value t) + resume value [[saved k] | rest] = executeDStateIO adapter state (k value) saved rest + resume _ _ = finish (err [94] t) + dispatch event{[instance op arg]} k = case findHandler instance environment of + (handler, outer) = executeDStateIO adapter state (handler op arg) outer (pair [environment k] stack) + _ = IO.bind (adapter state event) (result : case result of + [reply nextState] = executeDStateIO adapter nextState (k reply) environment stack + _ = IO.fail "Invalid test adapter state reply") + dispatch _ _ = finish (err [94] t) + in case action of + (100, value) = resume value stack + (101, (first, k)) = executeDStateIO adapter state first environment (pair [environment k] stack) + (102, (event, k)) = dispatch event k + (103, diagnostic) = finish (err diagnostic t) + (104, (entry, body)) = executeDStateIO adapter state body (pair entry environment) (pair [environment ret] stack) + _ = finish (err [94] t) +executeDIO adapter action environment stack = IO.bind + (executeDStateIO (state event : IO.bind (adapter event) (reply : IO.pure [reply state])) + t action environment stack) + (result : case result of + [reply _] = IO.pure reply + _ = IO.fail "Invalid test interpreter state reply") + +dFilesystem path logPath = IO.io (executeDIO (physical path logPath) (cHandlers 11 (publishB 7)) [] []) + +-- D entrypoints differ only in the chosen interpreter. +dPublish script = runD (cHandlers 11 (publishB 7)) script +dLogged script = runD (cHandlers 11 (cLogStore (publishB 7))) script +dCopy script = runD (cRoute 5 12 (cRoute 4 11 (copyB 7))) script +dTwice script = runD (cHandlers 11 (twiceB publishB)) script +dOverride script = runD (cHandlers 11 (cRoute 1 12 (publishB 7))) script +dPinned script = (let + publisher artifact = cRoute 1 11 (publishB artifact) + in runD (cHandlers 11 (cRoute 1 12 (publisher 7))) script) +dMissing script = runD (cRoute 3 30 (cRoute 2 20 (publishB 7))) script +dBench iterations depth script = runD (cRoute 2 20 (cWrap depth (repeatB iterations))) script +dRestore script = runD (cHandlers 11 (do seq + cRoute 1 12 (operation 1 1 7) + operation 1 1 8)) script +dEmission script = runD (cRoute 2 11 (cRoute 1 2 (cRoute 2 12 (operation 1 1 7)))) script +dChain script = runD (cRoute 11 12 (cRoute 1 11 (operation 1 1 7))) script +dNoReentry script = runD (cRoute 1 11 (cRoute 1 1 (operation 1 1 7))) script +dOuterLog script = runD (cRoute 3 30 (cLogStore (cHandlers 11 (publishB 7)))) script +dForward script = runD (cRoute 1 12 (do seq + service 11 1 7 + operation 1 1 8)) script +dPublishBench artifacts depth script = runD (cHandlers 11 (cWrap depth (publishMany publishB artifacts))) script +dEscaped script = runD (cHandlers 11 (seq (cRoute 1 12 (ret (operation 1 1 7))) (action : action))) script diff --git a/test/effect_server.py b/test/effect_server.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 +"""Opt-in artifact-server GET slice: captured services versus scoped interpreters. + +nix build .# +python3 test/effect_server.py result/bin/tricu --directory /tmp/effect-server --benchmark + +No listening socket or production source changes. Checks complete HTTP bytes, +logs, Result envelopes and effect traces against independent Python expectations. +Real filesystem runs use IO v2 and independent byte/error expectations. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import re +import statistics + +from effect_interfaces import tree +from http_headers import result +from source_order import Runner, end +from stable_bundle import byte_tree + +ROOT = Path(__file__).resolve().parent.parent +HASH = 'a' * 64 +TARGET = '/_arboricx/bundle/hash/' + HASH +BODY = b'\x00\xffportable-tree\n' + + +def encode(value): + if isinstance(value, str): + return byte_tree(value.encode()) + if isinstance(value, (list, tuple)): + return tree([encode(x) for x in value]) + return tree(value) + + +def ok(value): + return result(True, encode(value)) + + +def err(value): + return result(False, encode(value)) + + +def response(status, body, content_type='text/plain; charset=utf-8'): + if isinstance(body, str): + body = body.encode() + phrase = {200: 'OK', 400: 'Bad Request', 404: 'Not Found'}[status] + head = (f'HTTP/1.1 {status} {phrase}\r\nContent-Type: {content_type}\r\n' + f'Content-Length: {len(body)}\r\nConnection: close\r\n\r\n').encode() + return byte_tree(head + body) + + +def success(body=BODY): + return response(200, body, 'application/vnd.arboricx.bundle') + + +def event(context, hash=HASH, status=200): + return [context, [hash, status]] + + +def script(context, backend=11, hash=HASH, reply=None, status=200): + return [([backend, 6, hash], ok(byte_tree(BODY)) if reply is None else reply), + ([30, 7, event(context, hash, status)], ok(1))] + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + parser.add_argument('--benchmark', action='store_true') + args = parser.parse_args() + if os.geteuid() == 0: + parser.error("OS permission-denial controls require a non-root user") + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + source = ROOT / 'test/effect_interfaces/server.tri' + bundle = directory / 'server.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, + '--output', bundle, '--compile-timeout', '270'], seconds=300) + pin = runner.imported(bundle, 'import') + passed = [] + + def arguments(label, values): + result = [] + for index, value in enumerate(values): + path = directory / f'{label}-{index}.tree' + path.write_bytes(encode(value)) + result += ['--arg-tree-file', path] + return result + + def check(label, export, values, rows, value, *, remaining=(), trace=None): + actual = runner.invoke(label, pin, export, arguments(label, [*values, rows])) + wanted = encode([value, remaining, [row[0] for row in rows] if trace is None else trace]) + if actual != wanted: + (directory / (label+'-expected.tree')).write_bytes(wanted) + raise AssertionError(label) + passed.append(label) + + for variant in 'acd': + for mode, backend in [(0, 11), (1, 12), (2, 11)]: + context = f'request-{mode}' + check(f'{variant}-mode-{mode}', variant+'Request', [mode, context, 'GET', TARGET], + script(context, backend), ok(success())) + rows = script('shared', 11) + script('shared', 12, reply=ok(byte_tree(b'alternate'))) + check(variant+'-dual', variant+'Dual', ['shared', TARGET], rows, + ok([success(), success(b'alternate')])) + for label, reply, status, body in [ + ('missing', err('missing'), 404, 'Artifact unavailable\n'), + ('malformed', tree(0), 404, 'Invalid read result')]: + check(variant+'-'+label, variant+'Request', [0, 'r', 'GET', TARGET], + script('r', reply=reply, status=status), ok(response(status, body))) + # Production read protocol deliberately discards read Result remainder. + check(variant+'-read-remainder', variant+'Request', [0, 'r', 'GET', TARGET], + script('r', reply=result(True, byte_tree(BODY), byte_tree(b'unused'))), ok(success())) + for index, hash in enumerate(['../secret', 'A'*64, 'a'*63, 'a'*65]): + rows = [([30, 7, event('r', hash, 400)], ok(1))] + check(f'{variant}-invalid-{index}', variant+'Request', + [0, 'r', 'GET', '/_arboricx/bundle/hash/'+hash], rows, + ok(response(400, 'expected 64 lowercase hexadecimal hash characters'))) + for method, target in [('POST', TARGET), ('GET', '/other')]: + check(variant+'-not-found-'+method, variant+'Request', [0, 'r', method, target], [], + ok(response(404, 'Not found\n'))) + rows = script('r') + rows[-1] = (rows[-1][0], err('log unavailable')) + check(variant+'-log-failure', variant+'Request', [0, 'r', 'GET', TARGET], rows, + err('log unavailable')) + rows[-1] = (rows[-1][0], tree(42)) + check(variant+'-bad-log', variant+'Request', [0, 'r', 'GET', TARGET], rows, + err('Invalid completion-log response')) + # Repeated request contexts must not leak between sibling scopes. + check(variant+'-contexts', variant+'Requests', [['first', 'second'], TARGET], + script('first') + script('second'), ok([success(), success()])) + + root, alternate = directory / 'primary', directory / 'alternate' + for path, contents in [(root, BODY), (alternate, b'alternate')]: + object_path = path / 'objects' / HASH[:3] / (HASH+'.arboricx') + object_path.parent.mkdir(parents=True) + object_path.write_bytes(contents) + + def io_run(label, export, values): + output = runner.run(label, ['run', '--module', pin, '--name', export, + '--store', runner.store, '--io', '--format', 'ternary', '--profile', + *arguments(label, values)]) + text = next(re.match(r'^([012]+)', line)[1] for line in output.splitlines() + if re.match(r'^[012]+', line)) + raw = bytes(map(int, text)) + return raw[:end(raw)] + + for variant in 'acd': + for mode, expected in [(0, success()), (1, success(b'alternate')), (2, success())]: + label = f'{variant}-filesystem-{mode}' + actual = io_run(label, variant+'Filesystem', + [str(root), str(alternate), mode, 'real', TARGET]) + assert actual == encode([ok(expected), [event('real')]]), label + passed.append(label) + actual = io_run(variant+'-native-log-failure', variant+'FilesystemLogFailure', + [str(root), str(alternate), 0, 'failed', TARGET]) + assert actual == encode([err('log unavailable'), [event('failed')]]) + passed.append(variant+'-native-log-failure-retains-attempt') + # Independent current expectations replace execution of the frozen v1 + # route. Missing/OS-denied reads redact diagnostics; invalid hashes never + # reach the file adapter. Actual v2 registry integrity is tested separately. + for label, target, status, body in [ + ('success', TARGET, 200, BODY), + ('invalid', '/_arboricx/bundle/hash/../secret', 400, + 'expected 64 lowercase hexadecimal hash characters'), + ('missing', '/_arboricx/bundle/hash/'+'b'*64, 404, 'Artifact unavailable\n'), + ('denied', TARGET, 404, 'Artifact unavailable\n')]: + if label == 'denied': + root.chmod(0) + try: + actual = io_run(f'{variant}-filesystem-check-{label}', variant+'Filesystem', + [str(root), str(alternate), 0, 'real', target]) + finally: + root.chmod(0o700) + hash = target.removeprefix('/_arboricx/bundle/hash/') + expected = success() if status == 200 else response(status, body) + assert actual == encode([ok(expected), [event('real', hash, status)]]), label + passed.append(variant+'-filesystem-'+label) + assert not (directory / 'secret').exists() + assert (root / 'objects' / HASH[:3] / (HASH+'.arboricx')).read_bytes() == BODY + + benchmarks = [] + if args.benchmark: + for backend in ['scripted', 'filesystem']: + for count in [1, 20]: + contexts = list(range(count)) + rows = [row for context in contexts for row in script(context)] + samples = {variant: [] for variant in 'acd'} + for repeat, order in enumerate(['acd', 'cda', 'dac']): + for variant in order: + label = f'bench-{backend}-{variant}-{count}-{repeat}' + if backend == 'scripted': + check(label, variant+'Requests', [contexts, TARGET], rows, ok([success()]*count)) + else: + actual = io_run(label, variant+'RequestsIO', + [str(root), str(alternate), contexts, TARGET]) + assert actual == encode([ok([success()]*count), [event(context) for context in contexts]]), label + passed.append(label) + samples[variant].append(runner.records[-1]) + for variant, records in samples.items(): + benchmarks.append(dict(backend=backend, count=count, variant=variant, + seconds=statistics.median(r['invoke_seconds'] for r in records), + allocated_bytes=statistics.median(r['allocated_bytes'] for r in records))) + sources = [source, ROOT / 'test/effect_interfaces/workflow.tri', Path(__file__).resolve(), + ROOT / 'test/effect_interfaces/tricu.workspace', ROOT / 'lib/arboricx/server.tri', + ROOT / 'lib/http.tri', ROOT / 'lib/base.tri', ROOT / 'lib/io-v2.tri', ROOT / 'lib/io-v2/storage.tri', + ROOT / 'bootstrap/build-stable.arboricx'] + report = dict(passed=passed, benchmarks=benchmarks, host=str(args.tricu.resolve()), + bundle_sha256=hashlib.sha256(bundle.read_bytes()).hexdigest(), + inputs={str(path.relative_to(ROOT)): hashlib.sha256(path.read_bytes()).hexdigest() + for path in sources}, + limits=['GET request slice, not sockets or full server throughput', + 'completion logging added by experiment, not shipped behavior', + 'filesystem benchmarks use warm budgeted local reads and explicit source-state log capture', + 'all request responses fully rendered; scripted runs also render full traces, filesystem runs logs', + 'no route indexing; C/D use the same linear lookup']) + (directory / 'report.json').write_text(json.dumps(report, indent=2)+'\n') + print(json.dumps(report, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/test/executable_arguments.py b/test/executable_arguments.py @@ -149,15 +149,23 @@ class ExecutableArguments(unittest.TestCase): return b'\x02' + a + b path = self.root / 'io.tree' def action(tag, payload): - path.write_bytes(pair(byte_tree(b'tricuIO'), pair(natural(1), pair(natural(tag), payload)))) + path.write_bytes(pair(byte_tree(b'tricuIO'), pair(natural(2), pair(natural(tag), payload)))) options = ['--module', self.identity, '--name', 'id', '--arg-tree-file', str(path)] - action(10, byte_tree(b'program output\n')) - self.assertEqual(self.call('run', *options, '--io').stdout, 'program output\nt') - self.assertEqual(self.call('run', *options, '--io', '--no-result').stdout, 'program output\n') + # Authenticated artifact-only v2 output program; no source compiler in + # this transport test. Expected output remains an independent literal. + from bundle_dag import merge_bundles + program = self.root / 'output.arboricx' + merge_bundles(program, [(REPO / 'test/fixtures/support-library.arboricx', + [('demos_interactionTrees_yield.main', 'main')])], prune_unreachable=True) + output_module = self.imported(program, indexed=True) + options = ['--module', output_module, '--name', 'main'] + expected_output = 'A says 1\nA says 2\nA says 3\nB says 1\nB says 2\nB says 3\nFinished: doneA doneB\n' + self.assertEqual(self.call('run', *options, '--io').stdout, expected_output + 't') + self.assertEqual(self.call('run', *options, '--io', '--no-result').stdout, expected_output) for full in [False, True]: flags = ['--profile'] + (['--profile-full-result'] if full else []) profiled = self.call('run', *options, '--io', '--no-result', *flags) - self.assertEqual(profiled.stdout, 'program output\n') + self.assertEqual(profiled.stdout, expected_output) labels = [json.loads(line)['profile'] for line in profiled.stderr.splitlines() if line.startswith('{')] expected = ['load-begin', 'load-end', 'invoke-begin-1', 'invoke-end-1'] @@ -169,10 +177,11 @@ class ExecutableArguments(unittest.TestCase): reply = self.call('run', *options, *flags, success=False) self.assertEqual(reply.stdout, '') # reject before doing IO self.assertIn('--no-result requires --io', reply.stderr) + options = ['--module', self.identity, '--name', 'id', '--arg-tree-file', str(path)] action(2, byte_tree(b'explicit failure')) failed = self.call('run', *options, '--io', '--no-result', success=False) self.assertEqual(failed.stdout, '') - self.assertIn('explicit failure', failed.stderr) + self.assertIn('IO v2 task failed', failed.stderr) class ArtifactSafety(unittest.TestCase): """Independent wire fixtures and real CLI paths, all inside temporary stores.""" diff --git a/test/fixture_sources.py b/test/fixture_sources.py @@ -6,6 +6,7 @@ existing fixture metadata; it cannot bless an edited source as an old artifact. """ import argparse import hashlib +import json from pathlib import Path, PurePosixPath ROOT = Path(__file__).resolve().parent.parent @@ -23,7 +24,8 @@ def source_rows(fixtures): versions = {'tricu.' + cohort + '-fixture.v1'} if cohort == 'support-library': versions.update(('tricu.support-library-fixture.v2', - 'tricu.support-library-fixture.v3')) + 'tricu.support-library-fixture.v3', 'tricu.support-library-fixture.v4', + 'tricu.support-library-fixture.v5')) if not lines or lines[0] not in versions: raise ValueError('unsupported fixture metadata: ' + cohort) bundle = (fixtures / (cohort + '.arboricx')).read_bytes() @@ -87,6 +89,38 @@ def capture(fixtures, source_root): return rows +def materialize(fixtures, destination): + """Create a reference-only source tree, never bless current working bytes. + + Module bytes come exclusively from authenticated fixture snapshots. The map + is reconstructed from recorded membership and those sources' native headers; + this is not a claim that the result is an original Git checkout or accepted + compiler generation. Validate everything before creating the destination. + """ + from native_header import native_header + rows = verify(fixtures) + payloads = {path: snapshot_bytes(fixtures, pin) for _, _, _, path, pin in rows} + mapping = ['arboricx.source-map.v1'] + compiler_aliases = {alias for cohort, _, alias, _, _ in rows if cohort == 'compiler-library'} + for cohort, legacy, alias, path, _ in rows: + if cohort == 'compiler-library': + dependencies, _ = native_header(payloads[path].decode('utf-8')) + if not set(dependencies) <= compiler_aliases: + raise ValueError('reference compiler dependency outside cohort') + mapping.append('\t'.join(['native', alias, legacy, path, ','.join(dependencies) or '-'])) + destination.mkdir(parents=True, exist_ok=False) + for path, payload in payloads.items(): + target = destination / path + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(payload) + (destination / 'bootstrap').mkdir(exist_ok=True) + (destination / 'bootstrap/compiler-source-map.v1').write_text('\n'.join(mapping)+'\n') + (destination / 'reference-sources.json').write_text(json.dumps(dict( + schema='tricu.fixture-reference-sources.v1', scope='reference-only-not-working-tree', + sources={path: pin for _, _, _, path, pin in rows}), indent=2)+'\n') + return rows + + def current_mismatches(fixtures, source_root): rows = verify(fixtures) changed = [] @@ -116,13 +150,18 @@ def current_mismatches(fixtures, source_root): def main(): parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('mode', choices=('verify', 'capture', 'current')) + parser.add_argument('mode', choices=('verify', 'capture', 'current', 'materialize')) parser.add_argument('--fixtures', type=Path, default=ROOT / 'test/fixtures') parser.add_argument('--source-root', type=Path, help='required for capture; current defaults to the checkout') args = parser.parse_args() try: - if args.mode == 'capture': + if args.mode == 'materialize': + if args.source_root is None: + parser.error('materialize requires a new --source-root directory') + rows = materialize(args.fixtures, args.source_root) + print(f'Materialized {len(rows)} authenticated reference sources; working-tree freshness NOT checked.') + elif args.mode == 'capture': if args.source_root is None: parser.error('capture requires an explicit --source-root') rows = capture(args.fixtures, args.source_root) diff --git a/test/fixtures/compiler-library.arboricx b/test/fixtures/compiler-library.arboricx Binary files differ. diff --git a/test/fixtures/compiler-library.v1 b/test/fixtures/compiler-library.v1 @@ -1,17 +1,17 @@ tricu.compiler-library-fixture.v1 module source.index source_index lib/source/index.tri d3db01d9854f2371c0e1d36a99de6b21596cc99306a3131f3f753e037a8efbce -module source.map source_map lib/source/map.tri 72638c3d2ec6f47598cba67c730249e8381c67cd5ba6437c4d24711e206afced +module source.map source_map lib/source/map.tri efa6213780eb641f7ec9ae6006d105a8110232ac06b455c335a5552324e58591 module source.unit source_unit lib/source/unit.tri 0883c40a3f5257574290b9598dca64073f52f9af126adf14c76f6974f26dfff0 module prelude prelude lib/prelude.tri 0c26a0142d15cc2a11a4317264491c797944e76ea8afa07a1efef240a70f4289 -module io io lib/io.tri c4ec206590d25d581b9a96f242c22936d8d0b2e9903331ce50b30ad7de254bd6 -module source.object source_object lib/source/object.tri 79cd7f19d3de74be38bac823d8674f6b74327a4ae12bb0cf405119b51fbd9ea5 -module source.module source_module lib/source/module.tri fb609a75a12659d2a05a63681c0cf96ef606045b4aebe044eac077c8cd23fbf2 +module compiler.io compiler_io lib/source/io-v2.tri 575f7c3c8834bd2780f444a0078e72366fe2f0de6c4e00b4c3722e4da8bb9693 +module source.object source_object lib/source/object.tri 0f98a415e9bcdd223cd27eba9b501e91fe6b3778a2d5ea59218815081ab3d767 +module source.module source_module lib/source/module.tri 49be76ef3607cedcafd4e6a599b90a7f0601b5d030e1431011e879342c9b0e91 module source.resolve source_resolve lib/source/resolve.tri 7f5f726392cbbdaa9f30aef09952a31eda14f034e04112a210f60f10542f6add -module source.build source_build lib/source/build.tri 5c24cc853df42e433916b1528fc8cd56334bd393606f54e7b12efb6a7069be0d +module source.build source_build lib/source/build.tri 3bfd3a8ea35a6316e1ef9cc53ac2418a9dbd6115c69d129a1b2de81c87877e7a module parser parser lib/parser.tri 31e8c679e086bdf9b68e05a4bb71fd413e66e53fd93b2e4b4a806930020b36e4 module source.order source_order lib/source/order.tri ec97f34c89ead701af75053ea340ead1bb0fda13d83b474ca760c3aa359970fa module source.compile source_compile lib/source/compile.tri 3764484ae723d4311cf04b769251badf7f7a190d9480389303847be1ce665bb9 -module source.program source_program lib/source/program.tri 7a99fcc8da4c502ff67bc723b3ef317409a84f608c607cfa99331dd30245845a +module source.program source_program lib/source/program.tri 297b2195345b9bf4c49857aacd48545604157a9a0f7b3cb5d8ec7759e2e41e83 module source.ast source_ast lib/source/ast.tri b6d05cace61c04032ea39a858dedc7482988dbb3ed47581a9a3239094263d314 module base base lib/base.tri 7ba2537cc4ec0571a7aa8a02557551245ca04f5bc08d9f9c1eb4d2606ec67b37 module bytes bytes lib/bytes.tri 6d057be42bc16788a34ce80cca7cb97251cf7dbbae8fd57100a31206ab3dd2a0 @@ -19,12 +19,15 @@ module conversions conversions lib/conversions.tri fd93454ad6cf7b0d906f367bed9a6 module source.lexer source_lexer lib/source/lexer.tri 94ee3a70dbed59015891f7f5c2ef588d64f1a885218edc35eed98d96d85fd11b module source.parser source_parser lib/source/parser.tri 23c8142e9629f029ae22b7c0b377bbfb69e4f9ecc5ce452094c47d063a49c9d6 module source.core source_core lib/source/core.tri b132ca49c22098c8a5d638517d701ad8141028d9389594ac2134240d240a2637 -module source.lock source_lock lib/source/lock.tri 31ffd58166bf861e82da0a90594cfb9c6c12e19baa51d5166fe0e4414d24af94 -module source.bundle source_bundle lib/source/bundle.tri 3fedb41000f22609b0bdab8fb222175d1347202eceabe7bdb983bc28b3258627 -module graph graph lib/graph.tri bc8622d006e357b1045f2cb3c775a8a58045610ee6f4a617858762d37947b5ec +module source.lock source_lock lib/source/lock.tri 52c3f1b1f0d29975e3932850451dc8e3cee93ac475dac869d8e9eba7838bd830 +module source.bundle source_bundle lib/source/bundle.tri 2373335a22f1a6c7d00c7dd612ec8498deb8502eaf48bd6e8aa9f9407a1bfc13 +module graph graph lib/graph.tri e98fc7955feb673b86ac9176a211136493d6247b011cb80995fb53464028f976 module lexer lexer lib/lexer.tri 946bbf9797a5cf722c6e85f36ac36b1ec2dd3d1f3b2650187c182dff12162bf7 module source.pattern source_pattern lib/source/pattern.tri b27f1eac18cbe8ad80cdd91f9f546b2d2a782b6cc5c5676232491b95751abe5a module source.equations source_equations lib/source/equations.tri 825f175f5c6dd56e103b94e924283acf5913cbc4554fb42fc07a034f460d2076 -module source.stable source_stable lib/source/stable.tri b407662fec9436bfb7cc06eed2c14961174c6fa19863413ffc74d58ef72c5431 +module source.stable source_stable lib/source/stable.tri 0d38a5a7511d4a08f648dfa28beaa312f9139bf9639f7779f2f83ac45134f439 module math.native native_math lib/math/native.tri e9d0b87564d13695bef695c552e9707710d9147877f9d9dc5282c7335664cc57 -bundle aa29656c146d2fc239d27da869498670c401cf344516314d06b7e5634c02d6f1 +module io.v2 io_v2 lib/io-v2.tri 8b2e1cd6ef5acc8454a690875d2e8415869892f84c3fb5198c18fc8e665d0f32 +module io.v2.storage io_v2_storage lib/io-v2/storage.tri 64e2248dbd4a05fc78b9d92cd775bb111951af95e384b91000e7f7aa32410013 +module io.v2.text io_v2_text lib/io-v2/text.tri 1ca2682e6a35d949196ff3b1d2aabed47af57e7ef43b8e32b2b7b19ca004f64f +bundle 11e9432733bb2b4579b65c4479b1b7dda5b5a56a4fd837fdd88f9a6ebc9093dd diff --git a/test/fixtures/reference-source/060a440835999d443ef767ea5d4e9efe616a7186e1bb04947d6f9f80a21b0a4b.tri b/test/fixtures/reference-source/060a440835999d443ef767ea5d4e9efe616a7186e1bb04947d6f9f80a21b0a4b.tri @@ -0,0 +1,16 @@ +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export run +!export shout +safeRead path = IO.bind (IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + Text.readText reader 1048576)) (reply : case reply of + ((t t), (text, t)) = IO.pure text + (t, (_, t)) = IO.pure "missing" + _ = IO.fail "Invalid file reply") +shout path = IO.bind (safeRead path) (text : IO.pure (B.append text "!!!")) +main = IO.io (IO.bind (shout "demos/interactionTrees/greet.tri") (text : + IO.require IO.stdout (writer : IO.require (Text.writeText writer (B.append text "\n")) IO.pure))) +run path = IO.io (shout path) diff --git a/test/fixtures/reference-source/06944982576cd127727cf48915b1842237308ac7e1b517b3b21a8846fd577dc3.tri b/test/fixtures/reference-source/06944982576cd127727cf48915b1842237308ac7e1b517b3b21a8846fd577dc3.tri @@ -0,0 +1,167 @@ +!import base as B +!import io as IO +!import http as HTTP +!import socket as Socket +!import conversions as Convert +!import arboricx_server as Store +!import handlers as H +!import io_v2 as V2 +!import io_v2_storage as Storage +!import handler_log as Log +!import handler_artifacts as Artifacts +!export request +!export packageLookup +!export stdoutLog +!export router +!export serve +!export main +!export packageLookupV2 +!export stdoutLogV2 +!export writeLogV2 +!export routerV2 + +-- HTTP policy is separate from storage, logging, and the socket lifecycle. +-- Outcomes carry a status beside the complete response so logging does not +-- parse response bytes. Only completed application routes emit access events; +-- parser/protocol rejections remain owned by HTTP.httpHandlerIO. +reply status bytes = H.pure [status bytes] +error status message = reply status (HTTP.errorResponse status message) + +load digest = do H.bind + result <- Artifacts.load digest + case result of + ((t t), (bytes, t)) = reply 200 (HTTP.response 200 Store.bundleContentType bytes) + (t, (["invalid-digest" message], t)) = error 400 message + _ = error 404 "Artifact unavailable\n" + +publish bytes = do H.bind + result <- Artifacts.publish bytes + case result of + ((t t), (digest, t)) = reply 201 (HTTP.createdResponse digest) + _ = error 400 "Upload failed\n" + +-- Package storage is an explicit captured service: its identity is stable for +-- this server. Contextual artifact overrides need no package-path authority. +-- The HTTP parser supplies canonical target bytes, as in the original server. +packageLookup root target = case Store.packageTarget target of + (t (name, version)) = H.liftIO (IO.readFile + (Store.pathJoin (Store.pathJoin (Store.pathJoin root "packages") name) version)) + _ = H.pure (B.err ["invalid-package" "Invalid package name/version"] t) + +package lookup target = do H.bind + result <- lookup target + case result of + ((t t), (bytes, t)) = reply 200 (HTTP.response 200 "application/vnd.arboricx.package-release" bytes) + (t, (["invalid-package" message], t)) = error 400 message + _ = error 404 "Package release not found" + +get lookup target = case Store.stripPrefix Store.packagePrefix target of + (t suffix) = package lookup suffix + _ = case Store.stripPrefix Store.bundleHashPrefix target of + (t digest) = load digest + _ = reply 404 HTTP.notFoundResponse + +route _ "GET" "/" _ _ = reply 200 (HTTP.htmlResponse Store.htmlLandingPage) +route _ "GET" "/_arboricx/health" _ _ = reply 200 (HTTP.okResponse "OK\n") +route lookup "GET" target _ _ = get lookup target +route _ "POST" "/_arboricx/bundle" _ body = publish body +route _ _ _ _ _ = reply 404 HTTP.notFoundResponse + +request lookup method target headers body = H.label "registry-request" <| do H.bind + outcome <- route lookup method target headers body + case outcome of + [status response] = do H.bind + logged <- Log.info status + case logged of + ((t t), (t, t)) = H.pure response + (t, (message, t)) = H.fail "application-failure" "logging" "emit" message + _ = H.fail "application-failure" "logging" "emit" "Invalid logger reply" + _ = H.fail "application-failure" "registry" "route" "Invalid route outcome" + +-- Do not interpolate untrusted target strings into a line-oriented log. +-- Context remains structured for replacement sinks. No shared IO.get/put state +-- is used: concurrent requests each install their own lexical context. +stdoutLog ["info" fields status] = case B.checkContract B.nat? status of + ((t t), _) = H.liftIO <| do IO.bind + written <- IO.putStr (B.append "registry status=" (B.append (Convert.showNumber status) "\n")) + case written of + t = IO.pure (B.ok t t) + (t, (message, t)) = IO.pure (B.err message t) + _ = IO.pure (B.err "Invalid host logging reply" t) + _ = H.pure (B.err "Invalid access-log status" t) +stdoutLog _ = H.pure (B.err "Invalid access-log event" t) + +-- Interpretation failures (including failed logging) become redacted HTTP 500s. +-- They do not abort the listener or bypass ordinary respond-and-close behavior. +router root sink method target headers body = do IO.bind + result <- H.runIO (request (packageLookup root) method target headers body + |> Log.withContext [["method" method] ["target" target]] + |> Artifacts.withFilesystem root + |> Log.withSink sink) + case result of + ((t t), (response, t)) = IO.pure response + _ = do IO.bind + IO.putStr "registry request failed\n" + IO.pure (HTTP.errorResponse 500 "Internal Server Error\n") + +-- Same ordinary Result cleanup/scheduling policy as Store.arboricxServer. +-- No stronger exception, cancellation, or shutdown guarantees are implied. +serve root sink address port = let + failed message = IO.pure (B.err message t) + opened server = let + rejected message = IO.finally (failed message) (Socket.closeSocket_ server) + ready _ = Socket.serveForever server (HTTP.httpHandlerIO (router root sink)) + in IO.onResult_ (do IO.onOk_ + Socket.bindSocket server address port + Socket.listen server 128) + rejected ready + in IO.onResult_ Socket.socket failed opened + +-- Startup installs stable storage identity and an explicit logging sink. +-- Initialization/bind/listen failures still exit nonzero at the host boundary. +main = IO.io (IO.onResult_ + (do IO.onOk_ + Store.ensureStore "./store" + serve "./store" stdoutLog "0.0.0.0" 8080) + IO.abortIO + IO.pure) + +-- The captured package service remains independent of contextual artifact routes. +packageLookupV2 root limit target = case Store.packageTarget target of + (t (name, version)) = H.liftIO (Storage.readBytes + (Store.pathJoin (Store.pathJoin (Store.pathJoin root "packages") name) version) limit) + _ = H.pure (B.err ["invalid-package" "Invalid package name/version"] t) + +-- Status-only ASCII logging. Ordinary write failures remain Results; publication +-- precedes logging and is never rolled back when the logger fails. +writeLogV2 bytes = do V2.onOk + writer <- V2.stdout + V2.writeAll writer bytes + V2.pure (B.ok t t) +stdoutLogV2 ["info" fields status] = case B.checkContract B.nat? status of + ((t t), _) = H.liftIO (writeLogV2 + (B.append "registry status=" (B.append (Convert.showNumber status) "\n"))) + _ = H.pure (B.err "Invalid access-log status" t) +stdoutLogV2 _ = H.pure (B.err "Invalid access-log event" t) + +-- A supervised child contains request interpretation/logger failure without +-- weakening native fail-fast semantics or retaining completed task tickets. +-- Cancellation remains cancellation; a failure becomes a redacted HTTP response. +-- Socket ownership and framing are the listener's responsibility, not this router. +routerV2 root limit sink method target headers body = V2.scope (_ : do V2.require + spawned <- V2.spawn 1 1 [] (_ : H.runIOV2 + (request (packageLookupV2 root limit) method target headers body + |> Log.withContext [["method" method] ["target" target]] + |> Artifacts.withFilesystemV2 root limit + |> Log.withSink sink)) + case spawned of + [task ticket] = do V2.require + outcome <- V2.join ticket + case outcome of + ["completed" response] = V2.pure response + ["failed" _ _] = do V2.bind + writeLogV2 "registry request failed\n" + V2.pure (HTTP.errorResponse 500 "Internal Server Error\n") + ["cancelled" reason _] = V2.cancelled reason + _ = V2.fail "Invalid registry task outcome" + _ = V2.fail "Invalid registry spawn reply") diff --git a/test/fixtures/reference-source/06e81c8b62b8f26ba20faa3280b381ddd55ecf246876a21b028175bbeb87f896.tri b/test/fixtures/reference-source/06e81c8b62b8f26ba20faa3280b381ddd55ecf246876a21b028175bbeb87f896.tri @@ -0,0 +1,41 @@ +!import base as B +!import handlers as H +!export emit +!export info +!export warn +!export withContext +!export withSink +!export capture +!export discard + +-- Events are [level ordered-context-fields payload]. Payload/fields are trees, +-- not strings. The service has no implicit formatting or serialization policy. +eventC value{[level fields payload]} rest = case B.listOf B.anyC fields rest of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "log context must be a proper list" rest +eventC _ rest = B.contractErr "expected [level context payload]" rest + +-- Log sinks return ordinary Result unit; errors remain application-visible. +replyC value{((t t), (t, t))} rest = B.contractOk value rest +replyC value{(t, (message, t))} rest = B.contractOk value rest +replyC _ rest = B.contractErr "expected log Result with empty remainder" rest + +emit level payload = H.checkedRequest "logging" "emit" eventC replyC [level [] payload] +info payload = emit "info" payload +warn payload = emit "warn" payload + +contextHandler fields "emit" event{[level inner payload]} = + H.forward "logging" "emit" [level (B.append fields inner) payload] +contextHandler _ operation payload = H.forward "logging" operation payload +withContext fields action = case B.checkContract (B.listOf B.anyC) fields of + ((t t), (checked, t)) = H.handle "logging" (contextHandler checked) action + _ = H.fail "invalid-request" "logging" "context" "context must be a proper list" + +sinkHandler sink "emit" event = case B.checkContract eventC event of + ((t t), (checked, t)) = sink checked + (t, (message, _)) = H.fail "invalid-request" "logging" "emit" message + _ = H.fail "invalid-request-contract" "logging" "emit" event +sinkHandler _ operation payload = H.forward "logging" operation payload +withSink sink action = H.handle "logging" (sinkHandler sink) action +capture action = H.collect "logging" "emit" eventC (B.ok t t) action +discard action = withSink (_ : H.pure (B.ok t t)) action diff --git a/test/fixtures/reference-source/0d38a5a7511d4a08f648dfa28beaa312f9139bf9639f7779f2f83ac45134f439.tri b/test/fixtures/reference-source/0d38a5a7511d4a08f648dfa28beaa312f9139bf9639f7779f2f83ac45134f439.tri @@ -0,0 +1,194 @@ +!import prelude * +!import parser * +!import source_core * +!import source_ast * +!import source_compile * +!import source_resolve * +!import source_program * +!import source_order * +!export stableTermApply +!export stableApplication +!export stableBracket +!export stableEliminate +!export stableI +!export stableSelf +!export stableWait +!export stableWaitSelf +!export stableFix +!export stableBind +!export stableCompileCore +!export sourceDefinitionSelfRecursive +!export sourceRecursiveFunction +!export sourceRecursiveBody +!export sourceStablePrepare +!export orderStableProgram +!export sourceCompileStableDefinition +!export sourceCompileStableDefinitionWith +!export orderStableProgramWith + +-- Stable native abstraction backend. Selected explicitly by the unit/build API. +-- Barry Jay, Reflective Programs in Tree Calculus, sections 4.2, 4.4-4.5. + +-- Fold only constructor applications whose operands are literal trees. Leaf +-- and Stem functions construct Stem/Fork without reducing a redex. Never fold +-- a Fork function here: that would reintroduce the closed-body hazard. Packing +-- these generated trees avoids re-abstracting their syntax at every binder. +stableTermApply function argument location = (lazyBool + (_ : triage + (sourceTermLiteral (t (sourceTermPayload argument)) location) + (child : sourceTermLiteral (t child (sourceTermPayload argument)) location) + (_ _ : sourceTermApply function argument location) + (sourceTermPayload function)) + (_ : sourceTermApply function argument location) + (and? (equal? (sourceTermKind function) 1) (equal? (sourceTermKind argument) 1))) + +-- tricu's stem rule is t (t a) b c = a c (b c), unlike the book's +-- reversed b c (a c). Thus [x](u v) = t (t ([x]u)) ([x]v). +-- The annotated path below may prune independent constructor-only subtrees. +-- Literal payloads are represented trees. +stableApplication left right location = (stableTermApply + (stableTermApply (sourceTermLiteral t location) + (stableTermApply (sourceTermLiteral t location) left location) location) + right location) + +-- Internal annotated residual: ((shape, minimum-free-index+1), ordinary node). +-- Its application children carry the same annotations. Metadata is maintained +-- incrementally as nodes are built. A zero minimum denotes a closed subtree; +-- one means the binder being eliminated occurs. Any larger minimum shifts down by one when +-- removing an unused binder. Literal payloads are already represented trees. +-- Shape certificates permit only Leaf/Stem construction with certified +-- arguments. A variable has an opaque shape. +-- Fork applications, unknown calls and constructors containing computations +-- remain residual; neither binder independence alone nor eta is sufficient. +-- The ordinary residual API is restored at the public boundary. Keep both +-- shape and dependency summaries out of executable trees and diagnostics. +-- Zero bound means closed. Shapes: 0 computation, 1 Leaf, 2 Stem, +-- 3 Fork, 4 opaque variable. Application children carry annotations too. +stableCachedNode kind payload location shape bound = + pair (pair shape bound) (sourceTermNode kind payload location) +stableCachedShape node = fst (fst node) +stableCachedBound node = snd (fst node) +stableCachedKind node = sourceTermKind (snd node) +stableCachedPayload node = sourceTermPayload (snd node) +stableCachedLocation node = sourceTermLocation (snd node) +stableCachedLiteral tree location = stableCachedNode 1 tree location (triage 1 (_ : 2) (_ _ : 3) tree) 0 +stableCachedVariable index location = stableCachedNode 2 index location 4 (succ index) +stableCachedMin left right = lazyBool (_ : right) + (_ : lazyBool (_ : left) + (_ : lazyBool (_ : left) (_ : right) (lte_? left right)) + (isZero_? right)) (isZero_? left) +stableCachedRawApply function argument location = (let shape = stableCachedShape function in + stableCachedNode 3 (pair function argument) location + (lazyBool + (_ : lazyBool (_ : 0) (_ : succ shape) (isZero_? (stableCachedShape argument))) + (_ : 0) (or? (equal? shape 1) (equal? shape 2))) + (stableCachedMin (stableCachedBound function) (stableCachedBound argument))) +stableCachedApply function argument location = lazyBool + (_ : triage + (stableCachedLiteral (t (stableCachedPayload argument)) location) + (child : stableCachedLiteral (t child (stableCachedPayload argument)) location) + (_ _ : stableCachedRawApply function argument location) + (stableCachedPayload function)) + (_ : stableCachedRawApply function argument location) + (and? (equal? (stableCachedKind function) 1) (equal? (stableCachedKind argument) 1)) +stableCachedApplication left right location = stableCachedApply + (stableCachedApply (stableCachedLiteral t location) (stableCachedApply (stableCachedLiteral t location) left location) location) + right location +-- Precondition: the current binder is absent (minimum is not one). +-- Closed subtrees need neither shifting nor copying. Remaining metadata can +-- be shifted directly: no shape or dependency traversal is needed here. +stableCachedLower term = lazyBool + (_ : term) + (_ : case stableCachedKind term of + 2 = stableCachedVariable (pred_ (stableCachedPayload term)) (stableCachedLocation term) + 3 = stableCachedNode 3 (pair (stableCachedLower (fst (stableCachedPayload term))) (stableCachedLower (snd (stableCachedPayload term)))) + (stableCachedLocation term) (stableCachedShape term) (pred_ (stableCachedBound term)) + _ = term) + (isZero_? (stableCachedBound term)) +stableCachedBracket term = case stableCachedKind term of + 1 = stableCachedApply (stableCachedLiteral sourceCombK (stableCachedLocation term)) term (stableCachedLocation term) + 2 = lazyBool + (_ : stableCachedLiteral sourceCombI (stableCachedLocation term)) + (_ : stableCachedApply (stableCachedLiteral sourceCombK (stableCachedLocation term)) + (stableCachedVariable (pred_ (stableCachedPayload term)) (stableCachedLocation term)) (stableCachedLocation term)) + (isZero_? (stableCachedPayload term)) + 3 = lazyBool + (_ : stableCachedApply (stableCachedLiteral sourceCombK (stableCachedLocation term)) (stableCachedLower term) (stableCachedLocation term)) + (_ : stableCachedApplication (stableCachedBracket (fst (stableCachedPayload term))) + (stableCachedBracket (snd (stableCachedPayload term))) (stableCachedLocation term)) + (and? (not? (equal? (stableCachedBound term) 1)) (not? (isZero_? (stableCachedShape term)))) + _ = term +stableCachedFromPlain term = case sourceTermKind term of + 1 = stableCachedLiteral (sourceTermPayload term) (sourceTermLocation term) + 2 = stableCachedVariable (sourceTermPayload term) (sourceTermLocation term) + 3 = stableCachedRawApply (stableCachedFromPlain (fst (sourceTermPayload term))) + (stableCachedFromPlain (snd (sourceTermPayload term))) (sourceTermLocation term) + _ = stableCachedNode (sourceTermKind term) (sourceTermPayload term) (sourceTermLocation term) 0 0 +stableCachedToPlain term = case stableCachedKind term of + 3 = sourceTermApply (stableCachedToPlain (fst (stableCachedPayload term))) + (stableCachedToPlain (snd (stableCachedPayload term))) (stableCachedLocation term) + _ = snd term +stableCachedEliminate node = case sourceCoreKind node of + 1 = stableCachedLiteral t (sourceCoreStart node) + 2 = stableCachedVariable (sourceCorePayload node) (sourceCoreStart node) + 3 = stableCachedApply (stableCachedEliminate (fst (sourceCorePayload node))) + (stableCachedEliminate (snd (sourceCorePayload node))) (sourceCoreStart node) + 4 = stableCachedBracket (stableCachedEliminate (sourceCorePayload node)) + 5 = stableCachedLiteral (sourceCorePayload node) (sourceCoreStart node) + 6 = stableCachedLiteral (sourceCorePayload node) (sourceCoreStart node) + 7 = stableCachedLiteral (sourceCorePayload node) (sourceCoreStart node) + 8 = (let rec elements pending = (lazyList + (_ : stableCachedLiteral t (sourceCoreStart node)) + (head rest : stableCachedRawApply + (stableCachedRawApply (stableCachedLiteral t (sourceCoreStart node)) (stableCachedEliminate head) (sourceCoreStart node)) + (elements rest) (sourceCoreStart node)) pending) + in elements (sourceCorePayload node)) + _ = stableCachedNode 4 node (sourceCoreStart node) 0 0 +stableBracket term = stableCachedToPlain (stableCachedBracket (stableCachedFromPlain term)) +stableEliminate node = stableCachedToPlain (stableCachedEliminate node) + +-- Native wait and Z, with self-first workers: Z f x = f (Z f) x. +-- All applications below construct trees out of already-normal arguments. +stableI = sourceCoreFixI +stableSelf = sourceCoreFixSelf +stableWait = sourceCoreFixWait +stableWaitSelf = sourceCoreFixWaitSelf +stableFix = sourceCoreFix + +stableBind result next = lazyResult (problem rest : err problem rest) (value unused : next value) result +stableCompileCore core = sourceRunTerm (stableEliminate core) + +-- Determine self-reference with the ordinary lexical free-name traversal. A +-- parameter/local/pattern capture of the same spelling is not recursion. +sourceDefinitionSelfRecursive definition = (lazyMaybe (_ : false) (_ : true) + (sourceFindBinder (map sourceAstPayload (sourceFreeNames (sourceDefinitionBody definition))) + (sourceDefinitionName definition))) + +sourceRecursiveFunction = sourceAstFunction? +sourceRecursiveBody definition = sourceAstLambda (fst definition) (sourceDefinitionBody definition) + +-- Wrap self only for ordering; compile the original definitions below. This +-- retains existing missing-name, duplicate, equation and mutual-cycle checks. +sourceStablePrepare definitions = (lazyList (_ : ok [] t) + (definition rest : stableBind + (lazyBool + (_ : lazyBool + (_ : ok (pair (fst definition) (sourceRecursiveBody definition)) t) + (_ : err (parserDiagnostic 48 "recursive function requires parameters" + (sourceDefinitionName definition) (sourceDefinitionPosition definition) t) definition) + (sourceRecursiveFunction (sourceDefinitionBody definition))) + (_ : ok definition t) (sourceDefinitionSelfRecursive definition)) + (prepared : stableBind (sourceStablePrepare rest) (remaining : ok (pair prepared remaining) t))) + definitions) +orderStableProgramWith orderProgram environment program = (stableBind + (sourceStablePrepare (sourceProgramDefinitions program)) + (definitions : orderProgram environment (pair definitions (sourceProgramFinal program)))) + +sourceCompileStableDefinitionWith resolve environment definition = (let recursive = sourceDefinitionSelfRecursive definition in + stableBind (resolve environment (lazyBool + (_ : sourceRecursiveBody definition) (_ : sourceDefinitionBody definition) recursive)) + (core : stableBind (stableCompileCore core) + (tree : ok (lazyBool (_ : stableFix tree) (_ : tree) recursive) t))) + +orderStableProgram = orderStableProgramWith orderGateProgram +sourceCompileStableDefinition = sourceCompileStableDefinitionWith resolveStableGateAst diff --git a/test/fixtures/reference-source/0f98a415e9bcdd223cd27eba9b501e91fe6b3778a2d5ea59218815081ab3d767.tri b/test/fixtures/reference-source/0f98a415e9bcdd223cd27eba9b501e91fe6b3778a2d5ea59218815081ab3d767.tri @@ -0,0 +1,206 @@ +!import prelude * +!import compiler_io * +!import native_math as N +!export sourceObjectTreeDomain +!export sourceObjectFlatTreeDomain +!export sourceObjectManifestDomain +!export sourceObjectTreeAbi +!export sourceObjectTreeKind +!export sourceObjectEncodeOnto +!export sourceObjectEncode +!export sourceObjectDecode +!export sourceObjectDecodeComplete +!export sourceObjectShard +!export sourceObjectPath +!export sourceObjectDigest +!export sourceObjectHashHex +!export sourceObjectHashValid +!export sourceObjectVerify +!export sourceObjectGet +!export sourceObjectStoreAt +!export sourceObjectStore +!export sourceObjectPutTree +!export sourceObjectGetTree + +-- Native executable objects preserve sharing as name-neutral indexed bundles. +-- The flat codec below remains an independent data codec with its own domain; +-- it is not the native executable publication format. +sourceObjectTreeDomain = "arboricx.indexed-tree.v1" +sourceObjectFlatTreeDomain = "arboricx.tree-term.v1" +sourceObjectManifestDomain = "arboricx.module-manifest.v1" +sourceObjectTreeAbi = "arboricx.abi.tree.v1" +sourceObjectTreeKind = "arboricx.indexed-tree.v1" + +-- Build directly onto a suffix. Appending each encoded left subtree copies +-- its byte list once per enclosing fork and retains huge intermediate lists +-- for compiler exports. This visits each expanded tree occurrence once. +-- Keep triage as the small shape eliminator; canonical preorder is unchanged. +sourceObjectEncodeOnto tree suffix = + triage + (pair 0 suffix) + (child : pair 1 (sourceObjectEncodeOnto child suffix)) + (left right : pair 2 (sourceObjectEncodeOnto left (sourceObjectEncodeOnto right suffix))) + tree +sourceObjectEncode tree = sourceObjectEncodeOnto tree t + +sourceObjectDecode bytes = + lazyList + (_ : err "truncated tree-term" bytes) + (tag rest : + lazyBool + (_ : ok t rest) + (_ : lazyBool + (_ : lazyResult + (problem remaining : err problem remaining) + (child remaining : ok (t child) remaining) + (sourceObjectDecode rest)) + (_ : lazyBool + (_ : lazyResult + (problem remaining : err problem remaining) + (left remaining : lazyResult + (problem tail : err problem tail) + (right tail : ok (t left right) tail) + (sourceObjectDecode remaining)) + (sourceObjectDecode rest)) + (_ : err "invalid tree-term tag" rest) + (equal? tag 2)) + (equal? tag 1)) + (equal? tag 0)) + bytes + +sourceObjectDecodeComplete bytes = + lazyResult + (problem remaining : err problem remaining) + (tree remaining : lazyList + (_ : ok tree t) + (_ _ : err "trailing tree-term bytes" t) + remaining) + (sourceObjectDecode bytes) + +-- The shard is taken by finite destructuring: it must not cause recursion +-- over an abstract hash during fresh-store normalization. +sourceObjectShard hash = + matchList t (a r0 : + matchList t (b r1 : + matchList t (c _ : pair a (pair b (pair c t))) r1) r0) hash +sourceObjectPath root hash = + append root (append "/objects/" (append (sourceObjectShard hash) + (append "/" hash))) +sourceObjectDigest domain payload = + sha256Hex (append domain (pair 0 payload)) + +-- Native math assumes canonical naturals. Keep the existing behavior for +-- arbitrary trees: only canonical bytes use the binary comparison fast path. +-- Canonical-byte helpers stay private; malformed trees retain the reference path. +sourceObjectByteLeaf? value = triage true (_ : false) (_ _ : false) value +sourceObjectByteBit? value = triage true (child : sourceObjectByteLeaf? child) (_ _ : false) value +sourceObjectByteOne? value = triage false (child : sourceObjectByteLeaf? child) (_ _ : false) value +-- Eight statically bounded structural steps avoid numeric countdown work for +-- every digest byte. Inspect the rest shape before the bit, as in the bounded +-- recognizer; an invalid bit or exhausted bound must not force deeper fields. +sourceObjectByteStep next value = (triage + true + (_ : false) + (bit rest : lazyBool + (_ : sourceObjectByteOne? bit) + (_ : andLazy? (sourceObjectByteBit? bit) (_ : next rest)) + (sourceObjectByteLeaf? rest)) + value) +sourceObjectCanonicalByte value = (sourceObjectByteStep + (sourceObjectByteStep + (sourceObjectByteStep + (sourceObjectByteStep + (sourceObjectByteStep + (sourceObjectByteStep + (sourceObjectByteStep + (sourceObjectByteStep sourceObjectByteLeaf?))))))) + value) +-- This compatibility path must not call base's canonical-only arithmetic. +-- The historical predecessor leaves Stem values unchanged and interprets any +-- non-Leaf bit/tail as truthy. Comparison counts down its finite byte bound; +-- structural equality (not numeric equality) controls the strict endpoint. +sourceObjectReferencePred value = (triage + 0 + (child : t child) + (bit rest : lazyBool + (_ : matchBool (t t rest) 0 rest) + (_ : t (t t) (sourceObjectReferencePred rest)) bit) + value) +sourceObjectReferenceLte value bound = (lazyBool + (_ : true) + (_ : lazyBool (_ : false) + (_ : sourceObjectReferenceLte (sourceObjectReferencePred value) + (sourceObjectReferencePred bound)) (isZero_? bound)) + (isZero_? value)) +sourceObjectReferenceLt value bound = (and? + (sourceObjectReferenceLte value bound) (not? (equal? value bound))) +sourceObjectHashByteReference? byte = (or? + (and? (not? (sourceObjectReferenceLt byte 48)) (sourceObjectReferenceLte byte 57)) + (and? (not? (sourceObjectReferenceLt byte 97)) (sourceObjectReferenceLte byte 102))) +sourceObjectHashByte? byte = (lazyBool + (_ : N.lowerHex byte) + (_ : sourceObjectHashByteReference? byte) + (sourceObjectCanonicalByte byte)) +sourceObjectHashHex bytes = (lazyList + (_ : true) + (byte rest : andLazy? (sourceObjectHashByte? byte) (_ : sourceObjectHashHex rest)) + bytes) +sourceObjectHashValid hash = + andLazy? (equal? (length hash) 64) + (_ : sourceObjectHashHex hash) + +sourceObjectVerify domain payload expected = + + onResult_ (sourceObjectDigest domain payload) + (problem : pure (err problem t)) + (actual : lazyBool + (_ : pure (ok payload t)) + (_ : pure (err "CAS digest mismatch" t)) + (equal? actual expected)) + +sourceObjectGet root domain hash = + lazyBool + (_ : onResult_ (readFile (sourceObjectPath root hash)) + (problem : pure (err problem t)) + (payload : sourceObjectVerify domain payload hash)) + (_ : pure (err "invalid CAS digest" t)) + (sourceObjectHashValid hash) + +sourceObjectStore root domain payload = + onOk_ (sourceObjectDigest domain payload) (hash : + onOk_ (createDirectory root) (_ : + onOk_ (createDirectory (append root "/objects")) (_ : + onOk_ (createDirectory (append root (append "/objects/" + (sourceObjectShard hash)))) (_ : + sourceObjectStoreAt root domain payload hash)))) + +-- Host publishes via a unique same-directory temp and an atomic no-replace +-- link; tricu owns the object identity and verifies even a racing winner. +sourceObjectStoreAt root domain payload hash = + onOk_ (writeBytesIfAbsent (sourceObjectPath root hash) payload) (_ : + onResult_ (sourceObjectGet root domain hash) + (problem : pure (err problem t)) + (old : lazyBool + (_ : pure (ok hash t)) + (_ : pure (err "CAS object exists with different bytes" t)) + (equal? old payload))) + +-- tricu owns sharding/directories; the host hashes and atomically transports +-- the inert tree as an indexed DAG, with verified no-replace writes. +sourceObjectPutTree root tree = + onOk_ (hashIndexedTree tree) (hash : + onOk_ (createDirectory root) (_ : + onOk_ (createDirectory (append root "/objects")) (_ : + onOk_ (createDirectory (append root (append "/objects/" + (sourceObjectShard hash)))) (_ : + onOk_ (writeIndexedTreeIfAbsent (sourceObjectPath root hash) hash tree) + (_ : pure (ok hash t)))))) +-- Typed verified transport avoids expanding raw executable bytes into a +-- tricu byte list for hashing and decoding. The portable codec stays available +-- as an independent reference; this path does not silently fall back to it. +sourceObjectGetTree root hash = + lazyBool + (_ : readIndexedTree (sourceObjectPath root hash) hash) + (_ : pure (err "invalid CAS digest" t)) + (sourceObjectHashValid hash) + diff --git a/test/fixtures/reference-source/137f8881288b64071a60fc4e29ae2b276d32bac79f748b8f2174838b9da1ffc0.tri b/test/fixtures/reference-source/137f8881288b64071a60fc4e29ae2b276d32bac79f748b8f2174838b9da1ffc0.tri @@ -0,0 +1,398 @@ +!import prelude * +!import io * +!import socket * +!import parser * +!import native_math as Nat +!export * + +-- --------------------------------------------------------------------------- +-- Constants +-- --------------------------------------------------------------------------- + +maxHeaderBytes = 65536 +maxBodyBytes = 1048576 + +crlfcrlf = pair 13 (pair 10 (pair 13 (pair 10 t))) + +-- --------------------------------------------------------------------------- +-- Response construction +-- --------------------------------------------------------------------------- + +statusPhrases = + [(pair 200 "OK") + (pair 201 "Created") + (pair 204 "No Content") + (pair 400 "Bad Request") + (pair 404 "Not Found") + (pair 405 "Method Not Allowed") + (pair 413 "Content Too Large") + (pair 431 "Request Header Fields Too Large") + (pair 501 "Not Implemented") + (pair 505 "HTTP Version Not Supported")] + +lookupStatusPhrase code [(key, phrase) | rest] = case key of + ^code = phrase + _ = lookupStatusPhrase code rest + +lookupStatusPhrase _ _ = "Internal Server Error" + +statusPhrase code = lookupStatusPhrase code statusPhrases + +statusLine code phrase = append "HTTP/1.1 " (append (showNumber code) (append " " (append phrase "\r\n"))) +headerLine key value = append key (append ": " (append value "\r\n")) + +httpAppendResponseHeader accumulated (key, value) = append accumulated (headerLine key value) +-- Preserve the old pair-projection fallback for malformed response-header items. +httpAppendResponseHeader accumulated _ = append accumulated (headerLine "" "") + +buildResponse status headers body = (append + (statusLine status (statusPhrase status)) + (append (foldl httpAppendResponseHeader "" headers) (append "\r\n" body))) + +-- Count a proper response-body list in one pass. Unlike general listOf, +-- this boundary never transforms elements, so no validated copy is needed. +-- Force each successor's outer constructor before advancing: do not retain a +-- successor continuation for every byte. Input elements remain unforced. +-- Malformed list spines retain length's ordinary contract-failure Result. +httpBodyLength values = let rec + walk remaining count = triage count + (_ : contractErr "not a list" t) + (_ tail : case Nat.successor count of + next{(_, _)} = walk tail next + _ = contractErr "Invalid internal length counter" t) + remaining + in walk values 0 + +response status contentType body = (buildResponse status + [(pair "Content-Type" contentType) + (pair "Content-Length" (showNumber (httpBodyLength body))) + (pair "Connection" "close")] + body) + +emptyResponse status = (buildResponse status + [(pair "Content-Length" "0") (pair "Connection" "close")] + "") + +okResponse body = response 200 "text/plain; charset=utf-8" body +textResponse body = response 200 "text/plain; charset=utf-8" body +jsonResponse body = response 200 "application/json" body +htmlResponse body = response 200 "text/html; charset=utf-8" body +createdResponse body = response 201 "text/plain; charset=utf-8" body +notFoundResponse = response 404 "text/plain; charset=utf-8" "Not found\n" +badRequestResponse message = response 400 "text/plain; charset=utf-8" message +errorResponse status message = response status "text/plain; charset=utf-8" message + +headerEndState 2 13 = 3 +headerEndState _ 13 = 1 +headerEndState 3 10 = 4 +headerEndState 1 10 = 2 +headerEndState _ _ = 0 + +httpHeadersOnly [byte | rest] state reversed = (let + accumulated = pair byte reversed + continue 4 = reverse accumulated + continue next = httpHeadersOnly rest next accumulated + in continue (headerEndState state byte)) +httpHeadersOnly _ _ reversed = reverse reversed + +headersOnly bytes = httpHeadersOnly bytes 0 [] + +responseForMethod "HEAD" response = headersOnly response +responseForMethod _ response = response + +-- --------------------------------------------------------------------------- +-- Header receive / framing +-- --------------------------------------------------------------------------- + +-- maxBytes bounds the entire accumulated receive buffer, including any +-- prefetched suffix. Retain that policy; this is not a header-only byte count. +httpRecvUntil sock delimiter limit accumulated count = (let rec + receive buffer size = (do bind + reply <- recv sock 4096 + case reply of + (t, _) = pure (err 400 buffer) + ((t t), ([], _)) = pure (err 400 buffer) + ((t t), (chunk, _)) = (let + next = append buffer chunk + nextSize = Nat.add size (length chunk) + in case (Nat.lte nextSize limit) of + ^true = case (contains? delimiter next) of + ^true = pure (ok next t) + _ = receive next nextSize + _ = pure (err 431 next)) + _ = pure (err 400 buffer)) + in receive accumulated count) + +recvUntilMax sock delimiter limit = httpRecvUntil sock delimiter limit [] 0 + +recvUntil sock delimiter = recvUntilMax sock delimiter maxHeaderBytes + +recvHeaders sock = recvUntilMax sock crlfcrlf maxHeaderBytes + +-- --------------------------------------------------------------------------- +-- Request line parsing +-- --------------------------------------------------------------------------- + +-- HTTP/1.1 lines require CRLF. A bare CR/LF is an error, never a byte to +-- discard. Failure retains the offending suffix and prior consumption; EOF +-- cannot masquerade as a successfully parsed empty line. +httpLineError code found rest consumed = (parserReplyFail + (parserDiagnostic code "CRLF" found t ["http-line"]) + rest consumed false) + +httpScanLine [13 10 | rest] reversed = parserReplyOk (reverse reversed) rest true false +httpScanLine input{[13 | _]} reversed = httpLineError 2 13 input (not? (emptyList? reversed)) +httpScanLine input{[10 | _]} reversed = httpLineError 2 10 input (not? (emptyList? reversed)) +httpScanLine [byte | rest] reversed = httpScanLine rest (pair byte reversed) +httpScanLine [] reversed = httpLineError 1 t [] (not? (emptyList? reversed)) +httpScanLine input reversed = httpLineError 2 input input (not? (emptyList? reversed)) + +httpLineReply input = httpScanLine input [] +httpBadRequest = err 400 "Bad Request\n" + +-- Unlike the old pair-only helper, this Result distinguishes a blank CRLF +-- line from truncated/malformed input. Both callers propagate the failure. +readLineBytes input = (parserMatchReply + (_ _ _ _ : httpBadRequest) + (line rest _ _ : ok line rest) + (httpLineReply input)) + +-- The finite request line is already framed by readLineBytes. Deliberately +-- retain the historical three-fields grammar: empty fields are permitted, +-- but a third separating space is an error. The parser reply exposes byte +-- consumption to new clients, while parseThreeWords preserves HTTP's public +-- Result/400 boundary and the original "Bad Request" remainder. +httpNonSpace? byte = not? (equal? byte 32) + +httpThreeWordsReply = (parserContext "request-line" (do parserBind + method <- parserTakeWhile httpNonSpace? + parserExpect 32 + target <- parserTakeWhile httpNonSpace? + parserExpect 32 + version <- parserTakeWhile httpNonSpace? + parserEof + parserPure (pair method (pair target version)))) + +parseThreeWords = (bs : + parserMatchReply + (diagnostic rest consumed committed : err 400 "Bad Request\n") + (words rest consumed committed : ok words rest) + (httpThreeWordsReply bs)) + +parseRequestLine input = case (readLineBytes input) of + ((t t), (line, rest)) = case (parseThreeWords line) of + ((t t), (request, _)) = ok request rest + _ = httpBadRequest + _ = httpBadRequest + +-- --------------------------------------------------------------------------- +-- Header parsing +-- --------------------------------------------------------------------------- + +-- Canonical byte input (0..255), as supplied by the socket/byte codecs. +-- Historical malformed numeral/bit behavior is intentionally not a contract. +toLowerAsciiByte byte = case (Nat.inRange byte 65 90) of + ^true = Nat.add byte 32 + _ = byte + +-- RFC 9110 token names, OWS, and field-value bytes. obs-text (128..255) +-- stays opaque; only the ASCII header name is lowercased. Reject obs-fold. +httpTokenByte? byte = (or? (Nat.letter byte) + (or? (Nat.digit byte) (any? (equal? byte) "!#$%&'*+-.^_`|~"))) +httpOws? byte = or? (equal? byte 32) (equal? byte 9) +httpFieldByte? byte = (or? (equal? byte 9) + (or? (Nat.printable byte) (Nat.inRange byte 128 255))) +httpTrimOws value = reverse (dropWhile httpOws? (reverse (dropWhile httpOws? value))) + +-- Parse exactly one finite line, excluding CRLF. Preserve parser diagnostics +-- and consumption, but never accept a valid prefix followed by invalid bytes. +httpHeaderLineReply = (parserContext "header-line" (do parserBind + name <- parserLabel "header-name" (parserSome (parserSatisfy httpTokenByte?)) + parserLabel "colon" (parserExpect 58) + value <- parserLabel "header-value" (parserBefore (parserTakeWhile httpFieldByte?) parserEof) + parserPure (pair (map toLowerAsciiByte name) (httpTrimOws value)))) + +-- Only an actual empty CRLF line ends the headers. The returned suffix is +-- opaque body data, even if it contains CR, LF, NUL or header-looking bytes. +httpParseHeaderLines input reversedHeaders = case (readLineBytes input) of + ((t t), ([], rest)) = ok (reverse reversedHeaders) rest + ((t t), (line, rest)) = case (httpHeaderLineReply line) of + ((t t), (header, _)) = httpParseHeaderLines rest (pair header reversedHeaders) + _ = httpBadRequest + _ = httpBadRequest + +parseHeaders input = httpParseHeaderLines input [] + +-- --------------------------------------------------------------------------- +-- Content-Length parsing +-- --------------------------------------------------------------------------- + +-- The accumulator never exceeds maxBodyBytes. A single checked step can +-- reach at most 10 * maxBodyBytes + 9; after that, validate digits without +-- constructing a larger number. Trailing junk still means 400, not 413. +httpBodyTooLarge = err 413 "Request body too large\n" + +httpLengthOverflow [] = httpBodyTooLarge +httpLengthOverflow [byte | rest] = (let + continue ^true = httpLengthOverflow rest + continue _ = httpBadRequest + in continue (Nat.digit byte)) +httpLengthOverflow _ = httpBadRequest + +httpLengthDigits [] value = ok (just value) t +httpLengthDigits [byte | rest] value = (let + digit ^true = (let + next = Nat.decimal value (Nat.subtract byte 48) + bounded ^true = httpLengthDigits rest next + bounded _ = httpLengthOverflow rest + in bounded (Nat.lte next maxBodyBytes)) + digit _ = httpBadRequest + in digit (Nat.digit byte)) +httpLengthDigits _ _ = httpBadRequest + +-- Input is a finite canonical byte list, with no OWS: parseHeaders already +-- trims field edges. Empty strings, signs, commas and interior whitespace fail. +parseContentLengthValue input{[_ | _]} = httpLengthDigits input 0 +parseContentLengthValue _ = httpBadRequest + +-- Input names are normalized by parseHeaders. Select the sole raw length +-- before decoding: duplicate fields and unsupported transfer coding are 400 +-- regardless of field order or whether a length would otherwise exceed limits. +-- This server supports Content-Length framing only, not chunked decoding. +httpSelectLength [] t = ok nothing t +httpSelectLength [] (t value) = parseContentLengthValue value +httpSelectLength [("transfer-encoding", _) | _] _ = httpBadRequest +httpSelectLength [("content-length", value) | rest] t = httpSelectLength rest (just value) +httpSelectLength [("content-length", _) | _] _ = httpBadRequest +httpSelectLength [(_, _) | rest] selected = httpSelectLength rest selected +httpSelectLength _ _ = httpBadRequest + +contentLength headers = httpSelectLength headers nothing + +-- --------------------------------------------------------------------------- +-- Body reading +-- --------------------------------------------------------------------------- + +bodyReadState remaining reversed rest = pair remaining (pair reversed rest) + +-- State is (remaining, (reversed body, unused input)); zero consumes nothing. +-- remaining is a canonical natural supplied by the bounded length decoder. +takeBodyBytes bytes 0 reversed = bodyReadState 0 reversed bytes +-- The zero equation above excludes underflow. Specialized predecessor avoids +-- a full-width comparison/subtraction per byte of a canonical body counter. +takeBodyBytes [byte | rest] remaining reversed = takeBodyBytes rest (Nat.predecessor remaining) (pair byte reversed) +takeBodyBytes _ remaining reversed = bodyReadState remaining reversed [] + +-- Retain the existing receive-size buckets, expressed as native comparisons +-- rather than bit shifts. Zero is handled before asking for another chunk. +httpRecvBucket remaining [size | rest] = (let + choose ^true = size + choose _ = httpRecvBucket remaining rest + in choose (Nat.gte remaining size)) +httpRecvBucket _ _ = 1 + +recvChunkMax4096 remaining = httpRecvBucket remaining [4096 1024 256 64 16] + +-- One state machine owns both buffered and subsequent socket input. EOF before +-- completion is a 400, not another recv with unchanged remaining length. +httpReadBodyState _ (0, (reversed, rest)) = pure (ok (reverse reversed) rest) +httpReadBodyState sock (remaining, (reversed, _)) = (let + received (t, (message, _)) = pure (err 400 (append "recv failed while reading body: " message)) + received ((t t), ([], _)) = pure (err 400 "Unexpected end of request body\n") + received ((t t), (chunk, _)) = httpReadBodyState sock (takeBodyBytes chunk remaining reversed) + received _ = pure httpBadRequest + in do bind + reply <- recv sock (recvChunkMax4096 remaining) + received reply) +httpReadBodyState _ _ = pure httpBadRequest + +readBodyMore sock remaining reversed = httpReadBodyState sock (bodyReadState remaining reversed []) +readBodyExact sock expected initial = httpReadBodyState sock (takeBodyBytes initial expected []) + +-- readBodyExact starts with remaining + length(reversed) = expected. +-- takeBodyBytes preserves that sum; only remaining=0 succeeds, while early EOF +-- or recv failure returns an error. A successful body already has exact length: +-- do not traverse it again solely to recount it. +readBody sock headers initial = (let + selected ((t t), (t, _)) = pure (ok [] initial) + selected ((t t), ((t count), _)) = readBodyExact sock count initial + selected failure = pure failure + in selected (contentLength headers)) + +-- --------------------------------------------------------------------------- +-- Request validation +-- --------------------------------------------------------------------------- + +validMethod? "GET" = true +validMethod? "POST" = true +validMethod? "HEAD" = true +validMethod? _ = false + +validVersion? "HTTP/1.1" = true +validVersion? "HTTP/1.0" = true +validVersion? _ = false + +validTarget? [47 | _] = true +validTarget? _ = false + +-- Keep error precedence: method, version, then target. Headers are validated +-- separately; accepting them here does not select body framing. +validateRequest method target version _ = (let + targetChecked ^true = ok t t + targetChecked _ = httpBadRequest + versionChecked ^true = targetChecked (validTarget? target) + versionChecked _ = err 505 "HTTP Version Not Supported\n" + methodChecked ^true = versionChecked (validVersion? version) + methodChecked _ = httpBadRequest + in methodChecked (validMethod? method)) + +-- --------------------------------------------------------------------------- +-- Handler pipeline +-- --------------------------------------------------------------------------- + +routerMethod "HEAD" = "GET" +routerMethod method = method + +-- finally preserves a send Result and attempts close even after send failure. +-- Like the existing IO helper, it does not catch host exceptions/abortIO. +respondAndClose sock response = (do onOk_ + finally (send sock response) (closeSocket_ sock) + pure (ok t t)) + +httpRespond client method response = respondAndClose client (responseForMethod method response) + +httpHandleBody router client method target headers reply = (let + handle ((t t), (body, _)) = (do bind + response <- router (routerMethod method) target headers body + httpRespond client method response) + handle (t, (status, message)) = httpRespond client method (errorResponse status message) + handle _ = httpRespond client method (badRequestResponse "Bad Request\n") + in handle reply) + +httpHandleHeaders router client method target version input = (let + headersParsed ((t t), (headers, initial)) = (let + validated ((t t), _) = (do bind + reply <- readBody client headers initial + httpHandleBody router client method target headers reply) + validated (t, (status, message)) = httpRespond client method (errorResponse status message) + validated _ = httpRespond client method (badRequestResponse "Bad Request\n") + in validated (validateRequest method target version headers)) + -- Retain historical behavior: a header syntax failure has no trusted HEAD + -- response handling, even though the request line has already been parsed. + headersParsed _ = respondAndClose client (badRequestResponse "Bad Request\n") + in headersParsed (parseHeaders input)) + +httpHandleRequest router client ((t t), ((method, (target, version)), headers)) = httpHandleHeaders router client method target version headers +httpHandleRequest _ client _ = respondAndClose client (badRequestResponse "Bad Request\n") + +httpHandleReceived router client ((t t), (raw, _)) = httpHandleRequest router client (parseRequestLine raw) +httpHandleReceived _ client (t, (431, _)) = respondAndClose client (errorResponse 431 "Request Header Fields Too Large\n") +httpHandleReceived _ client _ = respondAndClose client (badRequestResponse "Bad Request\n") + +httpHandlerIO router client _ = (do bind + reply <- recvHeaders client + httpHandleReceived router client reply) + +-- One effectful pipeline; a pure router needs only this value-to-action adapter. +httpPureRouter router method target headers body = pure (router method target headers body) +httpHandler router client peer = httpHandlerIO (httpPureRouter router) client peer diff --git a/test/fixtures/reference-source/1551418951ec75f1a0f1d47e570b645ccc1e7e4f5bb1de6dd0c4583c4f2fb3a5.tri b/test/fixtures/reference-source/1551418951ec75f1a0f1d47e570b645ccc1e7e4f5bb1de6dd0c4583c4f2fb3a5.tri @@ -0,0 +1,106 @@ +!import prelude * +!import http * +!import native_math as Nat +!export * + +-- Store root is trusted configuration. These pure path helpers are not guards; +-- every read/write entrypoint must validate a hash before using it as a path. +pathJoin parent child = append parent (append "/" child) +objectDir root shard = pathJoin (pathJoin root "objects") shard + +hashShard [a | [b | [c | _]]] = [a b c] +hashShard [a | [b | _]] = [a b] +hashShard [a | _] = [a] +hashShard _ = [] + +bundleObjectPath root hash = pathJoin (objectDir root (hashShard hash)) (append hash ".arboricx") + +-- Exact canonical lowercase SHA-256 spelling. Literal patterns, unlike numeric +-- range predicates, reject malformed numeral trees without normalizing them. +-- Disjoint compact-bit ranges: 48..55, 56..57, 97, 98..99, +-- 100..101, 102. Every captured bit is checked as an exact Boolean. +bundleBit? t = true +bundleBit? (t t) = true +bundleBit? _ = false +bundleHexByte? (a, (b, (c, 6))) = and? (bundleBit? a) (and? (bundleBit? b) (bundleBit? c)) +bundleHexByte? (a, 28) = bundleBit? a +bundleHexByte? 97 = true +bundleHexByte? (a, 49) = bundleBit? a +bundleHexByte? (a, 50) = bundleBit? a +bundleHexByte? 102 = true +bundleHexByte? _ = false + +-- The counter is internal and canonical. At zero inspect only the list shape: +-- never force the 65th byte or its suffix, even for arbitrary malformed trees. +bundleHashBytes [] 0 = true +bundleHashBytes _ 0 = false +bundleHashBytes [byte | rest] remaining = case (bundleHexByte? byte) of + ^true = bundleHashBytes rest (Nat.subtract remaining 1) + _ = false + +bundleHashBytes _ _ = false + +-- The fixed SHA-256 boundary uses structural fuel instead of subtracting a +-- compact-natural counter per character. Retain bundleHashBytes for callers +-- that explicitly supply a length. Both stop before inspecting a 65th element. +bundleHashFuel8 tail = t (t (t (t (t (t (t (t tail))))))) +bundleHashFuel64 = bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 + (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 t))))))) +bundleHashScan [] t = true +bundleHashScan _ t = false +bundleHashScan [byte | rest] (t fuel) = case bundleHexByte? byte of + ^true = bundleHashScan rest fuel + _ = false +bundleHashScan _ _ = false +bundleHashValid? hash = bundleHashScan hash bundleHashFuel64 +bundleHashC = guardC "expected 64 lowercase hexadecimal hash characters" bundleHashValid? + +-- A proper list prefix; return its exact suffix, without normalizing the tail. +stripPrefix [] input = just input +stripPrefix [wanted | prefix] [byte | rest] = case byte of + ^wanted = stripPrefix prefix rest + _ = nothing + +stripPrefix _ _ = nothing + +bundleHashPrefix = "/_arboricx/bundle/hash/" +bundlePath = "/_arboricx/bundle" +healthPath = "/_arboricx/health" +bundleContentType = "application/vnd.arboricx.bundle" + +htmlLandingPage = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width, initial-scale=1'><title>Arboricx Server</title></head><body><h1>Arboricx Server</h1><p>Bundle registry</p><p><a href='https://git.eversole.co/James/tricu'>Made with Love (and trees, lots of trees)</a></p></body></html>" + +bundleResponse bytes = response 200 bundleContentType bytes + +-- Read-only exact-version index. Administrative publication happens locally; +-- this route never resolves dependencies or executes fetched code. HTTP input +-- bytes are canonical naturals. Scan at most 128 bytes per path component. +packagePrefix = "/_arboricx/package/" +packageAlphaNum? byte = (or? (Nat.inRange byte 48 57) + (or? (Nat.inRange byte 65 90) (Nat.inRange byte 97 122))) +packageByte? byte = (or? (packageAlphaNum? byte) + (or? (equal? byte 46) (or? (equal? byte 95) (equal? byte 45)))) +packageVersion [] reversed _ = just (reverse reversed) +packageVersion _ _ 0 = nothing +packageVersion [byte | rest] reversed remaining = case (packageByte? byte) of + ^true = packageVersion rest (pair byte reversed) (Nat.subtract remaining 1) + _ = nothing + +packageVersion _ _ _ = nothing +packageName [47 | [first | rest]] reversed _ = case (packageAlphaNum? first) of + ^true = case (packageVersion rest [first] 127) of + (t version) = just (pair (reverse reversed) version) + _ = nothing + _ = nothing + +packageName _ _ 0 = nothing +packageName [byte | rest] reversed remaining = case (packageByte? byte) of + ^true = packageName rest (pair byte reversed) (Nat.subtract remaining 1) + _ = nothing + +packageName _ _ _ = nothing +packageTarget [first | rest] = case (packageAlphaNum? first) of + ^true = packageName rest [first] 127 + _ = nothing + +packageTarget _ = nothing diff --git a/test/fixtures/reference-source/1ca2682e6a35d949196ff3b1d2aabed47af57e7ef43b8e32b2b7b19ca004f64f.tri b/test/fixtures/reference-source/1ca2682e6a35d949196ff3b1d2aabed47af57e7ef43b8e32b2b7b19ca004f64f.tri @@ -0,0 +1,148 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export readSource +!export readText +!export encodeText +!export writeText +!export lineCursor +!export readLine + +problem operation detail progress = ["invalid-data" operation detail progress] +-- base.foldr supplies the accumulator first. +flatten chunks = B.foldr (suffix chunk : B.append chunk suffix) [] (B.reverse chunks) + +checkedLimit limit next = case B.checkContract B.nat? limit of + ((t t), (count, t)) = case N.lte count 9223372036854775807 of + (t t) = next count + _ = IO.pure (B.err ["invalid-bound" "text/limit" "invalid text byte limit" "none"] t) + _ = IO.pure (B.err ["invalid-bound" "text/limit" "invalid text byte limit" "none"] t) + +-- Split a bounded prefix; native encode validates its scalars before producing +-- bytes. [] signals an improper character spine. +split 0 rest = [[] rest] +split _ [] = [[] []] +split count [c | rest] = case split (N.predecessor count) rest of + [prefix after] = [(B.pair c prefix) after] + _ = [] +split _ _ = [] + +encodeLoop remaining text chunks = case split 1024 text of + [[] []] = IO.pure (B.ok (flatten chunks) t) + [part rest] = do IO.onOk + bytes <- IO.encodeUTF8 part + case N.lte (B.length bytes) remaining of + (t t) = encodeLoop (N.subtract remaining (B.length bytes)) rest (B.pair bytes chunks) + _ = IO.pure (B.err ["invalid-bound" "text/encode" "encoded text exceeds byte limit" "none"] t) + _ = IO.pure (B.err (problem "text/encode" "improper scalar list" "none") t) +encodeText limit text = checkedLimit limit (remaining : encodeLoop remaining text []) + +-- Force the small acknowledged-byte counter before the next request, without +-- retaining arithmetic that depends on previously emitted chunks. +forceCount t next = next t +forceCount (t bit rest) next = case bit of + t = forceCount rest next + (t t) = forceCount rest next + _ = IO.fail "Invalid text counter" +forceCount _ _ = IO.fail "Invalid text counter" +writeFailure confirmed cause = IO.pure (B.err ["write-text" confirmed cause] t) +writeLoop writer text confirmed = forceCount confirmed (_ : case split 1024 text of + [[] []] = IO.pure (B.ok t t) + [part rest] = IO.bind (IO.encodeUTF8 part) (encoded : case encoded of + ((t t), (bytes, t)) = IO.bind (IO.writeAll writer bytes) (written : case written of + ((t t), (t, t)) = writeLoop writer rest (N.add confirmed (B.length bytes)) + (t, (cause, t)) = writeFailure confirmed cause + _ = IO.fail "Invalid text write reply") + (t, (cause, t)) = writeFailure confirmed cause + _ = IO.fail "Invalid UTF-8 encode reply") + _ = writeFailure confirmed (problem "text/write" "improper scalar list" "none")) +writeText writer text = writeLoop writer text 0 + +-- Whole-source convenience retaining the exact received octets and decoded +-- scalars. Source policy rejects an initial BOM. A one-byte EOF probe detects +-- excess input at the budget boundary. Callers serialize use of this cursor +-- across the complete operation; native leases cover individual reads. +-- Composite failure retains confirmed input +-- bytes (before any uncertain failing read) plus the unchanged native problem. +readFailure consumed cause = IO.pure (B.err ["read-source" consumed cause] t) +readCount 0 = 1 +readCount remaining = case N.lte remaining 4093 of + (t t) = remaining + _ = 4093 +readLoop reader limit remaining carry first rawChunks textChunks = + IO.bind (IO.readSome reader (readCount remaining)) (received : case received of + ((t t), (("eof", t), t)) = IO.bind (IO.decodeUTF8 1 carry) (decoded : case decoded of + ((t t), ([[] []], t)) = IO.pure (B.ok (B.pair (flatten rawChunks) (flatten textChunks)) t) + (t, (cause, t)) = readFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid UTF-8 final reply") + ((t t), (("data", bytes), t)) = let count = B.length bytes in + case N.lte count remaining of + (t t) = IO.bind (IO.decodeUTF8 0 (B.append carry bytes)) (decoded : case decoded of + ((t t), ([text pending], t)) = case first of + 1 = case text of + [] = readLoop reader limit (N.subtract remaining count) pending 1 (B.pair bytes rawChunks) textChunks + [65279 | _] = readFailure (N.add (N.subtract limit remaining) count) + (problem "text/read-source" "initial source BOM is not permitted" "none") + _ = readLoop reader limit (N.subtract remaining count) pending 0 (B.pair bytes rawChunks) (B.pair text textChunks) + _ = readLoop reader limit (N.subtract remaining count) pending 0 (B.pair bytes rawChunks) (B.pair text textChunks) + (t, (cause, t)) = readFailure (N.add (N.subtract limit remaining) count) cause + _ = IO.fail "Invalid UTF-8 decode reply") + _ = readFailure (N.add (N.subtract limit remaining) count) + ["invalid-bound" "text/read-source" "source exceeds byte limit" "none"] + (t, (cause, t)) = readFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid source read reply") +readSource reader limit = checkedLimit limit (count : readLoop reader count count [] 1 [] []) + +-- Single-consumer source cursor represented as a source value. Thread the +-- returned cursor forward, serialize its use with all reads on the underlying +-- reader, and retire it on read failure. Read-ahead is bounded to 4093 bytes. +lineCursor reader = [reader [] 0] + +-- The byte budget counts line content; LF delimits lines. CR, BOM and NUL +-- remain content. +-- Scan before decoding: malformed bytes after LF belong to the NEXT line. +lineSplit [] = [[] [] 0] +lineSplit [10 | rest] = [[] rest 1] +lineSplit [byte | rest] = case lineSplit rest of + [prefix after ended] = [(B.pair byte prefix) after ended] + _ = [] +lineSplit _ = [] +lineFailure consumed cause = IO.pure (B.err ["read-line" consumed cause] t) +lineReady reader after ending chunks = IO.pure + (B.ok [["line" (flatten chunks) ending] [reader after 0]] t) +lineEOF reader consumed carry chunks = IO.bind (IO.decodeUTF8 1 carry) (decoded : case decoded of + ((t t), ([[] []], t)) = case consumed of + 0 = IO.pure (B.ok [["eof"] [reader [] 1]] t) + _ = IO.pure (B.ok [["line" (flatten chunks) "eof"] [reader [] 1]] t) + (t, (cause, t)) = lineFailure consumed cause + _ = IO.fail "Invalid line final UTF-8 reply") +lineLoop reader limit remaining carry chunks buffered = forceCount remaining (_ : case buffered of + [] = IO.bind (IO.readSome reader 4093) (received : case received of + ((t t), (("eof", t), t)) = lineEOF reader (N.subtract limit remaining) carry chunks + ((t t), (("data", bytes), t)) = lineLoop reader limit remaining carry chunks bytes + (t, (cause, t)) = lineFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid line read reply") + _ = case lineSplit buffered of + [part after ended] = let count = B.length part in + case N.lte count remaining of + (t t) = IO.bind (IO.decodeUTF8 ended (B.append carry part)) (decoded : case decoded of + ((t t), ([text pending], t)) = case ended of + 1 = lineReady reader after "lf" (B.pair text chunks) + _ = lineLoop reader limit (N.subtract remaining count) pending (B.pair text chunks) [] + (t, (cause, t)) = lineFailure (N.add (N.subtract limit remaining) count) cause + _ = IO.fail "Invalid line UTF-8 reply") + _ = lineFailure (N.add (N.subtract limit remaining) count) + ["invalid-bound" "text/read-line" "line exceeds byte limit" "none"] + _ = IO.fail "Invalid line cursor bytes") +readLine cursor limit = checkedLimit limit (count : case cursor of + [reader buffered 0] = lineLoop reader count count [] [] buffered + [reader [] 1] = IO.pure (B.ok [["eof"] cursor] t) + _ = IO.pure (B.err ["invalid-argument" "text/read-line" "invalid line cursor" "none"] t)) + +-- Generic bounded whole-text convenience preserving an initial BOM as content. +-- It uses the shared incremental decoder with this text policy. +readText reader limit = checkedLimit limit (count : IO.onOk + (readLoop reader count count [] 0 [] []) + (received : case received of + (_, text) = IO.pure (B.ok text t) + _ = IO.fail "Invalid text read result")) diff --git a/test/fixtures/reference-source/2373335a22f1a6c7d00c7dd612ec8498deb8502eaf48bd6e8aa9f9407a1bfc13.tri b/test/fixtures/reference-source/2373335a22f1a6c7d00c7dd612ec8498deb8502eaf48bd6e8aa9f9407a1bfc13.tri @@ -0,0 +1,204 @@ +!import prelude * +!import compiler_io * +!import source_object * +!import source_module * +!import source_resolve * +!export * + +-- Indexed Arboricx v1.1 writer. The host only supplies binary writes; +-- tricu constructs the node DAG, canonical manifest and complete bundle. +-- Natural numbers use a little-endian bit tree. Consume its bits first, +-- rather than subtracting 256 repeatedly when encoding large offsets. +sourceBundleTakeByte number places weight byte = + triage + (pair byte 0) + (_ : pair byte 0) + (bit rest : + lazyBool + (_ : pair (add byte (matchBool weight 0 bit)) rest) + (_ : sourceBundleTakeByte rest (pred places) (add weight weight) + (add byte (matchBool weight 0 bit))) + (equal? places 1)) + number +sourceBundleSplitByte number = sourceBundleTakeByte number 8 1 0 +sourceBundleU32 number = + matchPair (b0 n1 : + matchPair (b1 n2 : + matchPair (b2 n3 : + matchPair (b3 n4 : pair b3 (pair b2 (pair b1 (pair b0 t)))) + (sourceBundleSplitByte n3)) + (sourceBundleSplitByte n2)) + (sourceBundleSplitByte n1)) + (sourceBundleSplitByte number) +sourceBundleU64 number = + matchPair (b0 n1 : + matchPair (b1 n2 : + matchPair (b2 n3 : + matchPair (b3 n4 : + matchPair (b4 n5 : + matchPair (b5 n6 : + matchPair (b6 n7 : + matchPair (b7 n8 : + [(b7) (b6) (b5) (b4) (b3) (b2) (b1) (b0)]) + (sourceBundleSplitByte n7)) + (sourceBundleSplitByte n6)) + (sourceBundleSplitByte n5)) + (sourceBundleSplitByte n4)) + (sourceBundleSplitByte n3)) + (sourceBundleSplitByte n2)) + (sourceBundleSplitByte n1)) + (sourceBundleSplitByte number) +sourceBundleText text = append (sourceBundleU32 (length text)) text + +-- An index-key has the same Leaf/Stem/Fork shape as its corresponding node, +-- with child indices (not subtrees) in the child positions. The association +-- list deduplicates structural keys across *all* exports, in left-to-right +-- postorder, matching Wire.buildBundle. +-- State: (nextIndex, (reversedIndexedNodes, seenKeyToIndex)). +sourceBundleAdd key state = + lazyMaybe + (_ : pair (fst state) + (pair (succ (fst state)) + (pair (pair key (fst (snd state))) + (pair (pair key (fst state)) (snd (snd state)))))) + (oldIndex : pair oldIndex state) + (sourceFindExternal (snd (snd state)) key) +sourceBundleVisit tree state = + triage + (sourceBundleAdd t state) + (child : matchPair (index next : sourceBundleAdd (t index) next) + (sourceBundleVisit child state)) + (left right : matchPair (leftIndex next : + matchPair (rightIndex after : + sourceBundleAdd (t leftIndex rightIndex) after) + (sourceBundleVisit right next)) + (sourceBundleVisit left state)) + tree +sourceBundleIndexExports exports state roots = + lazyList + (_ : pair (reverse roots) (reverse (fst (snd state)))) + (entry rest : matchPair (index after : + sourceBundleIndexExports rest after (pair (pair (fst entry) index) roots)) + (sourceBundleVisit (snd entry) state)) + exports +sourceBundleIndex namedTrees = + sourceBundleIndexExports namedTrees (pair 0 (pair t t)) t + +sourceBundlePayload key = + triage + [(0)] + (child : pair 1 (sourceBundleU32 child)) + (left right : pair 2 (append (sourceBundleU32 left) + (sourceBundleU32 right))) + key +sourceBundleNodeEntry key = + triage + (append (sourceBundleU32 1) (sourceBundlePayload key)) + (_ : append (sourceBundleU32 5) (sourceBundlePayload key)) + (_ _ : append (sourceBundleU32 9) (sourceBundlePayload key)) + key +sourceBundleNodeEntries nodes = + lazyList + (_ : t) + (key rest : append (sourceBundleNodeEntry key) (sourceBundleNodeEntries rest)) + nodes +sourceBundleNodes nodes = + append (sourceBundleU64 (length nodes)) (sourceBundleNodeEntries nodes) + +sourceBundleRootEntry root first = + append (sourceBundleU32 (snd root)) + (sourceBundleText (matchBool "default" "root" first)) +sourceBundleRootEntries roots first = + lazyList + (_ : t) + (root rest : append (sourceBundleRootEntry root first) + (sourceBundleRootEntries rest false)) + roots +sourceBundleExportEntry root = + append (sourceBundleText (fst root)) + (append (sourceBundleU32 (snd root)) + (append (sourceBundleText "term") + (sourceBundleText "arboricx.abi.tree.v1"))) +sourceBundleExportEntries roots = + lazyList + (_ : t) + (root rest : append (sourceBundleExportEntry root) (sourceBundleExportEntries rest)) + roots +sourceBundleManifest roots = + append "ARBMNFST" (append [(0) (1) (0) (1)] + (append (sourceBundleText "arboricx.bundle.manifest.v1") + (append (sourceBundleText "tree-calculus-executable-object") + (append (sourceBundleText "tree-calculus.v1") + (append (sourceBundleText "indexed") + (append (sourceBundleText "arboricx.indexed.node.v1") + (append (sourceBundleText "arboricx.indexed.payload.v1") + (append (sourceBundleText "tree-calculus.v1") + (append (sourceBundleText "normal-order") + (append (sourceBundleText "arboricx.abi.tree.v1") + (append (sourceBundleU32 0) + (append [(0)] + (append (sourceBundleU32 (length roots)) + (append (sourceBundleRootEntries roots true) + (append (sourceBundleU32 (length roots)) + (append (sourceBundleExportEntries roots) + (append (sourceBundleU32 1) + (append [(0) (5) (0) (0) (0) (8)] + (append "arboricx" (sourceBundleU32 0)))))))))))))))))))) +sourceBundleSection section offset size = + append (sourceBundleU32 section) + (append [(0) (1) (0) (1) (0) (0) (0) (0)] + (append (sourceBundleU64 offset) + (append (sourceBundleU64 size) (sourceBundleU32 0)))) +sourceBundleHeader = + append "ARBORICX" (append [(0) (1) (0) (0)] + (append (sourceBundleU32 2) + (append (sourceBundleU64 0) (sourceBundleU64 32)))) +-- Complete the variable-length sections before exposing their lengths to +-- fixed-width integer encoders; forcing an abstract `length` as bits can +-- otherwise drive unbounded normalization of an unknown stream. +sourceBundleCountFrom bytes count = + lazyList + (_ : ok count t) + (_ rest : sourceBundleCountFrom rest (succ count)) + bytes +sourceBundleCount bytes = sourceBundleCountFrom bytes 0 +sourceBundleAssemble manifest nodes = + lazyResult + (_ _ : t) + (manifestSize _ : lazyResult + (_ _ : t) + (nodesSize _ : append sourceBundleHeader + (append (sourceBundleSection 1 96 manifestSize) + (append (sourceBundleSection 2 (add manifestSize 96) + nodesSize) + (append manifest nodes)))) + (sourceBundleCount nodes)) + (sourceBundleCount manifest) +sourceBundleEncode namedTrees = + lazyList + (_ : err "bundle requires an export" t) + (_ _ : matchPair (roots nodes : + ok (sourceBundleAssemble (sourceBundleManifest roots) + (sourceBundleNodes nodes)) t) + (sourceBundleIndex namedTrees)) + namedTrees + +sourceBundleHydrate entries root namedTrees = + lazyList + (_ : pure (ok (reverse namedTrees) t)) + (entry rest : onResult_ (sourceObjectGetTree root (snd entry)) + (problem : pure (err problem t)) + (tree : sourceBundleHydrate rest root (pair (pair (fst entry) tree) namedTrees))) + entries +sourceBundlePack root manifestHash outputPath = + onResult_ (sourceModuleGetManifest root manifestHash) + (problem : pure (err problem t)) + (entries : onResult_ (sourceBundleHydrate entries root t) + (problem : pure (err problem t)) + (named : lazyResult + (problem unused : pure (err problem t)) + (bytes unused : onOk_ (sha256Hex bytes) (bundleHash : + onOk_ (atomicWriteBytes outputPath bytes) (_ : + pure (ok bundleHash t)))) + (sourceBundleEncode named))) + diff --git a/test/fixtures/reference-source/297b2195345b9bf4c49857aacd48545604157a9a0f7b3cb5d8ec7759e2e41e83.tri b/test/fixtures/reference-source/297b2195345b9bf4c49857aacd48545604157a9a0f7b3cb5d8ec7759e2e41e83.tri @@ -0,0 +1,259 @@ +!import prelude * +!import parser * +!import source_parser * +!import source_lexer * +!import source_ast * +!import source_equations * +!export * + +-- Program = pair definitions (Maybe finalExpression). +-- Definition = pair locatedNameToken body. Contiguous plain equations +-- coalesce into one located equation body; unique all-name singleton heads +-- take the ordinary lambda path. Program parsing is distinct from the +-- single-expression gate. +sourceProgramDefinitions program = fst program +sourceProgramFinal program = snd program +sourceDefinitionName definition = sourceTokenPayload (fst definition) +sourceDefinitionPosition definition = sourceTokenStart (fst definition) +sourceDefinitionBody definition = snd definition + +sourceProgramSkipNewlines = + parserMap (_ : t) (parserMany (sourceGateExpect sourceTokenNewline)) + +-- Plain and annotated heads share the owning definition's layout boundary. +sourceProgramDefinitionValue column = + sourceGateParseLayoutValue (sourceGateExprIndented column) + (sourceGateLayoutIndented column) "definition body" + +-- Look ahead across the head (including delimited pattern newlines). This +-- distinguishes `f t` (expression) from `f x = x` (definition), without +-- consuming tokens or trying a partially consumed alternative. +sourceProgramAssignOnLine? tokens = sourceEquationAssignLine? tokens + +sourceProgramDefinitionHead? tokens = + lazyList + (_ : false) + (token rest : + lazyBool + (_ : sourceProgramAssignOnLine? rest) + (_ : false) + (equal? (sourceTokenKind token) sourceTokenName)) + tokens + +-- Contract annotations on plain definition heads. Contract expressions are +-- ordinary names or parenthesized applications. Lowering preserves Result +-- short-circuiting and passes each accepted/transformed value to the body. +sourceProgramContract = + parserOr (sourceGateParseGroup (tokens : sourceGateExprWithLayout tokens sourceGateLayoutNested)) + (parserMap sourceAstName (sourceGateExpect sourceTokenName)) + +-- Argument = pair (Maybe namedBinderToken) (Maybe contractExpression). +sourceProgramArgNamed input = + parserBind (sourceGateExpect sourceTokenName) (name : + parserMap (contract : pair (just name) contract) + (parserOptional (parserThen (sourceGateExpect sourceTokenAt) + sourceProgramContract))) input + +sourceProgramArgPhantom = + parserMap (contract : pair nothing (just contract)) + (parserThen (sourceGateExpect sourceTokenAt) sourceProgramContract) + +sourceProgramArgument = parserOr sourceProgramArgPhantom sourceProgramArgNamed + +sourceProgramSynthetic name node = + sourceToken sourceTokenName name + (sourceAstStart node) (sourceAstStart node) + +sourceProgramName name node = sourceAstName (sourceProgramSynthetic name node) +sourceProgramApply2 function first second = + sourceAstApply (sourceAstApply function first) second +sourceProgramApply3 function first second third = + sourceAstApply (sourceProgramApply2 function first second) third + +sourceProgramChecked contract value = + sourceProgramApply2 (sourceProgramName "withContract" contract) contract value + +sourceProgramFailure msg = + sourceProgramApply2 + (sourceProgramName "err" msg) msg + (sourceAstLeaf (sourceProgramSynthetic "t" msg)) + +sourceProgramOnFailure node = + sourceAstLambda + (sourceProgramSynthetic "msg" node) + (sourceAstLambda (sourceProgramSynthetic "_" node) + (sourceProgramFailure (sourceProgramName "msg" node))) + +-- Earlier failed arguments must still accept the remaining arguments before +-- producing the error, just like the historical Haskell lowering. +sourceProgramAbsorbFailure node = + sourceAstLambda + (sourceProgramSynthetic "msg" node) + (sourceAstLambda (sourceProgramSynthetic "_" node) + (sourceAstLambda (sourceProgramSynthetic "_" node) + (sourceProgramFailure (sourceProgramName "msg" node)))) + +sourceProgramReturn contract body = + lazyMaybe + (_ : body) + (c : sourceProgramApply3 + (sourceProgramName "matchResult" c) + (sourceProgramOnFailure c) + (sourceAstLambda (sourceProgramSynthetic "r" c) + (sourceAstLambda (sourceProgramSynthetic "_" c) + (sourceProgramName "r" c))) + (sourceProgramChecked c body)) + contract + +sourceProgramArgName arg index anchor = + lazyMaybe + (_ : sourceProgramSynthetic + (if (isZero_? index) "x" (if (equal? index 1) "y" "z")) anchor) + (token : token) + (fst arg) + +sourceProgramRawApply args raw = (let rec + walk args raw index = + lazyList + (_ : raw) + (arg rest : walk rest + (sourceAstApply raw + (sourceAstName (sourceProgramArgName arg index raw))) + (succ index)) + args + in walk args raw 0) + +sourceProgramWrapArgs args body = (let rec + walk args index = + lazyList + (_ : body) + (arg rest : + sourceAstLambda (sourceProgramArgName arg index body) + (lazyMaybe + (_ : walk rest (succ index)) + (c : sourceProgramApply3 + (sourceProgramName "matchResult" c) + (lazyList + (_ : sourceProgramOnFailure c) + (other tail : sourceProgramAbsorbFailure c) + rest) + (sourceAstLambda (sourceProgramArgName arg index body) + (sourceAstLambda (sourceProgramSynthetic "_" c) + (walk rest (succ index)))) + (sourceProgramChecked c + (sourceAstName (sourceProgramArgName arg index body)))) + (snd arg))) + args + in walk args 0) + +sourceProgramAnyContract? args = + lazyList + (_ : false) + (arg rest : lazyMaybe + (_ : sourceProgramAnyContract? rest) + (contract : true) + (snd arg)) + args + +sourceProgramAnyPhantom? args = + lazyList + (_ : false) + (arg rest : lazyMaybe + (_ : true) + (name : sourceProgramAnyPhantom? rest) + (fst arg)) + args + +sourceProgramAllPhantom? args = + lazyList + (_ : true) + (arg rest : lazyMaybe + (_ : sourceProgramAllPhantom? rest) + (name : false) + (fst arg)) + args + +sourceProgramPlainArgs args body = + lazyList + (_ : body) + (arg rest : sourceAstLambda (sourceProgramArgName arg 0 body) + (sourceProgramPlainArgs rest body)) + args + +sourceProgramLowerDefinition name args result body = + lazyBool + (_ : lazyBool + (_ : sourceAstLocal + (sourceToken sourceTokenName + (append "_" (append (sourceTokenPayload name) "_raw")) + (sourceTokenStart name) (sourceTokenEnd name)) + body + (sourceProgramWrapArgs args + (sourceProgramReturn result + (sourceProgramRawApply args + (sourceProgramName + (append "_" (append (sourceTokenPayload name) "_raw")) + body)))) + (sourceTokenStart name) (sourceAstEnd body)) + (_ : sourceProgramWrapArgs args (sourceProgramReturn result body)) + (sourceProgramAnyPhantom? args)) + (_ : sourceProgramPlainArgs args body) + (or? (sourceProgramAnyContract? args) + (lazyMaybe (_ : false) (contract : true) result)) + +sourceProgramParseAnnotatedBinding input = + parserBind (sourceGateExpect sourceTokenName) (name : + parserBind (parserMany sourceProgramArgument) (args : + (remaining : lazyBool + (_ : parserThen (sourceGateExpect sourceTokenAssign) + (parserBind (parserOptional + (parserThen (sourceGateExpect sourceTokenAt) + sourceProgramContract)) (result : + parserMap (body : pair name + (sourceProgramLowerDefinition name args result body)) + (sourceProgramDefinitionValue (sourcePositionColumn (sourceTokenStart name))))) remaining) + (_ : parserReplyFail + (parserDiagnostic 47 "supported contract head" t + (sourceTokenStart name) t) remaining true false) + (or? (not? (sourceProgramAnyPhantom? args)) + (and? (sourceProgramAllPhantom? args) + (lte_? (length args) 2)))))) input + +-- Preserve the existing bounded annotated-head path. Plain heads now retain +-- located equations; annotated definitions cannot merge with those groups. +sourceProgramAnnotated? input = (lazyList (_ : false) + (token rest : let kind = sourceTokenKind token in (lazyBool (_ : true) + (_ : lazyBool (_ : lazyList (_ : false) + (next unused : equal? (sourceTokenKind next) sourceTokenAt) rest) + (_ : lazyBool (_ : false) (_ : sourceProgramAnnotated? rest) + (equal? kind sourceTokenNewline)) + (equal? kind sourceTokenAssign)) (equal? kind sourceTokenAt))) input) +sourceProgramParsePlainBinding input = (lazyList + (_ : sourceGateAtomError input) + (token rest : sourceEquationBinding + (sourceProgramDefinitionValue (sourcePositionColumn (sourceTokenStart token))) input) input) +sourceProgramParseDefinition input = + lazyBool + (_ : lazyBool (_ : sourceProgramParseAnnotatedBinding input) + (_ : sourceProgramParsePlainBinding input) (sourceProgramAnnotated? input)) + (_ : parserReplyFail + (parserDiagnostic 2 sourceTokenAssign t t t) input false false) + (sourceProgramDefinitionHead? input) + +sourceProgramParseTokens = + parserThen sourceProgramSkipNewlines + (parserBind + (parserMany + (parserBefore sourceProgramParseDefinition sourceProgramSkipNewlines)) + (definitions : parserBind + (input : lazyResult + (diagnostic rest : parserReplyFail diagnostic input false false) + (groups unused : parserReplyOk groups input false false) + (sourceEquationGroups definitions)) + (groups : parserBind + (parserOptional + (parserBefore sourceGateExpr sourceProgramSkipNewlines)) + (final : parserMap (_ : pair groups final) sourceGateEof)))) + +parseGateProgram = parserWithLexer lexGateSource sourceProgramParseTokens + diff --git a/test/fixtures/reference-source/2b971f5cfdfea12210f6ed1801a3660735e1da97d4d372cf751eb76879750991.tri b/test/fixtures/reference-source/2b971f5cfdfea12210f6ed1801a3660735e1da97d4d372cf751eb76879750991.tri @@ -0,0 +1,22 @@ +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main + +-- One child reads a bounded line; only the parent writes. Prompt availability +-- does not require input first. EOF is not an empty name, CR is not stripped, +-- and neither sleep nor scheduler timing is used to coordinate the prompt. +name reader = IO.require (Text.readLine (Text.lineCursor reader) 4096) (reply : case reply of + [["line" text _] _] = IO.pure (B.append "Hello, " (B.append text "!\n")) + [["eof"] _] = IO.pure "End of input.\n" + _ = IO.fail "Invalid line reply") +main = IO.io (IO.scope (_ : do IO.require + reader <- IO.stdin + writer <- IO.stdout + spawned <- IO.spawn 0 1 [] (_ : name reader) + Text.writeText writer "Please enter your first name: \n(this printed before you typed anything)\n(this second line also printed before you typed anything)\n" + case spawned of + [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" greeting] = IO.require (Text.writeText writer greeting) IO.pure + _ = IO.fail outcome) + _ = IO.fail "Invalid name task")) diff --git a/test/fixtures/reference-source/369b3d37d4704c1c28f038df3392572ab906c11bbdde16710f97b33fdc3e54fd.tri b/test/fixtures/reference-source/369b3d37d4704c1c28f038df3392572ab906c11bbdde16710f97b33fdc3e54fd.tri @@ -0,0 +1,168 @@ +!import base as B +!import io as IO +!import io_v2 as V2 +!export pure +!export bind +!export request +!export checkedRequest +!export handle +!export forward +!export label +!export fail +!export liftIO +!export runPure +!export runIO +!export collect +!export view +!export diagnostic +!export scopeV2 +!export spawnV2 +!export runIOV2 + +-- Request/response effects, separate from both host IO and base.exceptE. +-- Handler callbacks return computations yielding one reply, not resumptions. +pure value = B.pair 100 value +bind action next = B.pair 101 (B.pair action next) +request service operation argument = B.pair 102 (B.pair [service operation argument] pure) +handle service handler action = B.pair 104 (B.pair [service handler] action) +label name action = B.pair 105 (B.pair [name] action) +fail kind service operation detail = B.pair 103 [kind service operation detail] + +-- Handlers execute outside their own installation scope, so forwarding is an +-- ordinary request there. At other call sites this is just request, not a way +-- to bypass the current environment. Names are routing values, not authority. +forward = request +liftIO action = request "host.io" "run" action + +-- Boundary constructors, not host.io requests: normalization must snapshot the +-- actual lexical environment, including when invoked inside a handler callback. +-- Child frames start empty; immutable routing and label values are inherited. +scopeV2 body = B.pair 107 body +spawnV2 supervised keep handles body = B.pair 108 [supervised keep handles body] + +-- Diagnostics are data: kind, service, operation, detail, outer-to-inner labels. +diagnostic kind service operation detail labels = [kind service operation detail labels] + +checkedRequest service operation inputC outputC argument = + case B.checkContract inputC argument of + ((t t), (checked, t)) = do bind + reply <- request service operation checked + case B.checkContract outputC reply of + ((t t), (value, t)) = pure value + (t, (message, _)) = fail "invalid-response" service operation message + _ = fail "invalid-response-contract" service operation reply + (t, (message, _)) = fail "invalid-request" service operation message + _ = fail "invalid-request-contract" service operation argument + +findHandler wanted [[service handler] | outer] = B.lazyBool + (_ : B.pair handler outer) + (_ : findHandler wanted outer) + (B.equal? wanted service) +findHandler _ _ = t + +-- Normalized views: 100 value; 103 complete diagnostic; 106 ([service op arg], +-- outer-to-inner labels, reply continuation). View stops at the first request. +-- Frames retain caller environments and labels; handlers use the environment +-- suffix outside their installation, but keep the requesting execution's labels. +normalize action environment frames labels = let + broken = B.pair 103 (diagnostic "invalid-computation" t t t labels) + resume value [] = pure value + -- Force the popped stack tail's outer constructor before reusing it. A lazy + -- projection here can keep consumed frames (and their old chunks) reachable. + resume value [[saved savedLabels next] | rest] = case rest of + [] = normalize (next value) saved [] savedLabels + more{[_ | _]} = normalize (next value) saved more savedLabels + _ = broken + resume _ _ = broken + dispatch event{[service operation argument]} next = case findHandler service environment of + (handler, outer) = normalize (handler operation argument) outer + (B.pair [environment labels next] frames) labels + _ = B.pair 106 [event labels (reply : normalize (next reply) environment frames labels)] + dispatch _ _ = broken + in case action of + (100, value) = resume value frames + (101, (first, next)) = normalize first environment (B.pair [environment labels next] frames) labels + (102, (event, next)) = dispatch event next + (103, [kind service operation detail]) = B.pair 103 (diagnostic kind service operation detail labels) + (104, (entry{[service handler]}, body)) = normalize body (B.pair entry environment) + (B.pair [environment labels pure] frames) labels + (105, (extraLabels, body)) = normalize body environment + (B.pair [environment labels pure] frames) (B.append labels extraLabels) + (107, body) = B.pair 107 [labels + (owner : normalize (body owner) environment [] labels) + (reply : resume reply frames)] + (108, [supervised keep handles body]) = B.pair 108 [supervised keep handles labels + (fresh : normalize (body fresh) environment [] labels) + (reply : resume reply frames)] + _ = broken + +view action = normalize action [] [] [] + +-- Execution Result is distinct from an operation's ordinary Result value. +-- Pure interpretation rejects every unhandled request, including host IO. +runPure action = case view action of + (100, value) = B.ok value t + (103, problem) = B.err problem t + (106, [[service operation argument] labels next]) = B.err + (diagnostic "unhandled-request" service operation argument labels) t + _ = B.err (diagnostic "invalid-view" t t t []) t + +-- Diagnostic labels are normally unused on successful IO. Settle their list +-- spine at each IO boundary so frame projections do not retain request history. +-- Label payloads and continuations remain lazy; do not normalize them as data. +runHost current = case current of + (100, value) = IO.pure (B.ok value t) + (103, problem) = IO.pure (B.err problem t) + (106, [["host.io" "run" action] labels next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = IO.bind action (reply : runHost (next reply)) + _ = IO.pure (B.err (diagnostic "invalid-view" t t t []) t) + (106, [[service operation argument] labels next]) = IO.pure <| B.err + (diagnostic "unhandled-request" service operation argument labels) t + _ = IO.pure (B.err (diagnostic "invalid-view" t t t []) t) +runIO action = runHost (view action) + +-- Unlike the v1 execution-Result runner, this runner maps handler failures to +-- terminal v2 outcomes. Ordinary service Results remain ordinary values. A +-- supervisor observes terminal child outcomes through the host join operation. +runHostV2 current = case current of + (100, value) = V2.pure value + (103, problem) = V2.fail problem + (106, [["host.io" "run" action] labels next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind action (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + (106, [[service operation argument] labels next]) = V2.fail + (diagnostic "unhandled-request" service operation argument labels) + (107, [labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind (V2.scope (owner : runHostV2 (body owner))) + (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + (108, [supervised keep handles labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind (V2.spawn supervised keep handles (fresh : runHostV2 (body fresh))) + (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + _ = V2.fail (diagnostic "invalid-view" t t t []) +runIOV2 action = runHostV2 (view action) + +-- Stateful collection as a boundary transformer, not mutable handler state. +-- Inner handlers run first. Other residual requests are relayed to outer +-- handlers, with their labels scoped ONLY around the forwarded request. +-- It returns [value chronological-events]. Failures retain their labels. +collect service operation argumentC reply action = let rec + walk current reversed = case current of + (100, value) = pure [value (B.reverse reversed)] + (103, [kind failedService failedOperation detail labels]) = B.pair 105 + (B.pair labels (fail kind failedService failedOperation detail)) + (106, [event{[wanted op argument]} labels next]) = B.lazyBool + (_ : case B.checkContract argumentC argument of + ((t t), (checked, t)) = walk (next reply) (B.pair checked reversed) + (t, (message, _)) = B.pair 105 (B.pair labels (fail "invalid-request" service operation message)) + _ = B.pair 105 (B.pair labels (fail "invalid-request-contract" service operation argument))) + (_ : bind (B.pair 105 (B.pair labels (request wanted op argument))) + (answer : walk (next answer) reversed)) + (B.and? (B.equal? wanted service) (B.equal? op operation)) + (107, [labels body next]) = B.pair 105 (B.pair labels + (fail "state-boundary" service operation "Install collection inside the scoped body")) + (108, [supervised keep handles labels body next]) = B.pair 105 (B.pair labels + (fail "state-boundary" service operation "Install child-local collection explicitly")) + _ = fail "invalid-view" t t t + in walk (view action) [] diff --git a/test/fixtures/reference-source/37cfec8f76370ad98a5d8a0b1a3d36f9af5b0597fedf996f6ed29e2c94c0358c.tri b/test/fixtures/reference-source/37cfec8f76370ad98a5d8a0b1a3d36f9af5b0597fedf996f6ed29e2c94c0358c.tri @@ -0,0 +1,192 @@ +!import base * +!export * + +-- IO constructors for host-interpreted interaction trees. +-- Free-monad style: Bind is the single sequencing mechanism. + +version = 1 + +io = action : pair "tricuIO" (pair version action) + +pure = x : pair 0 x +bind = action k : pair 1 (pair action k) +-- Terminal diagnostic control for the Haskell IO scheduler. +abortIO = diagnostic : pair 2 diagnostic + +putStr = s : pair 10 s +getLine = pair 11 t + +-- Source boundary only: Result (raw octets, Unicode scalar list), strict UTF-8. +-- Initial BOM is rejected; binary readFile remains unchanged. +readSourceFile = p : pair 37 p +encodeSourceText = chars : pair 38 chars + +readFile = p : pair 20 p +writeFile = p c : pair 21 (pair p c) +putBytes = bs : pair 12 bs +writeBytes = p c : pair 22 (pair p c) + +listDirectory = p : pair 23 p +renameFile = old new : pair 24 (pair old new) +createDirectory = p : pair 25 p +deleteFile = p : pair 26 p +fileExists = p : pair 27 p + +sha256Hex = bs : pair 28 bs +currentTime = pair 29 t +-- Filesystem-only atomic primitives. writeBytesIfAbsent returns ok true +-- for a new object and ok false if the path already exists, without changing +-- it. atomicWriteBytes returns ok true after atomic replacement of an alias. +-- Hosts sync file contents before publication and the parent directory +-- afterwards. Publish referenced objects before replacing a lock or alias; +-- a post-publication sync failure can leave the new name visible. +writeBytesIfAbsent = p bs : pair 32 (pair p bs) +atomicWriteBytes = p bs : pair 33 (pair p bs) + +-- Read-permitted canonical tree-term transport. Verify the fixed +-- arboricx.tree-term.v1 digest before decoding; returns Result tree. +-- This loads an inert normal-form tree, never source or an applied program. +readTreeTerm = p digest : pair 34 (pair p digest) + +-- Fixed canonical tree-term domain. No byte-list marshalling. Publication +-- requires read and write permission, never replaces an existing path, and +-- verifies its bytes even when another publisher wins. Directories are explicit. +hashTreeTerm = tree : pair 35 tree +writeTreeTermIfAbsent = p digest tree : pair 36 (pair p (pair digest tree)) + +-- Name-neutral indexed DAG transport under arboricx.indexed-tree.v1. +-- These primitive codecs preserve sharing; tricu chooses paths and publication +-- order. Read verifies before decode; write verifies an existing/racing winner. +-- Both read and write permission are required for no-replace publication. +readIndexedTree = p digest : pair 50 (pair p digest) +hashIndexedTree = tree : pair 51 tree +writeIndexedTreeIfAbsent = p digest tree : pair 52 (pair p (pair digest tree)) + +-- Experimental Haskell-native bounded staging sessions. All return Results. +-- Run-local, task-owned identifiers are not independent filesystem authority. +uploadBegin directory expected = pair 90 (pair directory expected) +uploadAppend handle chunk = pair 91 (pair handle chunk) +uploadSeal handle = pair 92 handle +uploadCommit handle destination = pair 93 (pair handle destination) +uploadAbort handle = pair 94 handle + +ask = pair 30 t +local = f action : pair 31 (pair f action) + +get = pair 40 t +put = s : pair 41 s + +fork = action : pair 60 action +await = handle : pair 61 handle +yield = pair 62 t +sleep = ms : pair 63 ms + +-- --------------------------------------------------------------------------- +-- Generic sequencing combinators +-- --------------------------------------------------------------------------- + +thenIO = a b : bind a (_ : b) +mapIO = action f : bind action (x : pure (f x)) +void = action : bind action (_ : pure t) + +-- --------------------------------------------------------------------------- +-- Conditional execution +-- --------------------------------------------------------------------------- + +when = cond action : matchBool action (pure t) cond +unless = cond action : matchBool (pure t) action cond + +-- --------------------------------------------------------------------------- +-- Infinite loop +-- --------------------------------------------------------------------------- + +forever action = bind action (_ : forever action) + +-- --------------------------------------------------------------------------- +-- Result-aware combinators +-- --------------------------------------------------------------------------- + +-- Propagate driver Result on error; run okCase on success. +onOk action okCase = (do bind + reply <- action + matchResult (_ _ : pure reply) okCase reply) + +-- Value-only adapter: success remainder is explicitly discarded. +onOk_ action okCase = onOk action (value _ : okCase value) + +onResult action errCase okCase = (do bind + reply <- action + matchResult errCase okCase reply) + +-- Value-only handlers explicitly discard both Result remainders. +onResult_ action errCase okCase = (onResult action + (message _ : errCase message) + (value _ : okCase value)) + +mapErrIO prefix action = + onResult_ action + (e : pure (err (append prefix e) t)) + (v : pure (ok v t)) + +-- --------------------------------------------------------------------------- +-- Convenience helpers +-- --------------------------------------------------------------------------- + +print = s : void (putStr s) +putStrLn = s : void (putStr (append s "\n")) + +-- --------------------------------------------------------------------------- +-- Result-aware file helpers +-- --------------------------------------------------------------------------- + +onReadFile = path : onResult (readFile path) + +onWriteFile = path contents : onResult (writeFile path contents) + +onListDirectory = path : onResult (listDirectory path) +onRenameFile = old new : onResult (renameFile old new) +onCreateDirectory = path : onResult (createDirectory path) +onDeleteFile = path : onResult (deleteFile path) +onFileExists = path : onResult (fileExists path) +onSha256Hex = bs : onResult (sha256Hex bs) +onCurrentTime = onResult currentTime + +-- --------------------------------------------------------------------------- +-- Convenience helpers for the common cases +-- --------------------------------------------------------------------------- + +readFileOrPrintError = (path okCase : + onReadFile path + (err rest : putStrLn (append "Read failed: " err)) + okCase) + +writeFileOrPrintError = (path contents okCase : + onWriteFile path contents + (err rest : putStrLn (append "Write failed: " err)) + okCase) + +copyFile = (src dst : + onResult (readFile src) + (err rest : putStrLn (append "Read failed: " err)) + (contents rest : + onResult (writeFile dst contents) + (err rest : putStrLn (append "Write failed: " err)) + (_ _ : pure t))) + +-- --------------------------------------------------------------------------- +-- Ordinary-completion cleanup (not exception masking or abort recovery) +-- --------------------------------------------------------------------------- + +-- Run cleanup once after action returns any ordinary value/Result, including +-- failure; ignore cleanup's ordinary result and preserve the action's result. +-- Terminal abortIO, divergence and host exceptions can prevent cleanup. +finally action cleanup = (do bind + result <- action + cleanup + pure result) + +-- Acquisition returns a Result. Failure propagates unchanged without use or +-- release; success supplies the resource value (not the Result envelope). +-- Use the same ordinary-completion/cleanup-result policy as finally. +bracket acquire release use = (onOk_ acquire + (resource : finally (use resource) (release resource))) diff --git a/test/fixtures/reference-source/386257391872e7b73befc2bcc3634706acf4acf0ff791bc60161179434c9a813.tri b/test/fixtures/reference-source/386257391872e7b73befc2bcc3634706acf4acf0ff791bc60161179434c9a813.tri @@ -0,0 +1,31 @@ +!import base as B +!import conversions as C +!import native_math as N +!import io_v2 as IO +!import io_v2_http as Transport +!import http as HTTP +!export main +!export server +-- Sequential example, not the registry's bounded concurrent admission policy. +router "GET" path _ _ = IO.pure (HTTP.okResponse (B.append "Hello from " (B.append path "\n"))) +router _ _ _ _ = IO.pure (HTTP.errorResponse 405 "Method not allowed\n") +port ["ipv4" _ value] = value +port _ = 0 +ready listener = do IO.require + address <- IO.localAddress listener + output <- IO.stdout + IO.require (IO.writeAll output (B.append (C.showNumber (port address)) [10])) IO.pure +connection client = IO.bind (Transport.recvHeaders client) (reply : case reply of + ((t t), (bytes, t)) = Transport.handle router client (HTTP.parseRequestLine bytes) + (t, (431, t)) = Transport.respond client "" (HTTP.errorResponse 431 "Request Header Fields Too Large\n") + _ = Transport.respond client "" (HTTP.badRequestResponse "Bad Request\n")) +once listener = IO.scope (owner : IO.require (IO.acceptTCP listener owner) connection) +serve 0 _ = IO.pure t +serve count listener = IO.bind (once listener) (_ : serve (N.predecessor count) listener) +forever listener = IO.bind (once listener) (_ : forever listener) +server address count = IO.io (IO.scope (owner : do IO.require + listener <- IO.listenTCP owner address 5 + IO.bind (ready listener) (_ : serve count listener))) +main = IO.io (IO.scope (owner : do IO.require + listener <- IO.listenTCP owner (IO.ipv4 [127 0 0 1] 9050) 5 + IO.bind (ready listener) (_ : forever listener))) diff --git a/test/fixtures/reference-source/3bfd3a8ea35a6316e1ef9cc53ac2418a9dbd6115c69d129a1b2de81c87877e7a.tri b/test/fixtures/reference-source/3bfd3a8ea35a6316e1ef9cc53ac2418a9dbd6115c69d129a1b2de81c87877e7a.tri @@ -0,0 +1,342 @@ +!import prelude * +!import compiler_io * +!import source_object * +!import source_module * +!import source_lock * +!import source_bundle * +!import source_resolve * +!import source_unit * +!export * + +-- Local imports/re-exports acquire workspaceRoot/NAME.tri; !source is build-only. +-- Other profiles supply read/compile steps over the same graph and receipts. +-- References resolve only through selected locals or immutable full hashes. +sourceBuildSourcePath workspace name = + append workspace (append "/" (append name ".tri")) +sourceBuildContains names name = + lazyList + (_ : false) + (item rest : lazyBool + (_ : true) + (_ : sourceBuildContains rest name) + (equal? item name)) + names +-- Graph acquisition and binding use the same interpreter as module compilation. +sourceBuildStage source = (bindResult (sourceModuleHeaderStage source) + (staged _ : ok (pair (fst staged) source) t)) + +-- Lock rewriting is reused as a pure immutable-ref substitution, not as a +-- lockfile read. The groups below are selected from this build's local graph +-- or explicit full hashes; no moving module alias is consulted. +-- Per-graph memo = (completed locals, authenticated manifests by full hash). +-- It starts empty, belongs to one root/graph invocation, and never stores errors +-- or mutable aliases. CAS manifests are immutable during a build, just as local +-- graph memoization already requires; a new invocation reauthenticates them. +-- Entries enter only through sourceModuleGetManifest, including canonical bytes, +-- digest, export names, tree kind and ABI validation. Tree reads remain verified. +sourceBuildGetManifest root hash memo = (lazyMaybe + (_ : onResult_ (sourceModuleGetManifest root hash) + (problem : pure (err problem t)) + (entries : pure (ok (pair entries (pair (fst memo) + (pair (pair hash entries) (snd memo)))) t))) + (entries : pure (ok (pair entries memo) t)) + (sourceFindExternal (snd memo) hash)) +sourceBuildDone name manifest receipt memo = + pure (ok (pair (pair manifest receipt) + (pair (pair (pair name (pair manifest receipt)) (fst memo)) (snd memo))) t) + +sourceBuildResolveRefs refs root groups memo = + lazyList + (_ : pure (ok (pair groups memo) t)) + (ref rest : lazyMaybe + (_ : lazyBool + (_ : onResult_ (sourceBuildGetManifest root ref memo) + (problem : pure (err problem t)) + (loaded : sourceBuildResolveRefs rest root (pair + (pair ref (pair ref (fst loaded))) groups) (snd loaded))) + (_ : pure (err "undeclared local source reference" t)) + (sourceObjectHashValid ref)) + (_ : sourceBuildResolveRefs rest root groups memo) + (sourceFindExternal groups ref)) + refs +sourceBuildGroupsFromLocals locals root groups memo = + lazyList + (_ : pure (ok (pair (reverse groups) memo) t)) + (dep rest : onResult_ (sourceBuildGetManifest root + (fst (snd dep)) memo) + (problem : pure (err problem t)) + (loaded : sourceBuildGroupsFromLocals rest root (pair + (pair (fst dep) (pair (fst (snd dep)) (fst loaded))) groups) (snd loaded))) + locals + +-- Rewritten headers contain immutable hashes. Resolve only from the graph's +-- authenticated groups; do not reread aliases or reparse the same manifest. +sourceBuildGroupManifest groups hash = (lazyList + (_ : pure (err "unresolved build manifest" t)) + (group rest : lazyBool + (_ : pure (ok (snd (snd group)) t)) + (_ : sourceBuildGroupManifest rest hash) + (equal? (fst (snd group)) hash)) + groups) +sourceBuildLocalRows locals = + lazyList + (_ : t) + (dep rest : append "local\t" + (append (fst dep) (append "\t" + (append (fst (snd dep)) (append "\t" + (append (snd (snd dep)) + (append "\n" (sourceBuildLocalRows rest)))))))) + locals +sourceBuildPinRows exports ref manifest = + lazyList + (_ : t) + (entry rest : append "pin\t" + (append ref (append "\t" + (append manifest (append "\t" + (append (sourceModuleEscape (fst entry)) + (append "\t" (append (snd entry) + (append "\n" (sourceBuildPinRows rest ref manifest)))))))))) + exports +sourceBuildAllPinRows groups = + lazyList + (_ : t) + (group rest : append + (sourceBuildPinRows (snd (snd group)) (fst group) + (fst (snd group))) + (sourceBuildAllPinRows rest)) + groups +sourceBuildReceiptBytes sourceDigest compilerDigest optionsDigest manifestHash locals groups = + append "arboricx.build-receipt.v1\n" + (append "source\t" (append sourceDigest + (append "\ncompiler\t" (append compilerDigest + (append "\noptions\t" (append optionsDigest + (append "\noutput\t" (append manifestHash + (append "\n" (append (sourceBuildLocalRows locals) + (sourceBuildAllPinRows groups))))))))))) +sourceBuildPutReceipt root source compiler optionsDigest manifestHash locals groups = + onOk_ (sourceObjectDigest "arboricx.source.v1" source) (sourceDigest : + sourceObjectStore root "arboricx.build-receipt.v1" + (sourceBuildReceiptBytes sourceDigest compiler optionsDigest + manifestHash locals groups)) + +-- A mutable local index is only an acceleration hint. CAS manifests, receipts, +-- and tree objects remain the authoritative outputs. The key fixes the staged +-- source, declared compiler/options, ordered local receipts, and exact import +-- pins. A changed input must miss even if the old checkpoint still exists. +sourceBuildCacheKeyBytes sourceHash compiler options locals groups = + append "arboricx.source-build-key.v1\nsource\t" (append sourceHash + (append "\ncompiler\t" (append compiler + (append "\noptions\t" (append options + (append "\n" (append (sourceBuildLocalRows locals) + (sourceBuildAllPinRows groups)))))))) +sourceBuildCachePath root name = + append root (append "/cache/source-build/" name) +sourceBuildCacheBytes key manifest receipt = + append "arboricx.source-build-cache.v1\t" (append key + (append "\t" (append manifest (append "\t" receipt)))) +sourceBuildCacheDecode bytes = + lazyList + (_ : err "invalid build cache checkpoint" t) + (version rest1 : lazyList + (_ : err "invalid build cache checkpoint" t) + (key rest2 : lazyList + (_ : err "invalid build cache checkpoint" t) + (manifest rest3 : lazyList + (_ : err "invalid build cache checkpoint" t) + (receipt extra : lazyBool + (_ : ok (pair key (pair manifest receipt)) t) + (_ : err "invalid build cache checkpoint" t) + (and? (equal? version "arboricx.source-build-cache.v1") + (and? (emptyList? extra) + (and? (sourceObjectHashValid key) + (and? (sourceObjectHashValid manifest) + (and? (sourceObjectHashValid receipt) + (equal? bytes (sourceBuildCacheBytes key manifest receipt)))))))) + rest3) + rest2) + rest1) + (splitOnByte 9 bytes) +sourceBuildCachePublish root name key manifest receipt = + onOk_ (createDirectory root) (_ : + onOk_ (createDirectory (append root "/cache")) (_ : + onOk_ (createDirectory (append root "/cache/source-build")) (_ : + atomicWriteBytes (sourceBuildCachePath root name) + (sourceBuildCacheBytes key manifest receipt)))) + +sourceBuildVerifyGroups groups root locals = + lazyList + (_ : pure (ok t t)) + (group rest : lazyMaybe + (_ : onOk_ (sourceLockCheckTrees root (snd (snd group))) + (_ : sourceBuildVerifyGroups rest root locals)) + (_ : sourceBuildVerifyGroups rest root locals) + (sourceFindExternal locals (fst group))) + groups +sourceBuildCacheVerifyGroups root groups locals = + sourceBuildVerifyGroups groups root locals + +-- A checkpoint whose key matches current inputs must be complete and valid. +-- Local dependencies were verified by the graph walker before this lookup; +-- check full-hash external imports here as well as this unit's own exports. +-- An unused imported tree may be corrupted even when the output still exists. +-- Reject corruption instead of silently rebuilding and concealing it. +sourceBuildCacheUse root sourceHash options compiler locals groups memo name entry = + onResult_ (sourceObjectGet root "arboricx.build-receipt.v1" + (snd (snd entry))) + (problem : pure (err problem t)) + (receiptBytes : lazyBool + (_ : onOk_ (sourceBuildCacheVerifyGroups root groups locals) (_ : + onResult_ (sourceBuildGetManifest root (fst (snd entry)) memo) + (problem : pure (err problem t)) + (loaded : onOk_ (sourceLockCheckTrees root (fst loaded)) (_ : + sourceBuildDone name (fst (snd entry)) (snd (snd entry)) (snd loaded))))) + (_ : pure (err "build cache receipt mismatch" t)) + (equal? receiptBytes + (sourceBuildReceiptBytes sourceHash compiler options + (fst (snd entry)) locals groups))) + +sourceBuildCached root source unit compiler locals groups memo name build optionsName = + onOk_ (sourceObjectDigest "arboricx.source.v1" source) (sourceHash : + onOk_ (sourceObjectDigest "arboricx.build-options.v1" + optionsName) (options : + onOk_ (sourceObjectDigest "arboricx.source-build-key.v1" + (sourceBuildCacheKeyBytes sourceHash compiler options locals groups)) + (key : onResult_ (fileExists (sourceBuildCachePath root name)) + (problem : pure (err problem t)) + (exists : lazyBool + (_ : onResult_ (readFile (sourceBuildCachePath root name)) + (problem : pure (err problem t)) + (bytes : lazyResult + (problem unused : pure (err problem t)) + (entry unused : lazyBool + (_ : sourceBuildCacheUse root sourceHash options compiler + locals groups memo name entry) + (_ : sourceBuildCompile root source unit compiler locals + groups memo name key build options) + (equal? (fst entry) key)) + (sourceBuildCacheDecode bytes))) + (_ : sourceBuildCompile root source unit compiler locals + groups memo name key build options) + exists)))) + +sourceBuildCompile root source unit compiler locals groups memo name key build options = + onResult_ (build root unit groups) + (problem : pure (err problem t)) + (manifestHash : onResult_ (sourceBuildPutReceipt root source + compiler options manifestHash locals groups) + (problem : pure (err problem t)) + (receiptHash : onOk_ + (sourceBuildCachePublish root name key manifestHash receiptHash) + (_ : onResult_ (sourceBuildGetManifest root manifestHash memo) + (problem : pure (err problem t)) + (loaded : sourceBuildDone name manifestHash receiptHash (snd loaded))))) +sourceBuildFinish root source unit refs compiler locals memo name build options = + onResult_ (sourceBuildGroupsFromLocals locals root t memo) + (problem : pure (err problem t)) + (localGroups : onResult_ (sourceBuildResolveRefs refs root + (fst localGroups) (snd localGroups)) + (problem : pure (err problem t)) + (resolved : sourceBuildCached root source unit compiler + locals (fst resolved) (snd resolved) name build options)) + +-- The recursive walker is explicitly passed to the dependency-list helper: +-- its callback keeps the graph walk direct-self-recursive, not mutual. +-- The walker uses (read/stage, (compile, unit -> options bytes)); the public +-- compatibility API adapts constant options names. The shared walker owns +-- dependencies, immutable pins, receipts, checkpoints and cycles. +-- read/stage returns (fingerprint bytes, (dependencies, (refs, unit))). +sourceBuildReadTriPath path = + onResult_ (readSourceFile path) + (problem : pure (err problem t)) + (source : lazyResult + (problem unused : pure (err problem t)) + (staged unused : lazyResult + (problem unused : pure (err problem t)) + (refs unused : pure (ok (pair (fst source) + (pair (fst staged) (pair refs (snd staged)))) t)) + (sourceLockSourceRefs (snd staged))) + (sourceBuildStage (snd source))) +sourceBuildReadTri workspace name = + sourceBuildReadTriPath (sourceBuildSourcePath workspace name) +sourceBuildCompileTriWith compileDefinitions root source groups = (lazyResult + (problem unused : pure (err problem t)) + (staged unused : sourceModuleBuildResolvedWith (sourceBuildGroupManifest groups) compileDefinitions root staged) + (sourceLockRewriteStaged source groups)) + +sourceBuildCompileTri root clean groups = + sourceBuildCompileTriWith compileModuleDefinitions root clean groups +sourceBuildStableTriProfile = + pair (workspace name : sourceBuildReadTri workspace name) + (pair (root unit groups : sourceBuildCompileTriWith compileStableModuleDefinitions root unit groups) + "source-module.stable.v1") +sourceBuildTriProfile = + pair (workspace name : sourceBuildReadTri workspace name) + (pair (root unit groups : sourceBuildCompileTri root unit groups) + "source-module.reference.v1") +sourceBuildDependencies deps walk workspace root compiler stack memo profile locals = + lazyList + (_ : pure (ok (pair (reverse locals) memo) t)) + (name rest : onResult_ (walk name workspace root compiler stack memo profile) + (problem : pure (err problem t)) + (out : sourceBuildDependencies rest walk workspace root compiler stack (snd out) profile + (pair (pair name (fst out)) locals))) + deps +sourceBuildWalk name workspace root compiler stack memo profile = + lazyBool + (_ : lazyMaybe + (_ : lazyMaybe + (_ : onResult_ ((fst profile) workspace name) + (problem : pure (err problem t)) + (staged : onResult_ + (sourceBuildDependencies (fst (snd staged)) sourceBuildWalk workspace root compiler + (pair (pair name t) stack) memo profile t) + (problem : pure (err problem t)) + (built : sourceBuildFinish root (fst staged) + (snd (snd (snd staged))) (fst (snd (snd staged))) + compiler (fst built) (snd built) name + (fst (snd profile)) + ((snd (snd profile)) (snd (snd (snd staged))))))) + (_ : pure (err "local-source dependency cycle" t)) + (sourceFindExternal stack name)) + (cached : pure (ok (pair cached memo) t)) + (sourceFindExternal (fst memo) name)) + (_ : pure (err "invalid local source name" t)) + (sourceModuleAliasValid name) + +-- Unit-sensitive options select cache/receipt identity, not graph policy. +-- Profile = (read/stage, (compile, unit -> options bytes)). This permits +-- explicit mixed frontend/path selection without changing source identity. +-- Returns (moduleManifestHash, buildReceiptHash). Compiler identity remains +-- runner-declared reproducibility metadata, not proof. +sourceBuildGraphUnitProfile workspace root compilerDigest name profile = + lazyBool + (_ : onResult_ (sourceBuildWalk name workspace root compilerDigest t (pair t t) + profile) + (problem : pure (err problem t)) + (output : pure (ok (fst output) t))) + (_ : pure (err "invalid compiler artifact digest" t)) + (sourceObjectHashValid compilerDigest) +-- Compatibility adapter: existing profiles keep their constant options name. +sourceBuildGraphProfile workspace root compilerDigest name profile = + (sourceBuildGraphUnitProfile workspace root compilerDigest name + (pair (workspace name : (fst profile) workspace name) + (pair (root unit groups : (fst (snd profile)) root unit groups) + (unit : snd (snd profile))))) +sourceBuildGraph workspace root compilerDigest name = + sourceBuildGraphProfile workspace root compilerDigest name sourceBuildTriProfile + +-- One portable IO action drives local dependency builds, their separate +-- receipts, immutable manifest selection, and the final indexed bundle. +-- Returns (manifestHash, (receiptHash, bundleFileHash)). +sourceBuildGraphBundle workspace root compilerDigest name outputPath = + onResult_ (sourceBuildGraph workspace root compilerDigest name) + (problem : pure (err problem t)) + (built : onResult_ (sourceBundlePack root (fst built) outputPath) + (problem : pure (err problem t)) + (bundleHash : pure (ok (pair (fst built) + (pair (snd built) bundleHash)) t))) + +-- Same graph/IO/publication ABI; distinct backend options and artifact identity. +sourceBuildGraphStable workspace root compilerDigest name = + sourceBuildGraphProfile workspace root compilerDigest name sourceBuildStableTriProfile + diff --git a/test/fixtures/reference-source/40783b2c9c5750583151dda9d04b435d3d0d8a63b6f33cccfa94fd8ed958c14a.tri b/test/fixtures/reference-source/40783b2c9c5750583151dda9d04b435d3d0d8a63b6f33cccfa94fd8ed958c14a.tri @@ -0,0 +1,6 @@ +!import io_v2 as IO +!import io_v2_text as Text +!export main +-- Explicit v2 output, with ordinary write failure checked before success. +main = IO.io (IO.require IO.stdout (writer : + IO.require (Text.writeText writer "Hello, tricu\n") (_ : IO.pure ""))) diff --git a/test/fixtures/reference-source/49be76ef3607cedcafd4e6a599b90a7f0601b5d030e1431011e879342c9b0e91.tri b/test/fixtures/reference-source/49be76ef3607cedcafd4e6a599b90a7f0601b5d030e1431011e879342c9b0e91.tri @@ -0,0 +1,567 @@ +!import prelude * +!import compiler_io * +!import source_object * +!import source_unit * +!import source_resolve * +!import source_lexer * +!import parser * +!export * + +-- Initial executable-only Arboricx manifest: exact existing Haskell v1 +-- encoding with no contract fields. A row is name -> indexed executable object. +sourceModuleHeader = "arboricx.module-manifest.v1\n" +sourceModuleEscapeByte byte = + matchBool "%25" + (matchBool "%09" + (matchBool "%0A" (matchBool "%0D" (pair byte t) + (equal? byte 13)) (equal? byte 10)) (equal? byte 9)) + (equal? byte 37) +sourceModuleEscape bytes = + lazyList + (_ : t) + (byte rest : append (sourceModuleEscapeByte byte) (sourceModuleEscape rest)) + bytes +sourceModuleUnescapeCode a b = + matchBool (ok 37 t) + (matchBool (ok 9 t) + (matchBool (ok 10 t) (matchBool (ok 13 t) + (err "invalid manifest escape" t) + (and? (equal? a 48) (equal? b 68))) + (and? (equal? a 48) (equal? b 65))) + (and? (equal? a 48) (equal? b 57))) + (and? (equal? a 50) (equal? b 53)) +sourceModuleUnescape bytes = + lazyList + (_ : ok t t) + (byte rest : lazyBool + (_ : lazyList + (_ : err "truncated manifest escape" t) + (a tail : lazyList + (_ : err "truncated manifest escape" t) + (b remaining : lazyResult + (problem unused : err problem t) + (decoded unused : lazyResult + (problem unused : err problem t) + (suffix unused : ok (pair decoded suffix) t) + (sourceModuleUnescape remaining)) + (sourceModuleUnescapeCode a b)) + tail) + rest) + (_ : lazyResult + (problem unused : err problem t) + (suffix unused : ok (pair byte suffix) t) + (sourceModuleUnescape rest)) + (equal? byte 37)) + bytes +sourceModuleRow name hash = + append "export\t" (append (sourceModuleEscape name) + (append "\t" (append sourceObjectTreeKind + (append "\t" (append hash "\tarboricx.abi.tree.v1\n"))))) +sourceModuleEncodeRows entries = + lazyList + (_ : t) + (entry rest : append (sourceModuleRow (fst entry) (snd entry)) (sourceModuleEncodeRows rest)) + entries +sourceModuleEncode entries = + append sourceModuleHeader (sourceModuleEncodeRows entries) + +-- Reject malformed/unrecognized rows and duplicate names. Re-encoding +-- validates exact canonical bytes, including trailing newline and row order. +sourceModuleRows rows seen = + lazyList + (_ : ok (reverse seen) t) + (row rest : + lazyList + (_ : lazyList + (_ : ok (reverse seen) t) + (_ _ : err "blank manifest row" t) + rest) + (_ _ : sourceModuleRowParse row rest seen sourceModuleRows) + row) + rows +sourceModuleRowParse row rest seen self = + lazyList + (_ : err "invalid module manifest row" t) + (a tail1 : lazyList + (_ : err "invalid module manifest row" t) + (encodedName tail2 : lazyResult + (problem unused : err problem t) + (name unused : lazyList + (_ : err "invalid module manifest row" t) + (kind tail3 : lazyList + (_ : err "invalid module manifest row" t) + (hash tail4 : lazyList + (_ : err "invalid module manifest row" t) + (abi extra : lazyBool + (_ : lazyBool + (_ : lazyBool + (_ : lazyBool + (_ : lazyMaybe + (_ : self rest (pair (pair name hash) seen)) + (_ : err "duplicate module export" t) + (sourceFindExternal seen name)) + (_ : err "unsupported export ABI" t) + (equal? abi sourceObjectTreeAbi)) + (_ : err "unsupported export kind" t) + (equal? kind sourceObjectTreeKind)) + (_ : err "invalid export name or digest" t) + (and? (sourceModuleExportNameValid name) + (sourceModuleDigest? hash))) + (_ : err "invalid module manifest row" t) + (emptyList? extra)) + tail4) + tail3) + tail2) + (sourceModuleUnescape encodedName)) + tail1) + (splitOnByte 9 row) +-- The row parser receives its visitor, avoiding a mutually recursive group. +sourceModuleDecode bytes = + lazyList + (_ : err "empty module manifest" t) + (header rows : lazyBool + (_ : lazyResult + (problem remaining : err problem remaining) + (exports remaining : lazyBool + (_ : ok exports t) + (_ : err "noncanonical module manifest" t) + (equal? bytes (sourceModuleEncode exports))) + (sourceModuleRows rows t)) + (_ : err "unsupported module manifest version" t) + (equal? header "arboricx.module-manifest.v1")) + (splitOnByte 10 bytes) + +sourceModuleEntriesValid entries seen = + lazyList + (_ : true) + (entry rest : andLazy? + (and? (sourceModuleExportNameValid (fst entry)) + (and? (sourceModuleDigest? (snd entry)) + (lazyMaybe true (_ : false) + (sourceFindExternal seen (fst entry))))) + (_ : sourceModuleEntriesValid rest (pair entry seen))) + entries +sourceModulePutManifest root entries = + lazyList + (_ : pure (err "module requires an export" t)) + (_ _ : lazyBool + (_ : sourceObjectStore root sourceObjectManifestDomain + (sourceModuleEncode entries)) + (_ : pure (err "invalid or duplicate module export" t)) + (sourceModuleEntriesValid entries t)) + entries +sourceModuleGetManifest root hash = + onResult_ (sourceObjectGet root sourceObjectManifestDomain hash) + (problem : pure (err problem t)) + (payload : pure (sourceModuleDecode payload)) + +-- Workspace module aliases use the pre-existing neutral alias file format. +-- A consuming source may instead put the full immutable manifest hash here. +-- A v1 name containing '%' must be escaped by the Haskell codec. This +-- bounded producer accepts unescaped ASCII identifiers only. +sourceModuleExportChar? byte = sourceNameContinue? byte +sourceModuleExportNameRest name = + lazyList + (_ : true) + (byte rest : andLazy? (sourceModuleExportChar? byte) + (_ : sourceModuleExportNameRest rest)) + name +sourceModuleExportNameValid name = + lazyList + (_ : false) + (first rest : andLazy? (sourceNameStart? first) + (_ : sourceModuleExportNameRest rest)) + name + +sourceModuleAliasByte? byte = + or? (sourceAsciiLetter? byte) + (or? (sourceAsciiDigit? byte) + (or? (equal? byte 95) (equal? byte 45))) +sourceModuleAliasTail bytes = + lazyList + (_ : true) + (byte rest : andLazy? (sourceModuleAliasByte? byte) + (_ : sourceModuleAliasTail rest)) + bytes +sourceModuleAliasValid alias = + lazyList + (_ : false) + (first rest : andLazy? (sourceNameStart? first) + (_ : sourceModuleAliasTail rest)) + alias +sourceModuleDigest? bytes = sourceObjectHashValid bytes +sourceModuleAliasPath root alias = + append root (append "/aliases/modules/" alias) +sourceModuleAliasBytes hash = + append "kind: arboricx.module-manifest.v1\nhash: " + (append hash "\n") +sourceModuleResolveAlias root alias = + lazyBool + (_ : onResult_ (readFile (sourceModuleAliasPath root alias)) + (problem : pure (err problem t)) + (bytes : sourceModuleParseAlias bytes)) + (_ : pure (err "invalid module alias" t)) + (sourceModuleAliasValid alias) +sourceModuleParseAlias bytes = + lazyList + (_ : pure (err "invalid module alias" t)) + (kind rest : lazyList + (_ : pure (err "invalid module alias" t)) + (line trailing : lazyBool + (_ : sourceModuleParseAliasHash bytes line) + (_ : pure (err "invalid module alias" t)) + (equal? kind "kind: arboricx.module-manifest.v1")) + rest) + (splitOnByte 10 bytes) +sourceModuleParseAliasHash bytes line = + lazyList + (_ : pure (err "invalid module alias" t)) + (key tail : lazyList + (_ : pure (err "invalid module alias" t)) + (hash extra : lazyBool + (_ : lazyBool + (_ : pure (ok hash t)) + (_ : pure (err "noncanonical module alias" t)) + (equal? bytes (sourceModuleAliasBytes hash))) + (_ : pure (err "invalid module alias hash" t)) + (and? (equal? key "hash:") + (and? (emptyList? extra) (sourceModuleDigest? hash)))) + tail) + (splitOnByte 32 line) +sourceModuleResolveRef root ref = + lazyBool + (_ : pure (ok ref t)) + (_ : sourceModuleResolveAlias root ref) + (sourceModuleDigest? ref) +sourceModulePinAlias root alias hash = + lazyBool + (_ : lazyBool + (_ : onOk_ (sourceModuleGetManifest root hash) (_ : + onOk_ (createDirectory (append root "/aliases")) (_ : + onOk_ (createDirectory (append root "/aliases/modules")) (_ : + onOk_ (atomicWriteBytes (sourceModuleAliasPath root alias) + (sourceModuleAliasBytes hash)) (_ : pure (ok t t)))))) + (_ : pure (err "invalid manifest digest" t)) + (sourceModuleDigest? hash)) + (_ : pure (err "invalid module alias" t)) + (sourceModuleAliasValid alias) +sourceModuleGetRefManifest root ref = + onResult_ (sourceModuleResolveRef root ref) + (problem : pure (err problem t)) + (hash : sourceModuleGetManifest root hash) + +-- Only selected exports hydrate; an unrelated changed tree is never loaded. +sourceModuleLoadSelected root manifestHash name = + onResult_ (sourceModuleGetRefManifest root manifestHash) + (problem : pure (err problem t)) + (exports : lazyMaybe + (_ : pure (err "missing module export" t)) + (hash : sourceObjectGetTree root hash) + (sourceFindExternal exports name)) + +-- Compile a definitions-only source with explicitly resolved external trees. +-- Put every declared local export as an independent canonical indexed object. +-- Public IO builder: environment and export selection are explicit data; +-- no old !import resolver participates at runtime. +sourceModulePublish root environment source names = + lazyResult + (diagnostic unused : pure (err diagnostic t)) + (definitions unused : sourceModulePublishNames root definitions names) + (compileModuleDefinitions environment source) +sourceModulePublishNames root definitions names = + sourceModulePublishPending names root definitions t +sourceModulePublishPending names root definitions entries = + lazyList + (_ : lazyList + (_ : pure (err "module requires an export" t)) + (_ _ : sourceModulePutManifest root (reverse entries)) + entries) + (name rest : lazyMaybe + (_ : pure (err "undeclared local export" t)) + (tree : onResult_ (sourceObjectPutTree root tree) + (problem : pure (err problem t)) + (hash : lazyMaybe + (_ : sourceModulePublishPending rest root definitions (pair (pair name hash) entries)) + (_ : pure (err "duplicate export" t)) + (sourceFindExternal entries name))) + (sourceFindExternal definitions name)) + names + +-- One native header interpretation for tricu and map staging. The result is +-- (ordered local edges, (import selections, (export selections, masked body))). +-- Masking keeps original character offsets and line/column locations. Local +-- imports and re-exports acquire edges; immutable hashes never do. !source is +-- build-only and cannot duplicate an acquired edge, even later in the header. +sourceHeaderScanLine bytes reversed = (lazyList + (_ : pair (reverse reversed) t) + (byte rest : lazyBool (_ : pair (reverse reversed) bytes) + (_ : sourceHeaderScanLine rest (pair byte reversed)) (equal? byte 10)) bytes) +sourceHeaderLine bytes = sourceHeaderScanLine bytes t +sourceHeaderComment? word = (lazyList (_ : false) + (a rest : lazyList (_ : false) + (b tail : and? (equal? a 45) (equal? b 45)) rest) word) +sourceHeaderMask byte = (lazyBool (_ : byte) (_ : 32) + (or? (equal? byte 10) (equal? byte 13))) +sourceHeaderError message position = + err (parserDiagnostic 40 message t position t) t +sourceHeaderContains names name = (lazyList + (_ : false) + (item rest : lazyBool (_ : true) (_ : sourceHeaderContains rest name) + (equal? item name)) names) +sourceHeaderRefValid ref = + or? (sourceModuleDigest? ref) (sourceModuleAliasValid ref) +sourceHeaderEdge ref deps = (lazyBool + (_ : deps) + (_ : pair ref deps) + (or? (sourceModuleDigest? ref) (sourceHeaderContains deps ref))) +sourceHeaderNext self policy line rest deps built uses exports prefix position = + (let newline = lazyList (_ : t) (_ _ : [10]) rest in + let full = append line newline in + let next = lazyList (_ : t) (_ tail : tail) rest in + self next policy deps built uses exports + (append (reverse (map sourceHeaderMask full)) prefix) + (foldl sourcePositionAdvance position full)) +sourceHeaderSourceShape arguments = (lazyList + (_ : err "!source requires a module name" t) + (name extra : lazyList + (_ : lazyBool (_ : ok name t) (_ : err "invalid build-only source" t) + (and? (sourceModuleAliasValid name) (not? (sourceModuleDigest? name)))) + (_ _ : err "unexpected !source arguments" t) extra) arguments) +sourceHeaderScan bytes policy deps built uses exports prefix position = (lazyList + (_ : ok (pair (reverse deps) (pair (reverse uses) + (pair (reverse exports) (reverse prefix)))) t) + (_ _ : let split = sourceHeaderLine bytes in + let line = fst split in + let rest = snd split in + lazyList + (_ : sourceHeaderNext sourceHeaderScan policy line rest deps built uses exports prefix position) + (word arguments : lazyBool + (_ : sourceHeaderNext sourceHeaderScan policy line rest deps built uses exports prefix position) + (_ : lazyBool + (_ : lazyResult + (problem _ : sourceHeaderError problem position) + (name _ : lazyBool + (_ : sourceHeaderError "redundant build-only source" position) + (_ : sourceHeaderNext sourceHeaderScan policy line rest (pair name deps) + (pair name built) uses exports prefix position) + (sourceHeaderContains deps name)) + (sourceHeaderSourceShape arguments)) + (_ : lazyBool + (_ : lazyResult + (problem _ : sourceHeaderError problem position) + (spec _ : lazyBool + (_ : sourceHeaderError "redundant build-only source" position) + (_ : sourceHeaderNext sourceHeaderScan policy line rest + (sourceHeaderEdge (fst spec) deps) built + (pair arguments uses) exports prefix position) + (sourceHeaderContains built (fst spec))) + (bindResult (sourceModuleUseShape arguments) + (spec _ : bindResult (policy spec) (_ _ : ok spec t)))) + (_ : lazyBool + (_ : lazyResult + (problem _ : sourceHeaderError problem position) + (spec _ : lazyBool + (_ : lazyBool + (_ : sourceHeaderError "redundant build-only source" position) + (_ : lazyBool + (_ : sourceHeaderNext sourceHeaderScan policy line rest + (sourceHeaderEdge (snd spec) deps) built uses + (pair arguments exports) prefix position) + (_ : sourceHeaderError "invalid re-export reference" position) + (sourceHeaderRefValid (snd spec))) + (sourceHeaderContains built (snd spec))) + (_ : sourceHeaderNext sourceHeaderScan policy line rest deps built uses + (pair arguments exports) prefix position) + (equal? (fst spec) "from")) + (sourceModuleExportShape arguments)) + (_ : lazyBool + (_ : sourceHeaderError "unknown native header directive" position) + (_ : ok (pair (reverse deps) (pair (reverse uses) + (pair (reverse exports) (append (reverse prefix) bytes)))) t) + (equal? (head word) 33)) + (equal? word "!export")) + (equal? word "!import")) + (equal? word "!source")) + (sourceHeaderComment? word)) + (words line)) bytes) +sourceModuleHeaderStageWith policy source = + sourceHeaderScan source policy t t t t t sourceStart +sourceModuleHeaderStage source = + sourceModuleHeaderStageWith (spec : ok t t) source +sourceModuleStage source = (bindResult (sourceModuleHeaderStage source) + (staged _ : ok (snd staged) t)) + +-- Import selections are parsed once by the shared interpreter and by immutable +-- pin rewriting. A local name supplies its default qualifier; a hash cannot. +sourceModuleUseShape arguments = (bindResult + (lazyList + (_ : err "!import requires a reference" t) + (ref tail : lazyList + (_ : lazyBool + (_ : err "hash imports require an alias or *" t) + (_ : ok (pair ref ref) t) (sourceModuleDigest? ref)) + (second more : lazyBool + (_ : lazyList (_ : ok (pair ref "*") t) + (_ _ : err "unexpected !import arguments" t) more) + (_ : lazyBool + (_ : lazyList + (_ : err "!import requires an alias" t) + (alias end : lazyList (_ : ok (pair ref alias) t) + (_ _ : err "unexpected !import arguments" t) end) more) + (_ : err "expected !import REF [as Alias | *]" t) + (equal? second "as")) + (equal? second "*")) tail) arguments) + (spec _ : lazyBool + (_ : ok spec t) + (_ : err "invalid import reference or qualifier" t) + (and? (sourceHeaderRefValid (fst spec)) + (or? (equal? (snd spec) "*") (sourceModuleAliasValid (snd spec)))))) + +sourceModuleExportShape words = + lazyList + (_ : err "!export requires a name" t) + (name rest : lazyList + (_ : ok (pair name t) t) + (from tail : lazyBool + (_ : lazyBool + (_ : lazyList + (_ : err "missing re-export manifest" t) + (hash end : lazyList + (_ : ok (pair "from" hash) t) + (_ _ : err "unexpected !export arguments" t) + end) + tail) + (_ : err "only !export * from HASH is supported" t) + (equal? name "*")) + (_ : err "expected !export * from HASH" t) + (equal? from "from")) + rest) + words + +sourceModuleExtendPending pending getManifest root environment = + lazyList + (_ : pure (ok environment t)) + (words rest : lazyResult + (problem unused : pure (err problem t)) + (spec unused : onResult_ (getManifest (fst spec)) + (problem : pure (err problem t)) + (entries : sourceModuleExtendOne root entries (snd spec) environment + (next : sourceModuleExtendPending rest getManifest root next))) + (sourceModuleUseShape words)) + pending +sourceModuleExtendUsesWith getManifest root uses = + sourceModuleExtendPending uses getManifest root t +sourceModuleExtendUses root uses = + sourceModuleExtendUsesWith (sourceModuleGetRefManifest root) root uses + +sourceModuleQualified alias name = + lazyBool + (_ : name) + (_ : append alias (append "." name)) + (equal? alias "*") +sourceModuleExtendEntries entries root alias environment next = + lazyList + (_ : next environment) + (entry rest : + lazyMaybe + (_ : onResult_ (sourceObjectGetTree root (snd entry)) + (problem : pure (err problem t)) + (tree : sourceModuleExtendEntries rest root alias (pair + (pair (sourceModuleQualified alias (fst entry)) tree) + environment) next)) + (_ : pure (err "duplicate imported name" t)) + (sourceFindExternal environment + (sourceModuleQualified alias (fst entry)))) + entries +sourceModuleExtendOne root entries alias environment next = + sourceModuleExtendEntries entries root alias environment next + +sourceModuleExportEntries entries root definitions selected = + lazyList + (_ : pure (ok selected t)) + (entry rest : lazyMaybe + (_ : onResult_ (sourceObjectPutTree root (snd entry)) + (problem : pure (err problem t)) + (hash : sourceModuleExportEntries rest root definitions + (pair (pair (fst entry) hash) selected))) + (_ : pure (err "duplicate export" t)) + (sourceFindExternal selected (fst entry))) + entries +sourceModuleExportAll root definitions selected = + sourceModuleExportEntries definitions root definitions selected + +sourceModuleAddPending incoming root selected = + lazyList + (_ : pure (ok selected t)) + (entry rest : lazyMaybe + (_ : onResult_ (sourceObjectGetTree root (snd entry)) + (problem : pure (err problem t)) + (_ : sourceModuleAddPending rest root (pair entry selected))) + (_ : pure (err "duplicate re-export" t)) + (sourceFindExternal selected (fst entry))) + incoming +sourceModuleAddEntries root incoming selected = + sourceModuleAddPending incoming root selected + +sourceModuleSelectPending pending getManifest root definitions selected = + lazyList + (_ : lazyList + (_ : pure (err "module requires an export" t)) + (_ _ : sourceModulePutManifest root (reverse selected)) + selected) + (words rest : lazyResult + (problem unused : pure (err problem t)) + (request unused : lazyBool + (_ : onResult_ (getManifest (snd request)) + (problem : pure (err problem t)) + (entries : onResult_ (sourceModuleAddEntries root entries selected) + (problem : pure (err problem t)) + (new : sourceModuleSelectPending rest getManifest root definitions new))) + (_ : lazyBool + (_ : onResult_ (sourceModuleExportAll root definitions selected) + (problem : pure (err problem t)) + (new : sourceModuleSelectPending rest getManifest root definitions new)) + (_ : lazyMaybe + (_ : pure (err "undeclared local export" t)) + (tree : lazyMaybe + (_ : onResult_ (sourceObjectPutTree root tree) + (problem : pure (err problem t)) + (hash : sourceModuleSelectPending rest getManifest root definitions + (pair (pair (fst request) hash) selected))) + (_ : pure (err "duplicate export" t)) + (sourceFindExternal selected (fst request))) + (sourceFindExternal definitions (fst request))) + (equal? (fst request) "*")) + (equal? (fst request) "from")) + (sourceModuleExportShape words)) + pending +sourceModuleSelectWith getManifest root definitions exports = + sourceModuleSelectPending exports getManifest root definitions t +sourceModuleSelect root definitions exports = + sourceModuleSelectWith (sourceModuleGetRefManifest root) root definitions exports + +-- End-to-end source entrypoint: stage -> verify/hydrate immutable manifest +-- imports -> compile source -> store executable trees -> publish manifest. +-- The graph supplies already authenticated, exact-kind/ABI manifest entries. +-- Standalone builds still resolve and verify manifests through the ordinary IO +-- reader. The resolver never bypasses executable-tree verification. +sourceModuleBuildResolvedWith getManifest compileDefinitions root staged = + (onResult_ (sourceModuleExtendUsesWith getManifest root (fst staged)) + (problem : pure (err problem t)) + (environment : lazyResult + (problem unused : pure (err problem t)) + (definitions unused : sourceModuleSelectWith getManifest root definitions + (fst (snd staged))) + (compileDefinitions environment (snd (snd staged))))) +sourceModuleBuildStagedWith compileDefinitions root staged = + sourceModuleBuildResolvedWith (sourceModuleGetRefManifest root) compileDefinitions root staged +sourceModuleBuildWith compileDefinitions root source = (lazyResult + (problem unused : pure (err problem t)) + (staged unused : sourceModuleBuildStagedWith compileDefinitions root staged) + (sourceModuleStage source)) + +sourceModuleBuild root source = sourceModuleBuildWith compileModuleDefinitions root source + + +sourceModuleBuildStaged root staged = + sourceModuleBuildStagedWith compileModuleDefinitions root staged diff --git a/test/fixtures/reference-source/4dc6a84c739f524f09e5378ddb79140fd11629988a8a451c0e823f6c1dc21e96.tri b/test/fixtures/reference-source/4dc6a84c739f524f09e5378ddb79140fd11629988a8a451c0e823f6c1dc21e96.tri @@ -0,0 +1,17 @@ +!import base as B +!import conversions as C +!import io_v2 as IO +!import arboricx_streaming_v2 as Server +!import arboricx_handlers as Registry +!export main +!export server +-- Native streaming registry, separate from the unchanged deployed v1 entrypoint. +-- Source-owned ./store, 1-MiB body/object budget and bounded FIFO16 admission. +port ["ipv4" _ value] = value +port _ = 0 +ready listener = do IO.require + address <- IO.localAddress listener + writer <- IO.stdout + IO.require (IO.writeAll writer (B.append (C.showNumber (port address)) [10])) IO.pure +server root address count = IO.io (Server.serveCount root 1048576 Registry.stdoutLogV2 address 16 count ready) +main = IO.io (Server.serve "./store" 1048576 Registry.stdoutLogV2 (IO.ipv4 [127 0 0 1] 9050) 16 ready) diff --git a/test/fixtures/reference-source/4dcb43187b3cbd168fcb3ad5269be3e96257aa30773bca933e6cf37dfbdee066.tri b/test/fixtures/reference-source/4dcb43187b3cbd168fcb3ad5269be3e96257aa30773bca933e6cf37dfbdee066.tri @@ -0,0 +1,20 @@ +!import base as B +!import handlers as H +!import io_v2 as IO +!import io_v2_text as Text +!export main +-- Lexical application configuration, NOT the OS environment or a sandbox. +ask = H.request "configuration" "ask" t +reader value "ask" t = H.pure value +reader _ operation argument = H.forward "configuration" operation argument +line message = H.liftIO (IO.require IO.stdout (writer : + IO.require (Text.writeText writer (B.append message "\n")) IO.pure)) +inside = do H.bind + value <- ask + line (B.append "working in env: " value) + H.pure "inside-done" +main = IO.io (H.runIOV2 (H.handle "configuration" (reader "outer") (do H.bind + value <- H.handle "configuration" (reader "demo") inside + line (B.append "local returned: " value) + outside <- ask + line (B.append "outside env: " outside)))) diff --git a/test/fixtures/reference-source/4e2c4278cd4efaa9c2d9d7db51fb2a7a0c99fe2e9ac76bc3ea89bd26e47ba4b7.tri b/test/fixtures/reference-source/4e2c4278cd4efaa9c2d9d7db51fb2a7a0c99fe2e9ac76bc3ea89bd26e47ba4b7.tri @@ -0,0 +1,90 @@ +!import base as B +!import native_math as N +!import http as HTTP +!import io_v2 as IO +!import io_v2_storage as Storage +!import handlers as H +!import arboricx_server as Store +!export write +!export publish +!export publishWith + +-- Source policy, not a native upload service. Producer acknowledgement is never +-- the length oracle: only writes observed by this boundary advance its count. +write bytes = H.request "artifact-stream" "write" bytes +onOk action next = H.bind (H.liftIO action) (reply : case reply of + ((t t), (value, t)) = next value + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "host" "Invalid host Result") + +hashReader reader digest remaining = IO.onOk (IO.readSome reader 4096) (reply : case reply of + ("eof", t) = case remaining of + 0 = IO.finishDigest digest + _ = IO.pure (B.err "Existing artifact length mismatch" t) + ("data", bytes) = let count = HTTP.httpBodyLength bytes in case N.lte count remaining of + (t t) = IO.onOk (IO.updateDigest digest bytes) + (_ : hashReader reader digest (N.subtract remaining count)) + _ = IO.pure (B.err "Existing artifact length mismatch" t) + _ = IO.fail "Invalid read reply") + +-- An existing name is not publication success. Verify its complete contents and +-- length independently, using fresh reader/digest resources, before accepting it. +verifyWinner destination expected wanted = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner destination + digest <- IO.openDigest owner "sha256" + actual <- hashReader reader digest expected + case B.equal? actual wanted of + (t t) = do IO.onOk + IO.syncFile reader + IO.syncDirectory (Storage.parentPath destination) + IO.pure (B.ok t t) + _ = IO.pure (B.err "Existing artifact digest mismatch" t)) + +-- Destination policy runs only after exact-length completion and sealing. It +-- returns a handler Result path and can create a digest-derived shard. Native +-- publication remains generic and rejects cross-filesystem moves without copying. +finish destination expected stage digest = onOk (IO.finishDigest digest) (hash : + case B.checkContract Store.bundleHashC hash of + ((t t), (checked, t)) = onOk (IO.sealStage stage) (_ : + H.bind (destination checked) (reply : case reply of + ((t t), (path, t)) = onOk (IO.publishNew stage path) (outcome : case outcome of + "published" = H.pure (B.ok checked t) + "exists" = onOk (verifyWinner path expected checked) (_ : H.pure (B.ok checked t)) + _ = H.fail "invalid-response" "artifact-stream" "publish" "Invalid publication reply") + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "destination" "Expected destination Result")) + _ = H.fail "invalid-response" "artifact-stream" "digest" "Invalid digest reply") + +publish directory expected producer = publishWith directory expected + (digest : H.pure (B.ok (B.append directory (B.pair 47 digest)) t)) producer +publishWith directory expected destination producer = case B.checkContract B.nat? expected of + ((t t), (count, t)) = case N.lte count 67108864 of + (t t) = H.scopeV2 (owner : onOk (IO.openStage owner directory) (stage : + onOk (IO.openDigest owner "sha256") (digest : let rec + walk current remaining = case current of + (100, reply) = case reply of + ((t t), (t, t)) = case remaining of + 0 = finish destination count stage digest + _ = H.pure (B.err "Incomplete artifact stream" t) + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "producer" "Expected Result unit" + (103, [kind service operation detail labels]) = B.pair 105 + (B.pair labels (H.fail kind service operation detail)) + (106, [["artifact-stream" "write" bytes] labels next]) = + onOk (IO.writeAll stage bytes) (_ : case bytes of + [] = H.pure (B.err "Empty artifact chunk" t) + _ = let length = HTTP.httpBodyLength bytes in case N.lte length remaining of + (t t) = onOk (IO.updateDigest digest bytes) + (_ : walk (next (B.ok t t)) (N.subtract remaining length)) + _ = H.pure (B.err "Artifact exceeds declared length" t)) + (106, [[service operation argument] labels next]) = H.bind + (B.pair 105 (B.pair labels (H.request service operation argument))) + (reply : walk (next reply) remaining) + (107, [labels body next]) = B.pair 105 (B.pair labels + (H.fail "producer-boundary" "artifact-stream" "publish" "Producer must be sequential")) + (108, [supervised keep handles labels body next]) = B.pair 105 (B.pair labels + (H.fail "producer-boundary" "artifact-stream" "publish" "Producer must be sequential")) + _ = H.fail "invalid-view" "artifact-stream" "publish" "Invalid producer view" + in walk (H.view producer) count))) + _ = H.pure (B.err "Artifact length exceeds limit" t) + _ = H.pure (B.err "Invalid artifact length" t) diff --git a/test/fixtures/reference-source/4f64f9f4175b18c4e97caa24249d3bbe1261a57c6215de53c5ed4efe7fc546ab.tri b/test/fixtures/reference-source/4f64f9f4175b18c4e97caa24249d3bbe1261a57c6215de53c5ed4efe7fc546ab.tri @@ -0,0 +1,38 @@ +!import base as B +!import handlers as H +!export emit +!export info +!export warn +!export withContext +!export withSink +!export capture +!export discard + +-- Events are [level ordered-context-fields payload]. Payload/fields are trees; +-- sinks choose their formatting and serialization. +eventC value{[level fields payload]} rest = case B.listOf B.anyC fields rest of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "log context must be a proper list" rest +eventC _ rest = B.contractErr "expected [level context payload]" rest + +-- Log sinks return ordinary Result unit; errors remain application-visible. +replyC value{((t t), (t, t))} rest = B.contractOk value rest +replyC value{(t, (message, t))} rest = B.contractOk value rest +replyC _ rest = B.contractErr "expected log Result with empty remainder" rest + +emit level payload = H.checkedRequest "logging" "emit" eventC replyC [level [] payload] +info payload = emit "info" payload +warn payload = emit "warn" payload + +contextHandler fields "emit" event{[level inner payload]} = + H.forward "logging" "emit" [level (B.append fields inner) payload] +contextHandler _ operation payload = H.forward "logging" operation payload +withContext fields action = case B.checkContract (B.listOf B.anyC) fields of + ((t t), (checked, t)) = H.handle "logging" (contextHandler checked) action + _ = H.fail "invalid-request" "logging" "context" "context must be a proper list" + +sinkHandler sink "emit" event = H.checkRequest "logging" "emit" eventC event sink +sinkHandler _ operation payload = H.forward "logging" operation payload +withSink sink action = H.handle "logging" (sinkHandler sink) action +capture action = H.collect "logging" "emit" eventC (B.ok t t) action +discard action = withSink (_ : H.pure (B.ok t t)) action diff --git a/test/fixtures/reference-source/50691f992e348fb47baccc119c3809f7e33fdcce5c801b23ed8635962f346655.tri b/test/fixtures/reference-source/50691f992e348fb47baccc119c3809f7e33fdcce5c801b23ed8635962f346655.tri @@ -0,0 +1,35 @@ +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!import handlers as H +!import handler_log as Log +!export main +!export chatter + +-- Yield offers scheduling opportunities, never guaranteed alternation. Each +-- child installs its own collection; the parent alone writes the observed events. +-- No implicit shared state slot, inherited collector or concurrent stdout writes. +chatter name result = H.runIOV2 (Log.capture (do H.bind + Log.info (B.append name " says 1") + H.liftIO (IO.require IO.yield IO.pure) + Log.info (B.append name " says 2") + H.liftIO (IO.require IO.yield IO.pure) + Log.info (B.append name " says 3") + H.pure result)) +printEvents _ [] = IO.pure t +printEvents writer [["info" _ message] | rest] = IO.require + (Text.writeText writer (B.append message "\n")) (_ : printEvents writer rest) +printEvents _ _ = IO.fail "Invalid child log event" +consume writer [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" [value events]] = IO.bind (printEvents writer events) (_ : IO.pure value) + ["cancelled" reason _] = IO.cancelled reason + ["failed" problem cleanup] = IO.fail [problem cleanup] + _ = IO.fail "Invalid task outcome") +consume _ _ = IO.fail "Invalid spawn reply" +main = IO.io (IO.scope (_ : do IO.require + first <- IO.spawn 0 1 [] (_ : chatter "A" "doneA") + second <- IO.spawn 0 1 [] (_ : chatter "B" "doneB") + IO.yield + writer <- IO.stdout + IO.bind (consume writer first) (a : IO.bind (consume writer second) (b : + IO.require (Text.writeText writer (B.append "Finished: " (B.append a (B.append " " (B.append b "\n"))))) (_ : IO.pure t))))) diff --git a/test/fixtures/reference-source/52c3f1b1f0d29975e3932850451dc8e3cee93ac475dac869d8e9eba7838bd830.tri b/test/fixtures/reference-source/52c3f1b1f0d29975e3932850451dc8e3cee93ac475dac869d8e9eba7838bd830.tri @@ -0,0 +1,297 @@ +!import prelude * +!import compiler_io * +!import source_module * +!import source_object * +!import source_resolve * +!export * + +-- A named lock is an explicitly updated local snapshot. Rows record the +-- original reference, one coherent originating manifest, and every export +-- selected by that reference (the native import / re-export profile selects +-- a manifest's whole public interface). No ambient alias is read by a +-- locked build. The manifest remains authoritative for its export hashes. +sourceLockHeader = "arboricx.source-lock.v1\n" +sourceLockPath root name = append root (append "/locks/" name) +sourceLockRow ref hash entry = + append "pin\t" (append (sourceModuleEscape ref) + (append "\t" (append hash + (append "\t" (append (sourceModuleEscape (fst entry)) + (append "\t" (append (snd entry) "\n"))))))) +sourceLockRows entries ref hash = + lazyList + (_ : t) + (entry rest : append (sourceLockRow ref hash entry) + (sourceLockRows rest ref hash)) + entries +sourceLockGroups groups = + lazyList + (_ : t) + (group rest : append + (sourceLockRows (snd (snd group)) (fst group) (fst (snd group))) + (sourceLockGroups rest)) + groups +sourceLockEncode groups = + append sourceLockHeader (sourceLockGroups groups) + +-- A row is ref -> (manifest hash -> (export name -> exact tree hash)). +sourceLockParseRow fields = + lazyList + (_ : err "invalid lock row" t) + (tag r1 : lazyList + (_ : err "invalid lock row" t) + (encodedRef r2 : lazyList + (_ : err "invalid lock row" t) + (manifest r3 : lazyList + (_ : err "invalid lock row" t) + (encodedName r4 : lazyList + (_ : err "invalid lock row" t) + (tree extra : lazyBool + (_ : lazyResult + (problem unused : err problem t) + (ref unused : lazyResult + (problem unused : err problem t) + (name unused : lazyBool + (_ : ok (pair ref (pair manifest + (pair name tree))) t) + (_ : err "invalid lock ref, export or digest" t) + (and? (or? (sourceModuleDigest? ref) + (sourceModuleAliasValid ref)) + (and? (sourceModuleDigest? manifest) + (and? (sourceModuleExportNameValid name) + (sourceModuleDigest? tree))))) + (sourceModuleUnescape encodedName)) + (sourceModuleUnescape encodedRef)) + (_ : err "invalid lock row" t) + (and? (equal? tag "pin") (emptyList? extra))) + r4) + r3) + r2) + r1) + fields +sourceLockParseRows rows acc = + lazyList + (_ : ok (reverse acc) t) + (line rest : lazyList + (_ : lazyList + (_ : ok (reverse acc) t) + (_ _ : err "blank lock row" t) + rest) + (_ _ : lazyResult + (problem unused : err problem t) + (entry unused : sourceLockParseRows rest (pair entry acc)) + (sourceLockParseRow (splitOnByte 9 line))) + line) + rows +sourceLockDecode bytes = + lazyList + (_ : err "empty lock" t) + (header lines : lazyBool + (_ : sourceLockParseRows lines t) + (_ : err "unsupported lock version" t) + (equal? header "arboricx.source-lock.v1")) + (splitOnByte 10 bytes) + +sourceLockDistinct rows seen = + lazyList + (_ : ok (reverse seen) t) + (row rest : lazyMaybe + (_ : sourceLockDistinct rest (pair (pair (fst row) (fst (snd row))) seen)) + (previous : lazyBool + (_ : sourceLockDistinct rest seen) + (_ : err "incoherent lock snapshot" t) + (equal? previous (fst (snd row)))) + (sourceFindExternal seen (fst row))) + rows +sourceLockSelectRows rows ref entries = + lazyList + (_ : reverse entries) + (row rest : lazyBool + (_ : sourceLockSelectRows rest ref (pair (snd (snd row)) entries)) + (_ : sourceLockSelectRows rest ref entries) + (equal? ref (fst row))) + rows +sourceLockRowsFor rows ref = sourceLockSelectRows rows ref t +sourceLockCheckEntries entries root = + lazyList + (_ : pure (ok t t)) + (entry rest : onResult_ (sourceObjectGetTree root (snd entry)) + (problem : pure (err problem t)) + (_ : sourceLockCheckEntries rest root)) + entries +sourceLockCheckTrees root entries = sourceLockCheckEntries entries root +sourceLockVerifyRefs refs root rows groups = + lazyList + (_ : pure (ok (reverse groups) t)) + (refHash rest : onResult_ (sourceModuleGetManifest root (snd refHash)) + (problem : pure (err problem t)) + (entries : lazyBool + (_ : onOk_ (sourceLockCheckTrees root entries) (_ : + sourceLockVerifyRefs rest root rows (pair + (pair (fst refHash) (pair (snd refHash) entries)) groups))) + (_ : pure (err "lock export snapshot mismatch" t)) + (equal? entries (sourceLockRowsFor rows (fst refHash))))) + refs +sourceLockVerify root bytes = + lazyResult + (problem unused : pure (err problem t)) + (rows unused : lazyResult + (problem unused : pure (err problem t)) + (refs unused : lazyList + (_ : pure (err "lock requires a module" t)) + (_ _ : onResult_ (sourceLockVerifyRefs refs root rows t) + (problem : pure (err problem t)) + (groups : lazyBool + (_ : pure (ok groups t)) + (_ : pure (err "noncanonical lock" t)) + (equal? bytes (sourceLockEncode groups)))) + refs) + (sourceLockDistinct rows t)) + (sourceLockDecode bytes) + +-- Gather each source ref once. Resolving an alias occurs once per update; +-- repeated directives share the same immutable manifest snapshot. +sourceLockCollect refs root groups = + lazyList + (_ : pure (ok (reverse groups) t)) + (ref rest : lazyMaybe + (_ : onResult_ (sourceModuleResolveRef root ref) + (problem : pure (err problem t)) + (hash : onResult_ (sourceModuleGetManifest root hash) + (problem : pure (err problem t)) + (entries : onOk_ (sourceLockCheckTrees root entries) (_ : + sourceLockCollect rest root (pair (pair ref (pair hash entries)) groups))))) + (_ : sourceLockCollect rest root groups) + (sourceFindExternal groups ref)) + refs +sourceLockUseRefs uses acc = + lazyList + (_ : ok (reverse acc) t) + (words rest : lazyResult + (problem unused : err problem t) + (spec unused : sourceLockUseRefs rest (pair (fst spec) acc)) + (sourceModuleUseShape words)) + uses +sourceLockExportRefs exports acc = + lazyList + (_ : ok (reverse acc) t) + (words rest : lazyResult + (problem unused : err problem t) + (spec unused : lazyBool + (_ : sourceLockExportRefs rest (pair (snd spec) acc)) + (_ : sourceLockExportRefs rest acc) + (equal? (fst spec) "from")) + (sourceModuleExportShape words)) + exports +sourceLockSourceRefs source = + lazyResult + (problem unused : err problem t) + (staged unused : lazyResult + (problem unused : err problem t) + (uses unused : lazyResult + (problem unused : err problem t) + (exports unused : ok (append uses exports) t) + (sourceLockExportRefs (fst (snd staged)) t)) + (sourceLockUseRefs (fst staged) t)) + (sourceModuleStage source) +sourceLockUpdate root name source = + lazyBool + (_ : lazyResult + (problem unused : pure (err problem t)) + (refs unused : onResult_ (sourceLockCollect refs root t) + (problem : pure (err problem t)) + (groups : lazyList + (_ : pure (err "lock requires a module" t)) + (_ _ : onOk_ (createDirectory root) (_ : + onOk_ (createDirectory (append root "/locks")) (_ : + onOk_ (atomicWriteBytes (sourceLockPath root name) + (sourceLockEncode groups)) (_ : pure (ok t t))))) + groups)) + (sourceLockSourceRefs source)) + (_ : pure (err "invalid lock name" t)) + (sourceModuleAliasValid name) + +sourceLockRewriteUses uses groups = + lazyList + (_ : ok t t) + (words rest : lazyResult + (problem unused : err problem t) + (spec unused : lazyMaybe + (_ : err "unlocked module reference" t) + (group : lazyResult + (problem unused : err problem t) + (suffix unused : ok (append "!import " + (append (fst group) (append (lazyBool + (_ : " *\n") + (_ : append " as " (append (snd spec) "\n")) + (equal? (snd spec) "*")) suffix))) t) + (sourceLockRewriteUses rest groups)) + (sourceFindExternal groups (fst spec))) + (sourceModuleUseShape words)) + uses +sourceLockRewriteExports exports groups = + lazyList + (_ : ok t t) + (words rest : lazyResult + (problem unused : err problem t) + (spec unused : lazyResult + (problem unused : err problem t) + (suffix unused : lazyBool + (_ : lazyMaybe + (_ : err "unlocked re-export" t) + (group : ok (append "!export * from " + (append (fst group) (append "\n" suffix))) t) + (sourceFindExternal groups (snd spec))) + (_ : ok (append "!export " + (append (fst spec) (append "\n" suffix))) t) + (equal? (fst spec) "from")) + (sourceLockRewriteExports rest groups)) + (sourceModuleExportShape words)) + exports +sourceLockRewriteSource source groups = + lazyResult + (problem unused : err problem t) + (staged unused : lazyResult + (problem unused : err problem t) + (uses unused : lazyResult + (problem unused : err problem t) + (exports unused : ok (append uses + (append exports (snd (snd staged)))) t) + (sourceLockRewriteExports (fst (snd staged)) groups)) + (sourceLockRewriteUses (fst staged) groups)) + (sourceModuleStage source) +-- Immutable binding selection without moving body coordinates. Rewritten +-- directives are interpreted separately, never prepended to the masked body. +sourceLockRewriteStaged source groups = (bindResult (sourceModuleStage source) + (staged _ : bindResult (sourceLockRewriteSource source groups) + (rewritten _ : bindResult (sourceModuleStage rewritten) + (header _ : ok (pair (fst header) + (pair (fst (snd header)) (snd (snd staged)))) t)))) +sourceLockUniqueRefs refs seen = + lazyList + (_ : sourceLockGroupRefs (reverse seen)) + (ref rest : lazyMaybe + (_ : sourceLockUniqueRefs rest (pair (pair ref t) seen)) + (_ : sourceLockUniqueRefs rest seen) + (sourceFindExternal seen ref)) + refs +sourceLockGroupRefs groups = map fst groups +sourceLockBuild root name source = + lazyBool + (_ : onResult_ (readFile (sourceLockPath root name)) + (problem : pure (err problem t)) + (bytes : onResult_ (sourceLockVerify root bytes) + (problem : pure (err problem t)) + (groups : lazyResult + (problem unused : pure (err problem t)) + (refs unused : lazyBool + (_ : lazyResult + (problem unused : pure (err problem t)) + (staged unused : sourceModuleBuildStaged root staged) + (sourceLockRewriteStaged source groups)) + (_ : pure (err "lock source references changed; update required" t)) + (equal? (sourceLockGroupRefs groups) + (sourceLockUniqueRefs refs t))) + (sourceLockSourceRefs source)))) + (_ : pure (err "invalid lock name" t)) + (sourceModuleAliasValid name) + diff --git a/test/fixtures/reference-source/5445a4f505c572889455435960a144663e224fa40a9c1cce1d9a96a8e3678fef.tri b/test/fixtures/reference-source/5445a4f505c572889455435960a144663e224fa40a9c1cce1d9a96a8e3678fef.tri @@ -0,0 +1,94 @@ +!import prelude * +!import compiler_io * +!import io_v2 as RuntimeIO +!import io_v2_text as Text +!import source_object * +!import source_module * +!import source_lock * +!import source_build * +!import source_unit * +!import source_resolve * +!export * + +-- Opt-in bootstrap acceleration. The runner supplies its compiler identity; +-- rewritten source fixes exact imported manifests. Indices are hints, not CAS. +sourceExportCacheLog line = RuntimeIO.require + (Text.writeText RuntimeIO.stdout (append line "\n")) + +sourceExportCacheOne root scope name tree = + onOk_ (sourceObjectDigest "arboricx.source-export-name.v1" name) (nameHash : + (path : thenIO (sourceExportCacheLog (append "export-start=" name)) + (onOk_ (fileExists path) (exists : lazyBool + (_ : onOk_ (readFile path) (hash : + onOk_ (sourceObjectGetTree root hash) (_ : + thenIO (sourceExportCacheLog (append "export-reused=" name)) + (pure (ok hash t))))) + (_ : onOk_ (sourceObjectPutTree root tree) (hash : + onOk_ (atomicWriteBytes path hash) (_ : + thenIO (sourceExportCacheLog (append "export-complete=" + (append name (append "\t" hash)))) (pure (ok hash t))))) + exists))) (append scope (append "/" nameHash))) + +sourceExportCacheAll entries root scope selected = + lazyList + (_ : pure (ok selected t)) + (entry rest : lazyMaybe + (_ : onOk_ (sourceExportCacheOne root scope (fst entry) (snd entry)) + (hash : sourceExportCacheAll rest root scope (pair (pair (fst entry) hash) selected))) + (_ : pure (err "duplicate export" t)) + (sourceFindExternal selected (fst entry))) + entries + +sourceExportCacheSelect pending root scope definitions selected = + lazyList + (_ : sourceModulePutManifest root (reverse selected)) + (words rest : lazyResult + (problem unused : pure (err problem t)) + (request unused : lazyBool + (_ : onOk_ (sourceModuleGetRefManifest root (snd request)) (entries : + onOk_ (sourceModuleAddEntries root entries selected) + (next : sourceExportCacheSelect rest root scope definitions next))) + (_ : lazyBool + (_ : onOk_ (sourceExportCacheAll definitions root scope selected) + (next : sourceExportCacheSelect rest root scope definitions next)) + (_ : lazyMaybe + (_ : pure (err "undeclared local export" t)) + (tree : lazyMaybe + (_ : onOk_ (sourceExportCacheOne root scope (fst request) tree) + (hash : sourceExportCacheSelect rest root scope definitions + (pair (pair (fst request) hash) selected))) + (_ : pure (err "duplicate export" t)) + (sourceFindExternal selected (fst request))) + (sourceFindExternal definitions (fst request))) + (equal? (fst request) "*")) + (equal? (fst request) "from")) + (sourceModuleExportShape words)) + pending + +sourceExportCacheBuild root source compiler = + onOk_ (encodeSourceText source) (sourceBytes : + onOk_ (sourceObjectDigest "arboricx.source-export-build.v1" + (append compiler (pair 0 sourceBytes))) (key : + (scope : onOk_ (createDirectory root) (_ : + onOk_ (createDirectory (append root "/cache")) (_ : + onOk_ (createDirectory (append root "/cache/source-export")) (_ : + onOk_ (createDirectory scope) (_ : lazyResult + (problem unused : pure (err problem t)) + (staged unused : onOk_ (sourceModuleExtendUses root (fst staged)) + (environment : lazyResult + (problem unused : pure (err problem t)) + (definitions unused : sourceExportCacheSelect + (fst (snd staged)) root scope definitions t) + (compileModuleDefinitions environment (snd (snd staged))))) + (sourceModuleStage source)))))) + (append root (append "/cache/source-export/" key)))) + +sourceExportCacheCompile root clean groups compiler = + lazyResult + (problem unused : pure (err problem t)) + (rewritten unused : sourceExportCacheBuild root rewritten compiler) + (sourceLockRewriteSource clean groups) +sourceExportCacheProfile compiler = + pair (workspace name : sourceBuildReadTri workspace name) + (pair (root clean groups : sourceExportCacheCompile root clean groups compiler) + "source-module.reference.v1") diff --git a/test/fixtures/reference-source/575f7c3c8834bd2780f444a0078e72366fe2f0de6c4e00b4c3722e4da8bb9693.tri b/test/fixtures/reference-source/575f7c3c8834bd2780f444a0078e72366fe2f0de6c4e00b4c3722e4da8bb9693.tri @@ -0,0 +1,51 @@ +!import base as B +!import io_v2 as IO +!import io_v2_storage as Storage +!import io_v2_text as Text +!export * + +-- Compiler storage policy over IO v2, imported explicitly as compiler_io. +-- Explicit application policy: resident source/metadata bytes and encoded trees +-- are limited to 64 MiB. Heap use, expanded-tree size and CPU work require +-- separate resource controls. +byteLimit = 67108864 +version = 2 +io = IO.io +pure = IO.pure +bind = IO.bind +abortIO = IO.fail +thenIO first second = bind first (_ : second) +mapIO action f = bind action (value : pure (f value)) +void action = bind action (_ : pure t) +onOk action next = bind action (reply : B.matchResult (_ _ : pure reply) next reply) +onOk_ action next = IO.onOk action next +onResult action failed succeeded = bind action (reply : B.matchResult failed succeeded reply) +onResult_ action failed succeeded = onResult action (problem _ : failed problem) (value _ : succeeded value) + +readFile path = Storage.readBytes path byteLimit +sha256Hex bytes = Storage.hashBytes byteLimit bytes +readSourceFile path = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + Text.readSource reader byteLimit) +encodeSourceText text = Text.encodeText byteLimit text +-- Compiler callers create directories in parent-first order. A missing ancestor +-- produces an error. Sync the parent entry before use. +createDirectory path = do IO.onOk + IO.ensureDirectory path + IO.syncDirectory (Storage.parentPath path) +fileExists path = bind (IO.statPath path) (reply : case reply of + ((t t), (["file" _], t)) = pure (B.ok (t t) t) + ((t t), (_, t)) = pure (B.ok t t) + (t, (["not-found" _ _ _], t)) = pure (B.ok t t) + _ = pure reply) +writeBytesIfAbsent path bytes = Storage.publishBytes (Storage.parentPath path) path 0 byteLimit bytes +atomicWriteBytes path bytes = Storage.publishBytes (Storage.parentPath path) path 1 byteLimit bytes +hashIndexedTree tree = do IO.onOk + info <- IO.hashIndexedTree byteLimit tree + case info of + [_ digest] = pure (B.ok digest t) + _ = IO.fail "Invalid indexed hash reply" +writeIndexedTreeIfAbsent path digest tree = + Storage.publishTree (Storage.parentPath path) path byteLimit digest tree +readIndexedTree path digest = + Storage.readTree (Storage.parentPath path) path byteLimit digest diff --git a/test/fixtures/reference-source/5e4d9186f5235078c786432b6ac8a2952ee8c87bed8f808503d5107daaa07bac.tri b/test/fixtures/reference-source/5e4d9186f5235078c786432b6ac8a2952ee8c87bed8f808503d5107daaa07bac.tri @@ -0,0 +1,143 @@ +!import base as B +!import io as IO +!import handlers as H +!import io_v2 as V2 +!import io_v2_storage as Storage +!import io_v2_artifacts as Stream +!import arboricx_server as Store +!export load +!export loadFrom +!export publish +!export publishTo +!export withFilesystem +!export withFilesystemFor +!export prepareFilesystem +!export withFilesystemV2 +!export withFilesystemForV2 +!export prepareFilesystemV2 +!export publishStreamV2 + +-- Domain Results always have empty remainders. HTTP status mapping belongs to +-- the caller, not this interface. Invalid digests are ordinary input failures; +-- malformed request/reply protocols are interpreter failures. +-- Eight bounded bit steps, each checking exact Boolean shape and canonical +-- termination. The continuation chain replaces a per-bit recursive fuel test; +-- it neither accepts redundant high zeros nor delegates to unchecked arithmetic. +octetEnd = B.triage (t t) (_ : t) (_ _ : t) +octetStep next value = B.triage (t t) (_ : t) + (bit tail : B.triage + (B.triage t (_ : t) (_ _ : next tail) tail) + (child : B.triage (next tail) (_ : t) (_ _ : t) child) + (_ _ : t) bit) + value +octetValid = octetStep (octetStep (octetStep (octetStep + (octetStep (octetStep (octetStep (octetStep octetEnd))))))) +byteC = B.guardC "expected octet" octetValid + +-- These contracts are predicates, never element transformers. Scan without +-- rebuilding the accepted list or allocating a Result for each accepted byte. +-- Preserve the old first-error diagnostics and the caller's remainder. +bytesC value rest = let rec + walk remaining = B.triage + (B.contractOk value rest) + (_ : B.contractErr "not a list" rest) + (byte tail : case octetValid byte of + (t t) = walk tail + _ = B.contractErr "expected octet" rest) + remaining + in walk value + +loadFrom service digest = case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = H.checkedRequest service "load" B.anyC loadReplyC checked + (t, (message, _)) = H.pure (B.err ["invalid-digest" message] t) + _ = H.fail "invalid-contract" service "load" digest +load digest = loadFrom "artifacts" digest +publishTo service bytes = H.checkedRequest service "publish" bytesC publishReplyC bytes +publish bytes = publishTo "artifacts" bytes + +loadReplyC value{((t t), (bytes, t))} rest = case B.checkContract bytesC bytes of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "expected artifact bytes" rest +loadReplyC value{(t, (message, t))} rest = B.contractOk value rest +loadReplyC _ rest = B.contractErr "expected artifact-load Result with empty remainder" rest +publishReplyC value{((t t), (digest, t))} rest = case B.checkContract Store.bundleHashC digest of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "expected canonical artifact digest" rest +publishReplyC value{(t, (message, t))} rest = B.contractOk value rest +publishReplyC _ rest = B.contractErr "expected artifact-publication Result with empty remainder" rest + +-- Recheck the digest in the adapter: raw H.request callers do not get an +-- unchecked path-taking entrypoint. Host permissions remain authoritative. +filesystem root service "load" digest = case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = H.liftIO (IO.readFile (Store.bundleObjectPath root checked)) + (t, (message, _)) = H.pure (B.err ["invalid-digest" message] t) + _ = H.fail "invalid-contract" service "load" digest +filesystem root service "publish" bytes = case B.checkContract bytesC bytes of + ((t t), (checked, t)) = H.liftIO (Store.putBundle root checked) + (t, (message, _)) = H.fail "invalid-request" service "publish" message + _ = H.fail "invalid-contract" service "publish" bytes +filesystem _ service operation payload = H.forward service operation payload +withFilesystemFor service root action = H.handle service (filesystem root service) action +withFilesystem root action = withFilesystemFor "artifacts" root action + +-- Explicit application startup, not repeated implicitly on every request. +-- Returns the existing store initializer's ordinary Result; requires write grants. +prepareFilesystem root = H.liftIO (Store.ensureStore root) + +-- Native v2 adapter for the same domain interface. Limits are explicit byte +-- budgets, not heap guarantees. Roots/paths follow OS authority, not v1 grants. +-- Objects are immutable: verify and sync every winner; never repair corruption +-- by overwriting it. Loads authenticate the exact bytes returned to the caller. +loadV2 root limit digest = do V2.onOk + bytes <- Storage.readBytes (Store.bundleObjectPath root digest) limit + actual <- Storage.hashBytes limit bytes + case B.equal? actual digest of + (t t) = V2.pure (B.ok bytes t) + _ = V2.pure (B.err ["integrity-failed" "artifacts/load" "digest mismatch" "unknown"] t) + +publishV2 root limit bytes = do V2.onOk + digest <- Storage.hashBytes limit bytes + case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = let directory = Store.objectDir root (Store.hashShard checked) in do V2.onOk + V2.ensureDirectory directory + V2.syncDirectory (Store.pathJoin root "objects") + Storage.publishBytes directory (Store.bundleObjectPath root checked) 0 limit bytes + V2.pure (B.ok checked t) + _ = V2.fail "Invalid artifact digest reply" + +filesystemV2 root limit service "load" digest = case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = H.liftIO (loadV2 root limit checked) + (t, (message, _)) = H.pure (B.err ["invalid-digest" message] t) + _ = H.fail "invalid-contract" service "load" digest +filesystemV2 root limit service "publish" bytes = case B.checkContract bytesC bytes of + ((t t), (checked, t)) = H.liftIO (publishV2 root limit checked) + (t, (message, _)) = H.fail "invalid-request" service "publish" message + _ = H.fail "invalid-contract" service "publish" bytes +filesystemV2 _ _ service operation payload = H.forward service operation payload +withFilesystemForV2 service root limit action = H.handle service (filesystemV2 root limit service) action +withFilesystemV2 root limit action = withFilesystemForV2 "artifacts" root limit action + +-- Parent-first initialization; the configured root's parent must already exist. +-- Sync directory entries under host filesystem guarantees, not a crash theorem. +ensureV2 path = do V2.onOk + V2.ensureDirectory path + V2.syncDirectory (Storage.parentPath path) +prepareFilesystemV2 root = H.liftIO (do V2.onOk + ensureV2 root + ensureV2 (Store.pathJoin root "tmp") + ensureV2 (Store.pathJoin root "objects") + ensureV2 (Store.pathJoin root "aliases") + ensureV2 (Store.pathJoin (Store.pathJoin root "aliases") "names") + ensureV2 (Store.pathJoin (Store.pathJoin root "aliases") "packages") + ensureV2 (Store.pathJoin root "manifests") + V2.pure (B.ok t t)) + +-- Bounded producer, existing registry layout. The shard is chosen only after +-- hashing the complete stream, never by buffering the whole body or guessing its +-- digest. Temporary and object directories must share a filesystem. +streamDestination root digest = H.liftIO (do V2.onOk + V2.ensureDirectory (Store.objectDir root (Store.hashShard digest)) + V2.syncDirectory (Store.pathJoin root "objects") + V2.pure (B.ok (Store.bundleObjectPath root digest) t)) +publishStreamV2 root expected producer = Stream.publishWith + (Store.pathJoin root "tmp") expected (streamDestination root) producer diff --git a/test/fixtures/reference-source/61799bafcab20f90faea5562f2c25c89bdab164571ac6073e4c029c1f86e5b66.tri b/test/fixtures/reference-source/61799bafcab20f90faea5562f2c25c89bdab164571ac6073e4c029c1f86e5b66.tri @@ -0,0 +1,13 @@ +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export runState +-- A sequential state machine passes its state explicitly. There is no implicit +-- host get/put slot and no claim of a mutable cell shared across child tasks. +line writer state = Text.writeText writer (B.append "state: " (B.append state "\n")) +runState writer "idle" = IO.require (line writer "idle") (_ : runState writer "running") +runState writer "running" = IO.require (line writer "running") (_ : runState writer "done") +runState writer "done" = IO.require (line writer "done") (_ : IO.pure t) +runState _ _ = IO.fail "Unknown state" +main = IO.io (IO.require IO.stdout (writer : runState writer "idle")) diff --git a/test/fixtures/reference-source/64ba09f05b561ed1effe383dfad832e354f306a40675f478c7aecfaefd6d78d5.tri b/test/fixtures/reference-source/64ba09f05b561ed1effe383dfad832e354f306a40675f478c7aecfaefd6d78d5.tri @@ -0,0 +1,28 @@ +!import base * +!export * + +-- Ordered predicate/action rows. Each row is exactly [predicate action]. +-- First match wins; no match or a malformed reached row returns Leaf. +-- Unselected actions and subsequent rows remain lazy. +match value rules = (let rec + walk remaining = case remaining of + [rule | rest] = (case rule of + [test action] = (case test value of + t = walk rest + _ = action value) + _ = t) + _ = t + in walk rules) + +otherwise = const true + +-- cond stores test/action thunks, each called with Leaf. +cond rules = (let rec + walk remaining = case remaining of + [(testK, actionK) | rest] = (case testK t of + t = walk rest + _ = actionK t) + _ = t + in walk rules) + +guard testK actionK = pair testK actionK diff --git a/test/fixtures/reference-source/64e2248dbd4a05fc78b9d92cd775bb111951af95e384b91000e7f7aa32410013.tri b/test/fixtures/reference-source/64e2248dbd4a05fc78b9d92cd775bb111951af95e384b91000e7f7aa32410013.tri @@ -0,0 +1,147 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export readBytes +!export hashBytes +!export publishBytes +!export readTree +!export publishTree +!export parentPath + +-- Caller-selected limits and directories. Whole byte lists are resident source +-- values; transport chunks have a fixed bound. Paths follow OS authority. +problem op detail = ["invalid-data" op detail "unknown"] +checkedLimit limit next = case B.isNat? limit of + (t t) = case N.lte limit 9223372036854775807 of + (t t) = next limit + _ = IO.pure (B.err ["invalid-bound" "storage/limit" "invalid byte limit" "none"] t) + _ = IO.pure (B.err ["invalid-bound" "storage/limit" "invalid byte limit" "none"] t) +split 0 rest = [[] rest] +split _ [] = [[] []] +split count [c | rest] = case split (N.predecessor count) rest of + [prefix after] = [(B.pair c prefix) after] + _ = [] +split _ _ = [] +flatten chunks = B.foldr (suffix chunk : B.append chunk suffix) [] (B.reverse chunks) +readCount 0 = 1 +readCount remaining = case N.lte remaining 4096 of + (t t) = remaining + _ = 4096 +readLoop reader remaining chunks = do IO.onOk + received <- IO.readSome reader (readCount remaining) + case received of + ("eof", t) = IO.pure (B.ok (flatten chunks) t) + ("data", bytes) = case N.lte (B.length bytes) remaining of + (t t) = readLoop reader (N.subtract remaining (B.length bytes)) (B.pair bytes chunks) + _ = IO.pure (B.err ["invalid-bound" "storage/read" "file exceeds byte limit" "unknown"] t) + _ = IO.fail "Invalid storage read reply" +-- May consume one excess byte in the EOF probe at the budget boundary. +readBytes path limit = checkedLimit limit (count : IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + readLoop reader count [])) + +hashLoop digest remaining bytes = case split 4096 bytes of + [[] []] = IO.finishDigest digest + [part rest] = case N.lte (B.length part) remaining of + (t t) = do IO.onOk + IO.updateDigest digest part + hashLoop digest (N.subtract remaining (B.length part)) rest + _ = IO.pure (B.err ["invalid-bound" "storage/hash" "bytes exceed limit" "none"] t) + _ = IO.pure (B.err ["invalid-argument" "storage/hash" "improper byte list" "none"] t) +hashBytes limit bytes = checkedLimit limit (count : IO.scope (owner : do IO.onOk + digest <- IO.openDigest owner "sha256" + hashLoop digest count bytes)) +writeLoop writer bytes = case split 4096 bytes of + [[] []] = IO.pure (B.ok t t) + [part rest] = do IO.onOk + IO.writeAll writer part + writeLoop writer rest + _ = IO.fail "Invalid preflighted byte list" + +verifyBytes reader remaining = do IO.onOk + received <- IO.readSome reader 4096 + case received of + ("eof", t) = case remaining of + [] = IO.pure (B.ok t t) + _ = IO.pure (B.err (problem "storage/verify" "different bytes") t) + ("data", bytes) = case split (B.length bytes) remaining of + [expected rest] = case B.equal? bytes expected of + (t t) = verifyBytes reader rest + _ = IO.pure (B.err (problem "storage/verify" "different bytes") t) + _ = IO.fail "Invalid preflighted byte list" + _ = IO.fail "Invalid verification read reply" +verified owner directory destination verify created = do IO.onOk + reader <- IO.openReader owner destination + verify reader + IO.syncFile reader + IO.syncDirectory directory + IO.pure (B.ok created t) +-- Immutable names verify every winner, through EOF, before syncing its opened +-- inode and directory. Mismatches remain intact for diagnosis. Mutable +-- replacement acknowledges its own stage publication; a concurrent writer can +-- legitimately replace the alias immediately afterward. +publishBytes directory destination replace limit bytes = case replace of + 0 = do IO.onOk + hashBytes limit bytes + IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + writeLoop stage bytes + IO.sealStage stage + status <- IO.publishNew stage destination + case status of + "published" = verified owner directory destination (reader : verifyBytes reader bytes) (t t) + "exists" = verified owner directory destination (reader : verifyBytes reader bytes) t + _ = IO.fail "Invalid publication reply") + 1 = do IO.onOk + hashBytes limit bytes + IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + writeLoop stage bytes + IO.sealStage stage + IO.publishReplace stage destination + IO.pure (B.ok (t t) t)) + _ = IO.pure (B.err ["invalid-argument" "storage/publish" "invalid replacement flag" "none"] t) + +verifyTree reader limit tree = do IO.onOk + IO.verifyIndexedTree reader limit tree + end <- IO.readSome reader 1 + case end of + ("eof", t) = IO.pure (B.ok t t) + _ = IO.pure (B.err (problem "storage/verify" "trailing bytes") t) +publishTree directory destination limit expected tree = do IO.onOk + info <- IO.hashIndexedTree limit tree + case info of + [_ actual] = case B.equal? actual expected of + (t t) = IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + IO.writeIndexedTree stage limit expected tree + IO.sealStage stage + status <- IO.publishNew stage destination + case status of + "published" = verified owner directory destination (reader : verifyTree reader limit tree) (t t) + "exists" = verified owner directory destination (reader : verifyTree reader limit tree) t + _ = IO.fail "Invalid publication reply") + _ = IO.pure (B.err ["integrity-failed" "storage/publish" "digest mismatch" "none"] t) + _ = IO.fail "Invalid indexed hash reply" +readTree directory path limit expected = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + size <- IO.fileSize reader + IO.readIndexedTree reader directory limit size expected) + +-- POSIX lexical parent, retaining separators and literal '..' components. +-- Callers pass complete paths to the native path validators for OS resolution. +slashes [] = t t +slashes [47 | rest] = slashes rest +slashes _ = t +trimSeparators [47 | rest] = trimSeparators rest +trimSeparators rest = rest +parentReverse [] = "." +parentReverse [47 | rest] = case slashes rest of + (t t) = B.pair 47 (B.reverse rest) + _ = B.reverse rest +parentReverse [_ | rest] = parentReverse rest +parentReverse _ = "." +parentPath [] = "." +parentPath path = case trimSeparators (B.reverse path) of + [] = path + rest = parentReverse rest diff --git a/test/fixtures/reference-source/65c006672139d98c27b5022a8641c73b6d0c9c29dd42f6cfe8b4820928664807.tri b/test/fixtures/reference-source/65c006672139d98c27b5022a8641c73b6d0c9c29dd42f6cfe8b4820928664807.tri @@ -0,0 +1,20 @@ +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export run +!export writeThenRead +-- Check write/close success before opening a reader; do not discard the write +-- Result. Select the output path explicitly, rather than clobbering shared /tmp. +writeThenRead path text = IO.scope (owner : do IO.onOk + writer <- IO.openWriter owner path + Text.writeText writer text + IO.close writer + reader <- IO.openReader owner path + Text.readText reader 1048576) +main = IO.io (IO.require (IO.environmentVariable "TRICU_DEMO_OUTPUT") (selection : case selection of + ["value" path] = IO.require (writeThenRead path "hello from tricu") (text : + IO.require IO.stdout (writer : IO.require (Text.writeText writer (B.append text "\n")) IO.pure)) + ["missing"] = IO.fail "Set TRICU_DEMO_OUTPUT to an explicit output path" + _ = IO.fail "Invalid environment reply")) +run path text = IO.io (writeThenRead path text) diff --git a/test/fixtures/reference-source/677c3b5487e0715a82a9ec6d4c5afd4ac83433ec06350cafcc7c2147e8325b7e.tri b/test/fixtures/reference-source/677c3b5487e0715a82a9ec6d4c5afd4ac83433ec06350cafcc7c2147e8325b7e.tri @@ -0,0 +1,153 @@ +!import base as B +!import io_v2 as V2 +!export pure +!export bind +!export request +!export checkedRequest +!export handle +!export forward +!export label +!export fail +!export liftIO +!export runPure +!export runIO +!export collect +!export view +!export diagnostic +!export scopeV2 +!export spawnV2 +!export runIOV2 + +-- Request/response effects, separate from both host IO and base.exceptE. +-- Handler callbacks return computations yielding one reply, not resumptions. +pure value = B.pair 100 value +bind action next = B.pair 101 (B.pair action next) +request service operation argument = B.pair 102 (B.pair [service operation argument] pure) +handle service handler action = B.pair 104 (B.pair [service handler] action) +label name action = B.pair 105 (B.pair [name] action) +fail kind service operation detail = B.pair 103 [kind service operation detail] + +-- Handlers execute outside their own installation scope, so forwarding is an +-- ordinary request there. At other call sites this is just request, not a way +-- to bypass the current environment. Names are routing values, not authority. +forward = request +liftIO action = request "host.io" "run" action + +-- Boundary constructors, not host.io requests: normalization must snapshot the +-- actual lexical environment, including when invoked inside a handler callback. +-- Child frames start empty; immutable routing and label values are inherited. +scopeV2 body = B.pair 107 body +spawnV2 supervised keep handles body = B.pair 108 [supervised keep handles body] + +-- Diagnostics are data: kind, service, operation, detail, outer-to-inner labels. +diagnostic kind service operation detail labels = [kind service operation detail labels] + +checkedRequest service operation inputC outputC argument = + case B.checkContract inputC argument of + ((t t), (checked, t)) = do bind + reply <- request service operation checked + case B.checkContract outputC reply of + ((t t), (value, t)) = pure value + (t, (message, _)) = fail "invalid-response" service operation message + _ = fail "invalid-response-contract" service operation reply + (t, (message, _)) = fail "invalid-request" service operation message + _ = fail "invalid-request-contract" service operation argument + +findHandler wanted [[service handler] | outer] = B.lazyBool + (_ : B.pair handler outer) + (_ : findHandler wanted outer) + (B.equal? wanted service) +findHandler _ _ = t + +-- Normalized views: 100 value; 103 complete diagnostic; 106 ([service op arg], +-- outer-to-inner labels, reply continuation). View stops at the first request. +-- Frames retain caller environments and labels; handlers use the environment +-- suffix outside their installation, but keep the requesting execution's labels. +normalize action environment frames labels = let + broken = B.pair 103 (diagnostic "invalid-computation" t t t labels) + resume value [] = pure value + -- Force the popped stack tail's outer constructor before reusing it. A lazy + -- projection here can keep consumed frames (and their old chunks) reachable. + resume value [[saved savedLabels next] | rest] = case rest of + [] = normalize (next value) saved [] savedLabels + more{[_ | _]} = normalize (next value) saved more savedLabels + _ = broken + resume _ _ = broken + dispatch event{[service operation argument]} next = case findHandler service environment of + (handler, outer) = normalize (handler operation argument) outer + (B.pair [environment labels next] frames) labels + _ = B.pair 106 [event labels (reply : normalize (next reply) environment frames labels)] + dispatch _ _ = broken + in case action of + (100, value) = resume value frames + (101, (first, next)) = normalize first environment (B.pair [environment labels next] frames) labels + (102, (event, next)) = dispatch event next + (103, [kind service operation detail]) = B.pair 103 (diagnostic kind service operation detail labels) + (104, (entry{[service handler]}, body)) = normalize body (B.pair entry environment) + (B.pair [environment labels pure] frames) labels + (105, (extraLabels, body)) = normalize body environment + (B.pair [environment labels pure] frames) (B.append labels extraLabels) + (107, body) = B.pair 107 [labels + (owner : normalize (body owner) environment [] labels) + (reply : resume reply frames)] + (108, [supervised keep handles body]) = B.pair 108 [supervised keep handles labels + (fresh : normalize (body fresh) environment [] labels) + (reply : resume reply frames)] + _ = broken + +view action = normalize action [] [] [] + +-- Execution Result is distinct from an operation's ordinary Result value. +-- Pure interpretation rejects every unhandled request, including host IO. +runPure action = case view action of + (100, value) = B.ok value t + (103, problem) = B.err problem t + (106, [[service operation argument] labels next]) = B.err + (diagnostic "unhandled-request" service operation argument labels) t + _ = B.err (diagnostic "invalid-view" t t t []) t + +-- Handler failures become terminal v2 outcomes. Ordinary service Results remain +-- values. Labels/continuations stay lazy; only the label list spine is checked. +runHostV2 current = case current of + (100, value) = V2.pure value + (103, problem) = V2.fail problem + (106, [["host.io" "run" action] labels next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind action (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + (106, [[service operation argument] labels next]) = V2.fail + (diagnostic "unhandled-request" service operation argument labels) + (107, [labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind (V2.scope (owner : runHostV2 (body owner))) + (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + (108, [supervised keep handles labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind (V2.spawn supervised keep handles (fresh : runHostV2 (body fresh))) + (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + _ = V2.fail (diagnostic "invalid-view" t t t []) +runIOV2 action = runHostV2 (view action) +runIO = runIOV2 + +-- Stateful collection as a boundary transformer, not mutable handler state. +-- Inner handlers run first. Other residual requests are relayed to outer +-- handlers, with their labels scoped ONLY around the forwarded request. +-- It returns [value chronological-events]. Failures retain their labels. +collect service operation argumentC reply action = let rec + walk current reversed = case current of + (100, value) = pure [value (B.reverse reversed)] + (103, [kind failedService failedOperation detail labels]) = B.pair 105 + (B.pair labels (fail kind failedService failedOperation detail)) + (106, [event{[wanted op argument]} labels next]) = B.lazyBool + (_ : case B.checkContract argumentC argument of + ((t t), (checked, t)) = walk (next reply) (B.pair checked reversed) + (t, (message, _)) = B.pair 105 (B.pair labels (fail "invalid-request" service operation message)) + _ = B.pair 105 (B.pair labels (fail "invalid-request-contract" service operation argument))) + (_ : bind (B.pair 105 (B.pair labels (request wanted op argument))) + (answer : walk (next answer) reversed)) + (B.and? (B.equal? wanted service) (B.equal? op operation)) + (107, [labels body next]) = B.pair 105 (B.pair labels + (fail "state-boundary" service operation "Install collection inside the scoped body")) + (108, [supervised keep handles labels body next]) = B.pair 105 (B.pair labels + (fail "state-boundary" service operation "Install child-local collection explicitly")) + _ = fail "invalid-view" t t t + in walk (view action) [] diff --git a/test/fixtures/reference-source/708216b9a9b131be7a24abacef443e0322b2ff78f84b929245f486c1b2ac1d02.tri b/test/fixtures/reference-source/708216b9a9b131be7a24abacef443e0322b2ff78f84b929245f486c1b2ac1d02.tri @@ -0,0 +1,108 @@ +!import base as B +!import http as HTTP +!import conversions as Convert +!import arboricx_server as Store +!import handlers as H +!import io_v2 as V2 +!import io_v2_storage as Storage +!import handler_log as Log +!import handler_artifacts as Artifacts +!export request +!export packageLookupV2 +!export stdoutLogV2 +!export writeLogV2 +!export routerV2 + +-- HTTP policy is separate from storage, logging, and the socket lifecycle. +-- Outcomes carry a status beside the complete response so logging does not +-- parse response bytes. Only completed application routes emit access events; +-- parser/protocol rejections remain owned by the scoped HTTP transport. +reply status bytes = H.pure [status bytes] +error status message = reply status (HTTP.errorResponse status message) + +load digest = do H.bind + result <- Artifacts.load digest + case result of + ((t t), (bytes, t)) = reply 200 (HTTP.response 200 Store.bundleContentType bytes) + (t, (["invalid-digest" message], t)) = error 400 message + _ = error 404 "Artifact unavailable\n" + +publish bytes = do H.bind + result <- Artifacts.publish bytes + case result of + ((t t), (digest, t)) = reply 201 (HTTP.createdResponse digest) + _ = error 400 "Upload failed\n" + +-- Package storage is an explicit captured service: its identity is stable for +-- this server. Contextual artifact overrides need no package-path authority. +-- The HTTP parser supplies canonical target bytes, as in the original server. + +package lookup target = do H.bind + result <- lookup target + case result of + ((t t), (bytes, t)) = reply 200 (HTTP.response 200 "application/vnd.arboricx.package-release" bytes) + (t, (["invalid-package" message], t)) = error 400 message + _ = error 404 "Package release not found" + +get lookup target = case Store.stripPrefix Store.packagePrefix target of + (t suffix) = package lookup suffix + _ = case Store.stripPrefix Store.bundleHashPrefix target of + (t digest) = load digest + _ = reply 404 HTTP.notFoundResponse + +route _ "GET" "/" _ _ = reply 200 (HTTP.htmlResponse Store.htmlLandingPage) +route _ "GET" "/_arboricx/health" _ _ = reply 200 (HTTP.okResponse "OK\n") +route lookup "GET" target _ _ = get lookup target +route _ "POST" "/_arboricx/bundle" _ body = publish body +route _ _ _ _ _ = reply 404 HTTP.notFoundResponse + +request lookup method target headers body = H.label "registry-request" <| do H.bind + outcome <- route lookup method target headers body + case outcome of + [status response] = do H.bind + logged <- Log.info status + case logged of + ((t t), (t, t)) = H.pure response + (t, (message, t)) = H.fail "application-failure" "logging" "emit" message + _ = H.fail "application-failure" "logging" "emit" "Invalid logger reply" + _ = H.fail "application-failure" "registry" "route" "Invalid route outcome" + +-- The captured package service remains independent of contextual artifact routes. +packageLookupV2 root limit target = case Store.packageTarget target of + (t (name, version)) = H.liftIO (Storage.readBytes + (Store.pathJoin (Store.pathJoin (Store.pathJoin root "packages") name) version) limit) + _ = H.pure (B.err ["invalid-package" "Invalid package name/version"] t) + +-- Status-only ASCII logging. Ordinary write failures remain Results; publication +-- precedes logging and is never rolled back when the logger fails. +writeLogV2 bytes = do V2.onOk + writer <- V2.stdout + V2.writeAll writer bytes + V2.pure (B.ok t t) +stdoutLogV2 ["info" fields status] = case B.checkContract B.nat? status of + ((t t), _) = H.liftIO (writeLogV2 + (B.append "registry status=" (B.append (Convert.showNumber status) "\n"))) + _ = H.pure (B.err "Invalid access-log status" t) +stdoutLogV2 _ = H.pure (B.err "Invalid access-log event" t) + +-- A supervised child contains request interpretation/logger failure without +-- weakening native fail-fast semantics or retaining completed task tickets. +-- Cancellation remains cancellation; a failure becomes a redacted HTTP response. +-- Socket ownership and framing are the listener's responsibility, not this router. +routerV2 root limit sink method target headers body = V2.scope (_ : do V2.require + spawned <- V2.spawn 1 1 [] (_ : H.runIOV2 + (request (packageLookupV2 root limit) method target headers body + |> Log.withContext [["method" method] ["target" target]] + |> Artifacts.withFilesystemV2 root limit + |> Log.withSink sink)) + case spawned of + [task ticket] = do V2.require + outcome <- V2.join ticket + case outcome of + ["completed" response] = V2.pure response + ["failed" _ _] = do V2.bind + writeLogV2 "registry request failed\n" + V2.pure (HTTP.errorResponse 500 "Internal Server Error\n") + ["cancelled" reason _] = V2.cancelled reason + _ = V2.fail "Invalid registry task outcome" + _ = V2.fail "Invalid registry spawn reply") diff --git a/test/fixtures/reference-source/73cfe94509cdbc3c234bf4dabfac1796f5c6cf92565fcdd5a673a865bb8cd65b.tri b/test/fixtures/reference-source/73cfe94509cdbc3c234bf4dabfac1796f5c6cf92565fcdd5a673a865bb8cd65b.tri @@ -0,0 +1,165 @@ +!import base as B +!import io_v2 as V2 +!export pure +!export bind +!export request +!export checkedRequest +!export checkWith +!export checkRequest +!export handle +!export forward +!export label +!export fail +!export liftIO +!export runPure +!export runIO +!export collect +!export view +!export diagnostic +!export scopeV2 +!export spawnV2 +!export runIOV2 + +-- Request/response effects. Handler callbacks return computations yielding one +-- reply. liftIO bridges host actions into this protocol. +pure value = B.pair 100 value +bind action next = B.pair 101 (B.pair action next) +request service operation argument = B.pair 102 (B.pair [service operation argument] pure) +handle service handler action = B.pair 104 (B.pair [service handler] action) +label name action = B.pair 105 (B.pair [name] action) +fail kind service operation detail = B.pair 103 [kind service operation detail] + +-- Handlers execute outside their own installation scope, so forwarding is an +-- ordinary request there. At other call sites it uses the current environment. +-- Service names select routes; host operations use OS process authority. +forward = request +liftIO action = request "host.io" "run" action + +-- Boundary constructors snapshot the actual lexical environment during +-- normalization, including when invoked inside a handler callback. +-- Child frames start empty; immutable routing and label values are inherited. +scopeV2 body = B.pair 107 body +spawnV2 supervised keep handles body = B.pair 108 [supervised keep handles body] + +-- Diagnostics are data: kind, service, operation, detail, outer-to-inner labels. +diagnostic kind service operation detail labels = [kind service operation detail labels] + +-- Check once, then select one continuation. Success passes the checked value; +-- rejection passes its diagnostic; a malformed reply passes the original value. +-- Each continuation owns its outcome policy and only the selected one runs. +checkWith contract value rejected malformed next = case B.checkContract contract value of + ((t t), (checked, t)) = next checked + (t, (message, _)) = rejected message + _ = malformed value + +-- Request-boundary policy carries routing details into handler diagnostics. +checkRequest service operation contract argument = checkWith contract argument + (message : fail "invalid-request" service operation message) + (original : fail "invalid-request-contract" service operation original) + +checkedRequest service operation inputC outputC argument = + checkRequest service operation inputC argument (checked : do bind + reply <- request service operation checked + checkWith outputC reply + (message : fail "invalid-response" service operation message) + (original : fail "invalid-response-contract" service operation original) + pure) + +findHandler wanted [[service handler] | outer] = B.lazyBool + (_ : B.pair handler outer) + (_ : findHandler wanted outer) + (B.equal? wanted service) +findHandler _ _ = t + +-- Normalized views: 100 value; 103 complete diagnostic; 106 ([service op arg], +-- outer-to-inner labels, reply continuation). View stops at the first request. +-- Frames retain caller environments and labels; handlers use the environment +-- suffix outside their installation, but keep the requesting execution's labels. +normalize action environment frames labels = let + broken = B.pair 103 (diagnostic "invalid-computation" t t t labels) + resume value [] = pure value + -- Force the popped stack tail's outer constructor before reusing it. A lazy + -- projection here can keep consumed frames (and their old chunks) reachable. + resume value [[saved savedLabels next] | rest] = case rest of + [] = normalize (next value) saved [] savedLabels + more{[_ | _]} = normalize (next value) saved more savedLabels + _ = broken + resume _ _ = broken + dispatch event{[service operation argument]} next = case findHandler service environment of + (handler, outer) = normalize (handler operation argument) outer + (B.pair [environment labels next] frames) labels + _ = B.pair 106 [event labels (reply : normalize (next reply) environment frames labels)] + dispatch _ _ = broken + in case action of + (100, value) = resume value frames + (101, (first, next)) = normalize first environment (B.pair [environment labels next] frames) labels + (102, (event, next)) = dispatch event next + (103, [kind service operation detail]) = B.pair 103 (diagnostic kind service operation detail labels) + (104, (entry{[service handler]}, body)) = normalize body (B.pair entry environment) + (B.pair [environment labels pure] frames) labels + (105, (extraLabels, body)) = normalize body environment + (B.pair [environment labels pure] frames) (B.append labels extraLabels) + (107, body) = B.pair 107 [labels + (owner : normalize (body owner) environment [] labels) + (reply : resume reply frames)] + (108, [supervised keep handles body]) = B.pair 108 [supervised keep handles labels + (fresh : normalize (body fresh) environment [] labels) + (reply : resume reply frames)] + _ = broken + +view action = normalize action [] [] [] + +-- Execution Result is distinct from an operation's ordinary Result value. +-- Pure interpretation rejects every unhandled request, including host IO. +runPure action = case view action of + (100, value) = B.ok value t + (103, problem) = B.err problem t + (106, [[service operation argument] labels next]) = B.err + (diagnostic "unhandled-request" service operation argument labels) t + _ = B.err (diagnostic "invalid-view" t t t []) t + +-- Handler failures become terminal v2 outcomes. Ordinary service Results remain +-- values. Labels/continuations stay lazy; only the label list spine is checked. +runHostV2 current = case current of + (100, value) = V2.pure value + (103, problem) = V2.fail problem + (106, [["host.io" "run" action] labels next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind action (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + (106, [[service operation argument] labels next]) = V2.fail + (diagnostic "unhandled-request" service operation argument labels) + (107, [labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind (V2.scope (owner : runHostV2 (body owner))) + (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + (108, [supervised keep handles labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = V2.bind (V2.spawn supervised keep handles (fresh : runHostV2 (body fresh))) + (reply : runHostV2 (next reply)) + _ = V2.fail (diagnostic "invalid-view" t t t []) + _ = V2.fail (diagnostic "invalid-view" t t t []) +runIOV2 action = runHostV2 (view action) +runIO = runIOV2 + +-- Collection threads its accumulator through a boundary transformer. +-- Inner handlers run first. Other residual requests are relayed to outer +-- handlers, with their labels scoped ONLY around the forwarded request. +-- It returns [value chronological-events]. Failures retain their labels. +collect service operation argumentC reply action = let rec + walk current reversed = case current of + (100, value) = pure [value (B.reverse reversed)] + (103, [kind failedService failedOperation detail labels]) = B.pair 105 + (B.pair labels (fail kind failedService failedOperation detail)) + (106, [event{[wanted op argument]} labels next]) = B.lazyBool + (_ : case B.checkContract argumentC argument of + ((t t), (checked, t)) = walk (next reply) (B.pair checked reversed) + (t, (message, _)) = B.pair 105 (B.pair labels (fail "invalid-request" service operation message)) + _ = B.pair 105 (B.pair labels (fail "invalid-request-contract" service operation argument))) + (_ : bind (B.pair 105 (B.pair labels (request wanted op argument))) + (answer : walk (next answer) reversed)) + (B.and? (B.equal? wanted service) (B.equal? op operation)) + (107, [labels body next]) = B.pair 105 (B.pair labels + (fail "state-boundary" service operation "Install collection inside the scoped body")) + (108, [supervised keep handles labels body next]) = B.pair 105 (B.pair labels + (fail "state-boundary" service operation "Install child-local collection explicitly")) + _ = fail "invalid-view" t t t + in walk (view action) [] diff --git a/test/fixtures/reference-source/79c07186e9a3a36ce4c1de54b331c42dec6b95aa974156677c9336fd1c0849d7.tri b/test/fixtures/reference-source/79c07186e9a3a36ce4c1de54b331c42dec6b95aa974156677c9336fd1c0849d7.tri @@ -0,0 +1,18 @@ +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export worker +-- Structured children return work records; the parent serializes their output. +-- Task completion does not imply deterministic scheduling or atomic shared writes. +worker message = IO.require IO.yield (_ : IO.pure [(B.append "working: " message) (B.append message "-result")]) +consume writer [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" [message value]] = IO.require (Text.writeText writer (B.append message "\n")) (_ : IO.pure value) + _ = IO.fail outcome) +consume _ _ = IO.fail "Invalid spawn reply" +main = IO.io (IO.scope (_ : do IO.require + first <- IO.spawn 0 1 [] (_ : worker "job1") + second <- IO.spawn 0 1 [] (_ : worker "job2") + writer <- IO.stdout + IO.bind (consume writer first) (a : IO.bind (consume writer second) (b : + IO.require (Text.writeText writer (B.append "Got " (B.append a (B.append " and " (B.append b "\n"))))) IO.pure)))) diff --git a/test/fixtures/reference-source/7bce76817a1c9a6df5253b7f983a395d833e3622533a2d209c12a5c0f111aa5f.tri b/test/fixtures/reference-source/7bce76817a1c9a6df5253b7f983a395d833e3622533a2d209c12a5c0f111aa5f.tri @@ -0,0 +1,206 @@ +!import prelude * +!import io * +!import http * +!import socket * +!import native_math as Nat +!export * + +-- Store root is trusted configuration. These pure path helpers are not guards; +-- every read/write entrypoint must validate a hash before using it as a path. +pathJoin parent child = append parent (append "/" child) +objectDir root shard = pathJoin (pathJoin root "objects") shard + +hashShard [a | [b | [c | _]]] = [a b c] +hashShard [a | [b | _]] = [a b] +hashShard [a | _] = [a] +hashShard _ = [] + +bundleObjectPath root hash = pathJoin (objectDir root (hashShard hash)) (append hash ".arboricx") + +-- Exact canonical lowercase SHA-256 spelling. Literal patterns, unlike numeric +-- range predicates, reject malformed numeral trees without normalizing them. +-- Disjoint compact-bit ranges: 48..55, 56..57, 97, 98..99, +-- 100..101, 102. Every captured bit is checked as an exact Boolean. +bundleBit? t = true +bundleBit? (t t) = true +bundleBit? _ = false +bundleHexByte? (a, (b, (c, 6))) = and? (bundleBit? a) (and? (bundleBit? b) (bundleBit? c)) +bundleHexByte? (a, 28) = bundleBit? a +bundleHexByte? 97 = true +bundleHexByte? (a, 49) = bundleBit? a +bundleHexByte? (a, 50) = bundleBit? a +bundleHexByte? 102 = true +bundleHexByte? _ = false + +-- The counter is internal and canonical. At zero inspect only the list shape: +-- never force the 65th byte or its suffix, even for arbitrary malformed trees. +bundleHashBytes [] 0 = true +bundleHashBytes _ 0 = false +bundleHashBytes [byte | rest] remaining = case (bundleHexByte? byte) of + ^true = bundleHashBytes rest (Nat.subtract remaining 1) + _ = false + +bundleHashBytes _ _ = false + +-- The fixed SHA-256 boundary uses structural fuel instead of subtracting a +-- compact-natural counter per character. Retain bundleHashBytes for callers +-- that explicitly supply a length. Both stop before inspecting a 65th element. +bundleHashFuel8 tail = t (t (t (t (t (t (t (t tail))))))) +bundleHashFuel64 = bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 + (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 t))))))) +bundleHashScan [] t = true +bundleHashScan _ t = false +bundleHashScan [byte | rest] (t fuel) = case bundleHexByte? byte of + ^true = bundleHashScan rest fuel + _ = false +bundleHashScan _ _ = false +bundleHashValid? hash = bundleHashScan hash bundleHashFuel64 +bundleHashC = guardC "expected 64 lowercase hexadecimal hash characters" bundleHashValid? + +-- Directory creation already tolerates existing directories in the Haskell +-- driver. Propagate real failures and stop; void would discard those failures. +ensureDir path = mapErrIO "createDirectory: " (createDirectory path) +ensureStore root = (do onOk_ + ensureDir root + ensureDir (pathJoin root "tmp") + ensureDir (pathJoin root "objects") + ensureDir (pathJoin root "aliases") + ensureDir (pathJoin (pathJoin root "aliases") "names") + ensureDir (pathJoin (pathJoin root "aliases") "packages") + ensureDir (pathJoin root "manifests") + pure (ok t t)) + +-- Hash opaque bytes ourselves: no public unchecked hash-taking writer. Validate +-- even the digest reply before constructing filesystem actions. Atomic writes +-- use host-managed unique staging in the destination directory, not hash.tmp. +-- Existing-object replacement remains supported. A post-publication sync error +-- can leave the new object visible; this is not rollback or bundle validation. +putBundle root bytes = (do onOk_ + hash <- mapErrIO "sha256Hex: " (sha256Hex bytes) + case (checkContract bundleHashC hash) of + ((t t), (value, _)) = (do onOk_ + ensureDir (objectDir root (hashShard value)) + mapErrIO "atomicWriteBytes: " (atomicWriteBytes (bundleObjectPath root value) bytes) + pure (ok value t)) + (t, (diagnostic, _)) = pure (err (append "sha256Hex: " diagnostic) t) + _ = pure (err "sha256Hex: Invalid hash contract result" t)) + +-- Translate contract failures before IO, preserving HTTP status/message roles. +getBundleByHash root hash = case (checkContract bundleHashC hash) of + ((t t), (value, _)) = (do bind + reply <- readFile (bundleObjectPath root value) + case reply of + ((t t), (bytes, _)) = pure (ok bytes t) + (t, (message, _)) = pure (err 404 message) + _ = pure (err 404 "Invalid read result")) + (t, (diagnostic, _)) = pure (err 400 diagnostic) + _ = pure (err 400 "Invalid hash contract result") + +-- A proper list prefix; return its exact suffix, without normalizing the tail. +stripPrefix [] input = just input +stripPrefix [wanted | prefix] [byte | rest] = case byte of + ^wanted = stripPrefix prefix rest + _ = nothing + +stripPrefix _ _ = nothing + +bundleHashPrefix = "/_arboricx/bundle/hash/" +bundlePath = "/_arboricx/bundle" +healthPath = "/_arboricx/health" +bundleContentType = "application/vnd.arboricx.bundle" + +htmlLandingPage = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width, initial-scale=1'><title>Arboricx Server</title></head><body><h1>Arboricx Server</h1><p>Bundle registry</p><p><a href='https://git.eversole.co/James/tricu'>Made with Love (and trees, lots of trees)</a></p></body></html>" + +bundleResponse bytes = response 200 bundleContentType bytes + +serveBundleHash root hash = (do bind + reply <- getBundleByHash root hash + case reply of + ((t t), (bytes, _)) = pure (bundleResponse bytes) + (t, (status, message)) = pure (errorResponse status message) + _ = pure (errorResponse 500 "Invalid bundle result")) + +healthRoute "GET" ^healthPath = pure (okResponse "OK\n") +healthRoute _ _ = pure notFoundResponse + +putBundleRoute root "POST" ^bundlePath body = (do bind + reply <- putBundle root body + case reply of + ((t t), (hash, _)) = pure (createdResponse hash) + (t, (message, _)) = pure (badRequestResponse (append "Upload failed: " message)) + _ = pure (errorResponse 500 "Invalid upload result")) + +putBundleRoute _ _ _ _ = pure notFoundResponse + +getBundleRoute root "GET" target = case (stripPrefix bundleHashPrefix target) of + (t hash) = serveBundleHash root hash + _ = pure notFoundResponse + +getBundleRoute _ _ _ = pure notFoundResponse + +-- Read-only exact-version index. Administrative publication happens locally; +-- this route never resolves dependencies or executes fetched code. HTTP input +-- bytes are canonical naturals. Scan at most 128 bytes per path component. +packagePrefix = "/_arboricx/package/" +packageAlphaNum? byte = (or? (Nat.inRange byte 48 57) + (or? (Nat.inRange byte 65 90) (Nat.inRange byte 97 122))) +packageByte? byte = (or? (packageAlphaNum? byte) + (or? (equal? byte 46) (or? (equal? byte 95) (equal? byte 45)))) +packageVersion [] reversed _ = just (reverse reversed) +packageVersion _ _ 0 = nothing +packageVersion [byte | rest] reversed remaining = case (packageByte? byte) of + ^true = packageVersion rest (pair byte reversed) (Nat.subtract remaining 1) + _ = nothing + +packageVersion _ _ _ = nothing +packageName [47 | [first | rest]] reversed _ = case (packageAlphaNum? first) of + ^true = case (packageVersion rest [first] 127) of + (t version) = just (pair (reverse reversed) version) + _ = nothing + _ = nothing + +packageName _ _ 0 = nothing +packageName [byte | rest] reversed remaining = case (packageByte? byte) of + ^true = packageName rest (pair byte reversed) (Nat.subtract remaining 1) + _ = nothing + +packageName _ _ _ = nothing +packageTarget [first | rest] = case (packageAlphaNum? first) of + ^true = packageName rest [first] 127 + _ = nothing + +packageTarget _ = nothing + +servePackage root target = case (packageTarget target) of + (t (name, version)) = (do bind + reply <- readFile (pathJoin (pathJoin (pathJoin root "packages") name) version) + case reply of + ((t t), (bytes, _)) = pure (response 200 "application/vnd.arboricx.package-release" bytes) + _ = pure (errorResponse 404 "Package release not found")) + _ = pure (badRequestResponse "Invalid package name/version") + +getRegistryRoute root target = case (stripPrefix packagePrefix target) of + (t suffix) = servePackage root suffix + _ = getBundleRoute root "GET" target + +arboricxRouter _ "GET" "/" _ _ = pure (htmlResponse htmlLandingPage) +arboricxRouter _ "GET" ^healthPath _ _ = healthRoute "GET" healthPath +arboricxRouter root "GET" target _ _ = getRegistryRoute root target +arboricxRouter root "POST" target _ body = putBundleRoute root "POST" target body +arboricxRouter _ _ _ _ _ = pure notFoundResponse + +arboricxHandler root client peer = httpHandlerIO (arboricxRouter root) client peer + +-- Close a newly acquired socket on ordinary bind/listen failures. As elsewhere, +-- finally does not catch terminal abortIO or host exceptions; successful serving +-- delegates scheduling/accept policy to serveForever, without a shutdown hook. +arboricxServer root addr port = (let + failed message = pure (err message t) + opened server = (let + rejected message = finally (failed message) (closeSocket_ server) + ready _ = serveForever server (arboricxHandler root) + in onResult_ (do onOk_ + bindSocket server addr port + listen server 128) + rejected ready) + in onResult_ socket failed opened) diff --git a/test/fixtures/reference-source/811886e206ba2912893ebba28afe4a4085b8838fc5a097b165ffb56637c93524.tri b/test/fixtures/reference-source/811886e206ba2912893ebba28afe4a4085b8838fc5a097b165ffb56637c93524.tri @@ -0,0 +1,90 @@ +!import base as B +!import native_math as N +!import http as HTTP +!import io_v2 as IO +!import io_v2_storage as Storage +!import handlers as H +!import arboricx_server as Store +!export write +!export publish +!export publishWith + +-- Source-owned upload policy. Only writes observed by this boundary advance +-- its byte count; producer acknowledgements report producer status. +write bytes = H.request "artifact-stream" "write" bytes +onOk action next = H.bind (H.liftIO action) (reply : case reply of + ((t t), (value, t)) = next value + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "host" "Invalid host Result") + +hashReader reader digest remaining = IO.onOk (IO.readSome reader 4096) (reply : case reply of + ("eof", t) = case remaining of + 0 = IO.finishDigest digest + _ = IO.pure (B.err "Existing artifact length mismatch" t) + ("data", bytes) = let count = HTTP.httpBodyLength bytes in case N.lte count remaining of + (t t) = IO.onOk (IO.updateDigest digest bytes) + (_ : hashReader reader digest (N.subtract remaining count)) + _ = IO.pure (B.err "Existing artifact length mismatch" t) + _ = IO.fail "Invalid read reply") + +-- Accept an existing name only after verifying its complete contents and length +-- independently with fresh reader/digest resources. +verifyWinner destination expected wanted = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner destination + digest <- IO.openDigest owner "sha256" + actual <- hashReader reader digest expected + case B.equal? actual wanted of + (t t) = do IO.onOk + IO.syncFile reader + IO.syncDirectory (Storage.parentPath destination) + IO.pure (B.ok t t) + _ = IO.pure (B.err "Existing artifact digest mismatch" t)) + +-- Destination policy runs only after exact-length completion and sealing. It +-- returns a handler Result path and can create a digest-derived shard. Native +-- publication remains generic and rejects cross-filesystem moves without copying. +finish destination expected stage digest = onOk (IO.finishDigest digest) (hash : + case B.checkContract Store.bundleHashC hash of + ((t t), (checked, t)) = onOk (IO.sealStage stage) (_ : + H.bind (destination checked) (reply : case reply of + ((t t), (path, t)) = onOk (IO.publishNew stage path) (outcome : case outcome of + "published" = H.pure (B.ok checked t) + "exists" = onOk (verifyWinner path expected checked) (_ : H.pure (B.ok checked t)) + _ = H.fail "invalid-response" "artifact-stream" "publish" "Invalid publication reply") + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "destination" "Expected destination Result")) + _ = H.fail "invalid-response" "artifact-stream" "digest" "Invalid digest reply") + +publish directory expected producer = publishWith directory expected + (digest : H.pure (B.ok (B.append directory (B.pair 47 digest)) t)) producer +publishWith directory expected destination producer = case B.checkContract B.nat? expected of + ((t t), (count, t)) = case N.lte count 67108864 of + (t t) = H.scopeV2 (owner : onOk (IO.openStage owner directory) (stage : + onOk (IO.openDigest owner "sha256") (digest : let rec + walk current remaining = case current of + (100, reply) = case reply of + ((t t), (t, t)) = case remaining of + 0 = finish destination count stage digest + _ = H.pure (B.err "Incomplete artifact stream" t) + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "artifact-stream" "producer" "Expected Result unit" + (103, [kind service operation detail labels]) = B.pair 105 + (B.pair labels (H.fail kind service operation detail)) + (106, [["artifact-stream" "write" bytes] labels next]) = + onOk (IO.writeAll stage bytes) (_ : case bytes of + [] = H.pure (B.err "Empty artifact chunk" t) + _ = let length = HTTP.httpBodyLength bytes in case N.lte length remaining of + (t t) = onOk (IO.updateDigest digest bytes) + (_ : walk (next (B.ok t t)) (N.subtract remaining length)) + _ = H.pure (B.err "Artifact exceeds declared length" t)) + (106, [[service operation argument] labels next]) = H.bind + (B.pair 105 (B.pair labels (H.request service operation argument))) + (reply : walk (next reply) remaining) + (107, [labels body next]) = B.pair 105 (B.pair labels + (H.fail "producer-boundary" "artifact-stream" "publish" "Producer must be sequential")) + (108, [supervised keep handles labels body next]) = B.pair 105 (B.pair labels + (H.fail "producer-boundary" "artifact-stream" "publish" "Producer must be sequential")) + _ = H.fail "invalid-view" "artifact-stream" "publish" "Invalid producer view" + in walk (H.view producer) count))) + _ = H.pure (B.err "Artifact length exceeds limit" t) + _ = H.pure (B.err "Invalid artifact length" t) diff --git a/test/fixtures/reference-source/8338f32bdf08b7743933809b2acce42899d0020e83c95cd0b4d25b0a8b5c07df.tri b/test/fixtures/reference-source/8338f32bdf08b7743933809b2acce42899d0020e83c95cd0b4d25b0a8b5c07df.tri @@ -0,0 +1,297 @@ +!import prelude * +!import parser * +!import native_math as Nat +!export * + +-- --------------------------------------------------------------------------- +-- Constants +-- --------------------------------------------------------------------------- + +maxHeaderBytes = 65536 +maxBodyBytes = 1048576 + +crlfcrlf = pair 13 (pair 10 (pair 13 (pair 10 t))) + +-- --------------------------------------------------------------------------- +-- Response construction +-- --------------------------------------------------------------------------- + +statusPhrases = + [(pair 200 "OK") + (pair 201 "Created") + (pair 204 "No Content") + (pair 400 "Bad Request") + (pair 404 "Not Found") + (pair 405 "Method Not Allowed") + (pair 413 "Content Too Large") + (pair 431 "Request Header Fields Too Large") + (pair 501 "Not Implemented") + (pair 505 "HTTP Version Not Supported")] + +lookupStatusPhrase code [(key, phrase) | rest] = case key of + ^code = phrase + _ = lookupStatusPhrase code rest + +lookupStatusPhrase _ _ = "Internal Server Error" + +statusPhrase code = lookupStatusPhrase code statusPhrases + +statusLine code phrase = append "HTTP/1.1 " (append (showNumber code) (append " " (append phrase "\r\n"))) +headerLine key value = append key (append ": " (append value "\r\n")) + +httpAppendResponseHeader accumulated (key, value) = append accumulated (headerLine key value) +-- Preserve the old pair-projection fallback for malformed response-header items. +httpAppendResponseHeader accumulated _ = append accumulated (headerLine "" "") + +buildResponse status headers body = (append + (statusLine status (statusPhrase status)) + (append (foldl httpAppendResponseHeader "" headers) (append "\r\n" body))) + +-- Count a proper response-body list in one pass. Unlike general listOf, +-- this boundary never transforms elements, so no validated copy is needed. +-- Force each successor's outer constructor before advancing: do not retain a +-- successor continuation for every byte. Input elements remain unforced. +-- Malformed list spines retain length's ordinary contract-failure Result. +httpBodyLength values = let rec + walk remaining count = triage count + (_ : contractErr "not a list" t) + (_ tail : case Nat.successor count of + next{(_, _)} = walk tail next + _ = contractErr "Invalid internal length counter" t) + remaining + in walk values 0 + +response status contentType body = (buildResponse status + [(pair "Content-Type" contentType) + (pair "Content-Length" (showNumber (httpBodyLength body))) + (pair "Connection" "close")] + body) + +emptyResponse status = (buildResponse status + [(pair "Content-Length" "0") (pair "Connection" "close")] + "") + +okResponse body = response 200 "text/plain; charset=utf-8" body +textResponse body = response 200 "text/plain; charset=utf-8" body +jsonResponse body = response 200 "application/json" body +htmlResponse body = response 200 "text/html; charset=utf-8" body +createdResponse body = response 201 "text/plain; charset=utf-8" body +notFoundResponse = response 404 "text/plain; charset=utf-8" "Not found\n" +badRequestResponse message = response 400 "text/plain; charset=utf-8" message +errorResponse status message = response status "text/plain; charset=utf-8" message + +headerEndState 2 13 = 3 +headerEndState _ 13 = 1 +headerEndState 3 10 = 4 +headerEndState 1 10 = 2 +headerEndState _ _ = 0 + +httpHeadersOnly [byte | rest] state reversed = (let + accumulated = pair byte reversed + continue 4 = reverse accumulated + continue next = httpHeadersOnly rest next accumulated + in continue (headerEndState state byte)) +httpHeadersOnly _ _ reversed = reverse reversed + +headersOnly bytes = httpHeadersOnly bytes 0 [] + +responseForMethod "HEAD" response = headersOnly response +responseForMethod _ response = response + +-- --------------------------------------------------------------------------- +-- Request line parsing +-- --------------------------------------------------------------------------- + +-- HTTP/1.1 lines require CRLF. A bare CR/LF is an error, never a byte to +-- discard. Failure retains the offending suffix and prior consumption; EOF +-- cannot masquerade as a successfully parsed empty line. +httpLineError code found rest consumed = (parserReplyFail + (parserDiagnostic code "CRLF" found t ["http-line"]) + rest consumed false) + +httpScanLine [13 10 | rest] reversed = parserReplyOk (reverse reversed) rest true false +httpScanLine input{[13 | _]} reversed = httpLineError 2 13 input (not? (emptyList? reversed)) +httpScanLine input{[10 | _]} reversed = httpLineError 2 10 input (not? (emptyList? reversed)) +httpScanLine [byte | rest] reversed = httpScanLine rest (pair byte reversed) +httpScanLine [] reversed = httpLineError 1 t [] (not? (emptyList? reversed)) +httpScanLine input reversed = httpLineError 2 input input (not? (emptyList? reversed)) + +httpLineReply input = httpScanLine input [] +httpBadRequest = err 400 "Bad Request\n" + +-- Unlike the old pair-only helper, this Result distinguishes a blank CRLF +-- line from truncated/malformed input. Both callers propagate the failure. +readLineBytes input = (parserMatchReply + (_ _ _ _ : httpBadRequest) + (line rest _ _ : ok line rest) + (httpLineReply input)) + +-- The finite request line is already framed by readLineBytes. Deliberately +-- retain the historical three-fields grammar: empty fields are permitted, +-- but a third separating space is an error. The parser reply exposes byte +-- consumption to new clients, while parseThreeWords preserves HTTP's public +-- Result/400 boundary and the original "Bad Request" remainder. +httpNonSpace? byte = not? (equal? byte 32) + +httpThreeWordsReply = (parserContext "request-line" (do parserBind + method <- parserTakeWhile httpNonSpace? + parserExpect 32 + target <- parserTakeWhile httpNonSpace? + parserExpect 32 + version <- parserTakeWhile httpNonSpace? + parserEof + parserPure (pair method (pair target version)))) + +parseThreeWords = (bs : + parserMatchReply + (diagnostic rest consumed committed : err 400 "Bad Request\n") + (words rest consumed committed : ok words rest) + (httpThreeWordsReply bs)) + +parseRequestLine input = case (readLineBytes input) of + ((t t), (line, rest)) = case (parseThreeWords line) of + ((t t), (request, _)) = ok request rest + _ = httpBadRequest + _ = httpBadRequest + +-- --------------------------------------------------------------------------- +-- Header parsing +-- --------------------------------------------------------------------------- + +-- Canonical byte input (0..255), as supplied by the socket/byte codecs. +-- Historical malformed numeral/bit behavior is intentionally not a contract. +toLowerAsciiByte byte = case (Nat.inRange byte 65 90) of + ^true = Nat.add byte 32 + _ = byte + +-- RFC 9110 token names, OWS, and field-value bytes. obs-text (128..255) +-- stays opaque; only the ASCII header name is lowercased. Reject obs-fold. +httpTokenByte? byte = (or? (Nat.letter byte) + (or? (Nat.digit byte) (any? (equal? byte) "!#$%&'*+-.^_`|~"))) +httpOws? byte = or? (equal? byte 32) (equal? byte 9) +httpFieldByte? byte = (or? (equal? byte 9) + (or? (Nat.printable byte) (Nat.inRange byte 128 255))) +httpTrimOws value = reverse (dropWhile httpOws? (reverse (dropWhile httpOws? value))) + +-- Parse exactly one finite line, excluding CRLF. Preserve parser diagnostics +-- and consumption, but never accept a valid prefix followed by invalid bytes. +httpHeaderLineReply = (parserContext "header-line" (do parserBind + name <- parserLabel "header-name" (parserSome (parserSatisfy httpTokenByte?)) + parserLabel "colon" (parserExpect 58) + value <- parserLabel "header-value" (parserBefore (parserTakeWhile httpFieldByte?) parserEof) + parserPure (pair (map toLowerAsciiByte name) (httpTrimOws value)))) + +-- Only an actual empty CRLF line ends the headers. The returned suffix is +-- opaque body data, even if it contains CR, LF, NUL or header-looking bytes. +httpParseHeaderLines input reversedHeaders = case (readLineBytes input) of + ((t t), ([], rest)) = ok (reverse reversedHeaders) rest + ((t t), (line, rest)) = case (httpHeaderLineReply line) of + ((t t), (header, _)) = httpParseHeaderLines rest (pair header reversedHeaders) + _ = httpBadRequest + _ = httpBadRequest + +parseHeaders input = httpParseHeaderLines input [] + +-- --------------------------------------------------------------------------- +-- Content-Length parsing +-- --------------------------------------------------------------------------- + +-- The accumulator never exceeds maxBodyBytes. A single checked step can +-- reach at most 10 * maxBodyBytes + 9; after that, validate digits without +-- constructing a larger number. Trailing junk still means 400, not 413. +httpBodyTooLarge = err 413 "Request body too large\n" + +httpLengthOverflow [] = httpBodyTooLarge +httpLengthOverflow [byte | rest] = (let + continue ^true = httpLengthOverflow rest + continue _ = httpBadRequest + in continue (Nat.digit byte)) +httpLengthOverflow _ = httpBadRequest + +httpLengthDigits [] value = ok (just value) t +httpLengthDigits [byte | rest] value = (let + digit ^true = (let + next = Nat.decimal value (Nat.subtract byte 48) + bounded ^true = httpLengthDigits rest next + bounded _ = httpLengthOverflow rest + in bounded (Nat.lte next maxBodyBytes)) + digit _ = httpBadRequest + in digit (Nat.digit byte)) +httpLengthDigits _ _ = httpBadRequest + +-- Input is a finite canonical byte list, with no OWS: parseHeaders already +-- trims field edges. Empty strings, signs, commas and interior whitespace fail. +parseContentLengthValue input{[_ | _]} = httpLengthDigits input 0 +parseContentLengthValue _ = httpBadRequest + +-- Input names are normalized by parseHeaders. Select the sole raw length +-- before decoding: duplicate fields and unsupported transfer coding are 400 +-- regardless of field order or whether a length would otherwise exceed limits. +-- This server supports Content-Length framing only, not chunked decoding. +httpSelectLength [] t = ok nothing t +httpSelectLength [] (t value) = parseContentLengthValue value +httpSelectLength [("transfer-encoding", _) | _] _ = httpBadRequest +httpSelectLength [("content-length", value) | rest] t = httpSelectLength rest (just value) +httpSelectLength [("content-length", _) | _] _ = httpBadRequest +httpSelectLength [(_, _) | rest] selected = httpSelectLength rest selected +httpSelectLength _ _ = httpBadRequest + +contentLength headers = httpSelectLength headers nothing + +-- --------------------------------------------------------------------------- +-- Body reading +-- --------------------------------------------------------------------------- + +bodyReadState remaining reversed rest = pair remaining (pair reversed rest) + +-- State is (remaining, (reversed body, unused input)); zero consumes nothing. +-- remaining is a canonical natural supplied by the bounded length decoder. +takeBodyBytes bytes 0 reversed = bodyReadState 0 reversed bytes +-- The zero equation above excludes underflow. Specialized predecessor avoids +-- a full-width comparison/subtraction per byte of a canonical body counter. +takeBodyBytes [byte | rest] remaining reversed = takeBodyBytes rest (Nat.predecessor remaining) (pair byte reversed) +takeBodyBytes _ remaining reversed = bodyReadState remaining reversed [] + +-- Retain the existing receive-size buckets, expressed as native comparisons +-- rather than bit shifts. Zero is handled before asking for another chunk. +httpRecvBucket remaining [size | rest] = (let + choose ^true = size + choose _ = httpRecvBucket remaining rest + in choose (Nat.gte remaining size)) +httpRecvBucket _ _ = 1 + +recvChunkMax4096 remaining = httpRecvBucket remaining [4096 1024 256 64 16] + +-- --------------------------------------------------------------------------- +-- Request validation +-- --------------------------------------------------------------------------- + +validMethod? "GET" = true +validMethod? "POST" = true +validMethod? "HEAD" = true +validMethod? _ = false + +validVersion? "HTTP/1.1" = true +validVersion? "HTTP/1.0" = true +validVersion? _ = false + +validTarget? [47 | _] = true +validTarget? _ = false + +-- Keep error precedence: method, version, then target. Headers are validated +-- separately; accepting them here does not select body framing. +validateRequest method target version _ = (let + targetChecked ^true = ok t t + targetChecked _ = httpBadRequest + versionChecked ^true = targetChecked (validTarget? target) + versionChecked _ = err 505 "HTTP Version Not Supported\n" + methodChecked ^true = versionChecked (validVersion? version) + methodChecked _ = httpBadRequest + in methodChecked (validMethod? method)) + +-- --------------------------------------------------------------------------- +-- Handler pipeline +-- --------------------------------------------------------------------------- + +routerMethod "HEAD" = "GET" +routerMethod method = method diff --git a/test/fixtures/reference-source/834a2c8b9c9007364036bb22159ca0c5050c1536e87c4c40bf5573efee863e30.tri b/test/fixtures/reference-source/834a2c8b9c9007364036bb22159ca0c5050c1536e87c4c40bf5573efee863e30.tri @@ -0,0 +1,25 @@ +!import base as B +!import conversions as C +!import native_math as N +!import io_v2 as IO +!export main +!export server +-- A deliberately sequential, bounded-stream echo demonstration. A stalled peer +-- holds the single connection slot; there is no idle timeout or availability SLA. +port ["ipv4" _ value] = value +port _ = 0 +ready listener = do IO.require + address <- IO.localAddress listener + output <- IO.stdout + IO.require (IO.writeAll output (B.append (C.showNumber (port address)) [10])) IO.pure +once listener = IO.scope (owner : IO.require (IO.acceptTCP listener owner) + (client : IO.require (IO.copyStream client client) (_ : IO.pure t))) +serve 0 _ = IO.pure t +serve count listener = IO.bind (once listener) (_ : serve (N.predecessor count) listener) +forever listener = IO.bind (once listener) (_ : forever listener) +server address count = IO.io (IO.scope (owner : do IO.require + listener <- IO.listenTCP owner address 5 + IO.bind (ready listener) (_ : serve count listener))) +main = IO.io (IO.scope (owner : do IO.require + listener <- IO.listenTCP owner (IO.ipv4 [127 0 0 1] 0) 5 + IO.bind (ready listener) (_ : forever listener))) diff --git a/test/fixtures/reference-source/870df776ac7142fac5aad7f0169dfd08b2934f2bfa61ad3ae1c9b949f8f75088.tri b/test/fixtures/reference-source/870df776ac7142fac5aad7f0169dfd08b2934f2bfa61ad3ae1c9b949f8f75088.tri @@ -0,0 +1,111 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!import io_v2_http as Transport +!import io_v2_artifacts as Stream +!import http as HTTP +!import handlers as H +!import handler_artifacts as Artifacts +!import handler_log as Log +!import arboricx_handlers as Registry +!export pump +!export connection +!export serve +!export serveCount + +pump _ 0 _ = H.pure (B.ok t t) +pump client remaining initial = let + limit = N.minimum remaining 4096 + consume (unused, (reversed, rest)) = case reversed of + [] = H.bind (H.liftIO (IO.readSome client limit)) (reply : case reply of + ((t t), (("data", chunk), t)) = pump client remaining chunk + _ = H.pure (B.err "Unexpected end of request body" t)) + _ = H.bind (Stream.write (B.reverse reversed)) (reply : case reply of + ((t t), (t, t)) = pump client (N.subtract remaining (N.subtract limit unused)) rest + _ = H.pure reply) + consume _ = H.fail "invalid-response" "artifact-stream" "buffer" "Invalid body state" + in consume (HTTP.takeBodyBytes initial limit []) + +logged sink status response = H.bind + (Log.withSink sink (Log.withContext [["method" "POST"] ["target" "/_arboricx/bundle"]] (Log.info status))) + (reply : case reply of + ((t t), (t, t)) = H.pure response + _ = H.pure (HTTP.errorResponse 500 "Internal Server Error\n")) +upload root sink client count initial = H.bind + (Artifacts.publishStreamV2 root count (H.label "streaming-upload" (pump client count initial))) + (reply : case reply of + ((t t), (digest, t)) = logged sink 201 (HTTP.createdResponse digest) + (t, (_, t)) = logged sink 400 (HTTP.badRequestResponse "Upload failed\n") + _ = H.fail "invalid-response" "artifact-stream" "publish" "Invalid upload Result") +-- Supervised request interpretation retains the socket in its connection owner; +-- borrowing does not extend lifetime. Join before responding, so cleanup failure +-- cannot falsely acknowledge success. Cancellation is never rendered as HTTP 500. +response root sink client count initial = IO.scope (_ : IO.require + (IO.spawn 1 1 [] (_ : H.runIOV2 (upload root sink client count initial))) (spawned : case spawned of + [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" value] = IO.pure value + ["failed" _ _] = IO.pure (HTTP.errorResponse 500 "Internal Server Error\n") + ["cancelled" reason _] = IO.cancelled reason + _ = IO.fail "Invalid upload task outcome") + _ = IO.fail "Invalid upload spawn reply")) +boundedResponse root limit sink client count initial = case N.lte count limit of + (t t) = IO.bind (response root sink client count initial) (Transport.respond client "POST") + _ = Transport.respond client "POST" (HTTP.errorResponse 413 "Request body too large\n") +framed root limit sink client fields initial = case HTTP.contentLength fields of + ((t t), (t, _)) = boundedResponse root limit sink client 0 initial + ((t t), ((t count), _)) = boundedResponse root limit sink client count initial + (t, (status, message)) = Transport.respond client "POST" (HTTP.errorResponse status message) + _ = Transport.respond client "POST" (HTTP.badRequestResponse "Bad Request\n") +headers root limit sink client version raw = case HTTP.parseHeaders raw of + ((t t), (fields, initial)) = case HTTP.validateRequest "POST" "/_arboricx/bundle" version fields of + ((t t), _) = framed root limit sink client fields initial + (t, (status, message)) = Transport.respond client "POST" (HTTP.errorResponse status message) + _ = Transport.respond client "POST" (HTTP.badRequestResponse "Bad Request\n") + _ = Transport.respond client "" (HTTP.badRequestResponse "Bad Request\n") +line root limit sink client ((t t), (("POST", ("/_arboricx/bundle", version)), raw)) = + headers root limit sink client version raw +line root limit sink client parsed = Transport.handle (Registry.routerV2 root limit sink) client parsed +connection root limit sink client = IO.bind (Transport.recvHeaders client) (reply : case reply of + ((t t), (raw, _)) = line root limit sink client (HTTP.parseRequestLine raw) + (t, (431, _)) = Transport.respond client "" (HTTP.errorResponse 431 "Request Header Fields Too Large\n") + _ = Transport.respond client "" (HTTP.badRequestResponse "Bad Request\n")) + +worker root limit sink [client] = connection root limit sink client +worker _ _ _ _ = IO.fail "Invalid connection handoff" +settle ticket = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" _] = IO.pure t + ["failed" _ _] = IO.bind (Registry.writeLogV2 "registry connection failed\n") (_ : IO.pure t) + ["cancelled" reason _] = IO.cancelled reason + _ = IO.fail "Invalid connection task outcome") +drain [] = IO.pure t +drain [ticket | rest] = IO.bind (settle ticket) (_ : drain rest) +drain _ = IO.fail "Invalid connection tickets" +next (t count) = B.just (N.predecessor count) +next _ = t +-- FIFO bounded admission: consume the oldest ticket before accepting beyond the +-- configured window. No discarded tickets, detached tasks or unbounded backlog in +-- the language. A slow oldest connection can delay admission; no deadline promise. +admit _ _ _ _ _ _ (t 0) tickets = drain tickets +admit root limit sink owner listener window remaining tickets = case N.gte (B.length tickets) window of + (t t) = case tickets of + [ticket | rest] = IO.bind (settle ticket) (_ : admit root limit sink owner listener window remaining rest) + _ = IO.fail "Invalid admission window" + _ = IO.require (IO.acceptTCP listener owner) (client : IO.require + (IO.spawn 1 1 [client] (worker root limit sink)) (spawned : case spawned of + [_ ticket] = admit root limit sink owner listener window (next remaining) (B.append tickets [ticket]) + _ = IO.fail "Invalid connection spawn reply")) +start root limit sink address window remaining ready = case B.checkContract B.nat? limit of + ((t t), _) = startWindow root limit sink address window remaining ready + _ = IO.fail "Invalid artifact byte limit" +startWindow root limit sink address window remaining ready = case B.checkContract B.nat? window of + ((t t), _) = case B.and? (N.gte window 1) (N.lte window 256) of + (t t) = IO.scope (owner : do IO.require + H.runIOV2 (Artifacts.prepareFilesystemV2 root) + listener <- IO.listenTCP owner address 128 + IO.bind (ready listener) (_ : admit root limit sink owner listener window remaining [])) + _ = IO.fail "Connection window must be between 1 and 256" + _ = IO.fail "Invalid connection window" +serve root limit sink address window ready = start root limit sink address window t ready +serveCount root limit sink address window count ready = case B.checkContract B.nat? count of + ((t t), _) = start root limit sink address window (B.just count) ready + _ = IO.fail "Invalid connection count" diff --git a/test/fixtures/reference-source/87fa3ab396fb5177d0fcd0ab5375e94d3cd11876e2edf398e2f74a08d5f767e1.tri b/test/fixtures/reference-source/87fa3ab396fb5177d0fcd0ab5375e94d3cd11876e2edf398e2f74a08d5f767e1.tri @@ -0,0 +1,50 @@ +!import base as B +!import io_v2 as IO +!import io_v2_storage as Storage +!import io_v2_text as Text +!export * + +-- Compiler storage policy over IO v2, imported explicitly as compiler_io. +-- Explicit application policy: resident source/metadata bytes and encoded trees +-- are limited to 64 MiB. This is not a heap, expanded-tree or CPU limit. +byteLimit = 67108864 +version = 2 +io = IO.io +pure = IO.pure +bind = IO.bind +abortIO = IO.fail +thenIO first second = bind first (_ : second) +mapIO action f = bind action (value : pure (f value)) +void action = bind action (_ : pure t) +onOk action next = bind action (reply : B.matchResult (_ _ : pure reply) next reply) +onOk_ action next = IO.onOk action next +onResult action failed succeeded = bind action (reply : B.matchResult failed succeeded reply) +onResult_ action failed succeeded = onResult action (problem _ : failed problem) (value _ : succeeded value) + +readFile path = Storage.readBytes path byteLimit +sha256Hex bytes = Storage.hashBytes byteLimit bytes +readSourceFile path = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + Text.readSource reader byteLimit) +encodeSourceText text = Text.encodeText byteLimit text +-- Compiler callers create directories in parent-first order. A missing ancestor +-- is an error, not implicit recursive creation. Sync the parent entry before use. +createDirectory path = do IO.onOk + IO.ensureDirectory path + IO.syncDirectory (Storage.parentPath path) +fileExists path = bind (IO.statPath path) (reply : case reply of + ((t t), (["file" _], t)) = pure (B.ok (t t) t) + ((t t), (_, t)) = pure (B.ok t t) + (t, (["not-found" _ _ _], t)) = pure (B.ok t t) + _ = pure reply) +writeBytesIfAbsent path bytes = Storage.publishBytes (Storage.parentPath path) path 0 byteLimit bytes +atomicWriteBytes path bytes = Storage.publishBytes (Storage.parentPath path) path 1 byteLimit bytes +hashIndexedTree tree = do IO.onOk + info <- IO.hashIndexedTree byteLimit tree + case info of + [_ digest] = pure (B.ok digest t) + _ = IO.fail "Invalid indexed hash reply" +writeIndexedTreeIfAbsent path digest tree = + Storage.publishTree (Storage.parentPath path) path byteLimit digest tree +readIndexedTree path digest = + Storage.readTree (Storage.parentPath path) path byteLimit digest diff --git a/test/fixtures/reference-source/8b2e1cd6ef5acc8454a690875d2e8415869892f84c3fb5198c18fc8e665d0f32.tri b/test/fixtures/reference-source/8b2e1cd6ef5acc8454a690875d2e8415869892f84c3fb5198c18fc8e665d0f32.tri @@ -0,0 +1,181 @@ +!import base as B +!import native_math as N +!export * + +-- Structured IO with named, versioned facilities. +-- Native execution uses OS process authority. +io action = B.pair "tricuIO" (B.pair 2 action) +pure value = B.pair 0 value +bind action next = B.pair 1 (B.pair action next) +fail diagnostic = B.pair 2 diagnostic +scope body = B.pair 3 body +currentScope = B.pair 4 t + +-- Flags are canonical 0/1. Callback receives fresh handles. +-- Success is [task joinTicket]; non-result children receive t in the ticket slot. +spawn supervised keep handles body = B.pair 5 + (B.pair supervised (B.pair keep (B.pair handles body))) + +join ticket = B.pair 6 ticket +discard ticket = B.pair 7 ticket +cancel task reason = B.pair 8 (B.pair task reason) +cancelled reason = B.pair 9 reason + +-- Named, independently versioned host facilities; core sequencing stays separate. +request group revision operation arguments = B.pair 10 + (B.pair group (B.pair revision (B.pair operation arguments))) + +capabilities = request "runtime" 1 "capabilities" t +inspect action = request "runtime" 1 "inspect" action + +stdin = request "standard" 1 "input" t +stdout = request "standard" 1 "output" t +stderr = request "standard" 1 "error" t + +openReader owner path = request "file" 1 "open-reader" (B.pair owner path) +openWriter owner path = request "file" 1 "open-writer" (B.pair owner path) +openAppender owner path = request "file" 1 "open-appender" (B.pair owner path) +openUpdate owner path = request "file" 1 "open-update" (B.pair owner path) + +seekFile handle offset = request "file" 1 "seek" (B.pair handle offset) +filePosition handle = request "file" 1 "position" handle +syncFile handle = request "file" 1 "sync" handle + +syncDirectory path = request "directory" 1 "sync" path + +fileSize handle = request "file" 1 "size" handle +resizeFile handle size = request "file" 1 "resize" (B.pair handle size) + +statPath path = request "path" 1 "stat" path +lstatPath path = request "path" 1 "lstat" path +removeFile path = request "path" 1 "remove-file" path +renamePath source destination = request "path" 1 "rename" (B.pair source destination) + +createDirectory path = request "directory" 1 "create" path +removeDirectory path = request "directory" 1 "remove" path +openDirectory owner path = request "directory" 1 "open" (B.pair owner path) +nextDirectory directory = request "directory" 1 "next" directory + +ipv4 octets port = ["ipv4" octets port] +ipv6 words scope port = ["ipv6" words scope port] + +resolveTCP host family port limit = request "resolver" 1 "resolve" [host family port limit] + +listenTCP owner address backlog = request "tcp" 1 "listen" (B.pair owner (B.pair address backlog)) +connectTCP owner address = request "tcp" 1 "connect" (B.pair owner address) +acceptTCP listener owner = request "tcp" 1 "accept" (B.pair listener owner) + +localAddress socket = request "tcp" 1 "local-address" socket +peerAddress socket = request "tcp" 1 "peer-address" socket +shutdownWrite socket = request "tcp" 1 "shutdown-write" socket + +-- Read exactly one authenticated frame from the current cursor. +-- Temporary disk <= size; the caller supplies a per-operation limit. +readIndexedTree reader directory limit size digest = request "indexed-tree" 1 "read" + [reader directory limit size digest] + +-- Canonical native encoding avoids expanded tree/byte lists. The writer checks +-- the size and expected digest before sending. Retry decisions require confirmed +-- progress; a failed write can have partial or uncertain effects. +hashIndexedTree limit tree = request "indexed-tree" 1 "hash" (B.pair limit tree) + +writeIndexedTree writer limit digest tree = request "indexed-tree" 1 "write" + [writer limit digest tree] + +-- Exact canonical-byte comparison consumes precisely one frame. +verifyIndexedTree reader limit tree = request "indexed-tree" 1 "verify" [reader limit tree] + +-- Stateless bounded codecs: decode returns [text carry], with carry <=3 octets. +-- Set final=1 at EOF to reject incomplete suffixes. NUL and BOM are valid scalars; +-- source-file BOM rejection belongs to library/application policy. +encodeUTF8 text = request "utf8" 1 "encode" text +decodeUTF8 final bytes = request "utf8" 1 "decode" [final bytes] + +readSome reader limit = request "stream" 1 "read" (B.pair reader limit) +writeSome writer bytes = request "stream" 1 "write" (B.pair writer bytes) +close handle = request "resource" 1 "close" handle + +openStage owner directory = request "stage" 1 "open" (B.pair owner directory) +sealStage stage = request "stage" 1 "seal" stage +publishNew stage destination = request "stage" 1 "publish" (B.pair stage (B.pair 0 destination)) +publishReplace stage destination = request "stage" 1 "publish" (B.pair stage (B.pair 1 destination)) + +openDigest owner algorithm = request "digest" 1 "open" (B.pair owner algorithm) +updateDigest digest bytes = request "digest" 1 "update" (B.pair digest bytes) +finishDigest digest = request "digest" 1 "finish" digest + +onOk action next = bind action (reply : case reply of + ((t t), (value, t)) = next value + (t, (_, t)) = pure reply + _ = fail "Invalid operation Result") + +-- Nonrecursive initialization for caller-ordered store directory creation. +-- stat follows an existing symlink and accepts it when its target is a directory. +ensureDirectory path = bind (createDirectory path) (reply : case reply of + (t, (["already-exists" _ _ _], t)) = onOk (statPath path) (status : case status of + ["directory"] = pure (B.ok t t) + _ = pure (B.err ["not-directory" "directory/ensure" "" "none"] t)) + _ = pure reply) + +-- Explicit sequential fallback policy, separate from native connect/resolve. +-- Stop on the first success; preserve the last ordinary failure unchanged. +connectFirstTCP owner addresses = connectCandidates owner addresses + (B.err ["not-found" "tcp/connect-first" "No endpoint candidates" "none"] t) +connectCandidates _ [] previous = pure previous +connectCandidates owner [address | rest] _ = bind (connectTCP owner address) (reply : case reply of + ((t t), (_, t)) = pure reply + (t, (_, t)) = connectCandidates owner rest reply + _ = fail "Invalid connection Result") +connectCandidates _ _ _ = pure (B.err ["invalid-argument" "tcp/connect-first" "Invalid endpoint list" "none"] t) + +-- Turn an explicitly selected operation failure into terminal task failure. +-- Ordinary Results elsewhere remain values. +require action next = bind action (reply : case reply of + ((t t), (value, t)) = next value + (t, (problem, t)) = fail problem + _ = fail "Invalid operation Result") + +-- Whole-chunk convenience over the native positive-progress contract. Failure +-- stops immediately, preserving partial or uncertain progress for the caller. +dropWritten 0 bytes = bytes +dropWritten count [_ | rest] = dropWritten (N.predecessor count) rest +dropWritten _ _ = [] +-- Failure carries confirmed progress BEFORE the failing writeSome, plus its +-- unchanged diagnostic (whose own progress can be unknown). Composite progress +-- includes earlier successful writes even when the last operation reports "none". +writeAll writer bytes = let rec + walk accepted remaining = case remaining of + [] = pure (B.ok t t) + _ = bind (writeSome writer remaining) (reply : case reply of + ((t t), (count, t)) = walk (N.add accepted count) (dropWritten count remaining) + (t, (problem, t)) = pure (B.err ["write-all" accepted problem] t) + _ = fail "Invalid operation Result") + in walk 0 bytes + +-- Sequential bounded pumping, without acquiring or closing either endpoint. +-- Errors retain writeAll's confirmed prefix and original uncertain progress. +copyStream reader writer = do onOk + received <- readSome reader 4096 + case received of + ("eof", t) = pure (B.ok t t) + ("data", bytes) = do onOk + writeAll writer bytes + copyStream reader writer + _ = fail "Invalid read reply" + +-- Relative waits and native monotonic observations. Scheduler latency can extend +-- waits; yield offers a scheduling opportunity with scheduler-selected ordering. +monotonicNanoseconds = request "clock" 1 "monotonic" t +sleepMilliseconds milliseconds = request "clock" 1 "sleep" milliseconds +yield = request "scheduler" 1 "yield" t + +-- Returns Result [task joinTicket stdinWriter stdoutReader stderrReader]. +-- cwd: ["inherit"] or ["at" path]; env: ["inherit"] or ["replace" [[key value] ...]]. +-- Normal scope exit waits. Close stdin for EOF; drain stdout/stderr concurrently. +-- Join completion holds ["exit" code] or ["signal" number]. +spawnProcess executable arguments directory environment graceMilliseconds = + request "process" 1 "spawn" [executable arguments directory environment graceMilliseconds] + +-- Read-only host context. Environment query returns ["missing"] or ["value" text]; empty and absent remain distinct. +workingDirectory = request "process" 1 "working-directory" t +environmentVariable name = request "process" 1 "environment-variable" name diff --git a/test/fixtures/reference-source/9acae0f91625a1f6e13601276f2e11b319d346d872e85d758377fd649da5d5d8.tri b/test/fixtures/reference-source/9acae0f91625a1f6e13601276f2e11b319d346d872e85d758377fd649da5d5d8.tri @@ -0,0 +1,147 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export readSource +!export readText +!export encodeText +!export writeText +!export lineCursor +!export readLine + +problem operation detail progress = ["invalid-data" operation detail progress] +-- base.foldr supplies accumulator first, unlike the conventional Haskell API. +flatten chunks = B.foldr (suffix chunk : B.append chunk suffix) [] (B.reverse chunks) + +checkedLimit limit next = case B.checkContract B.nat? limit of + ((t t), (count, t)) = case N.lte count 9223372036854775807 of + (t t) = next count + _ = IO.pure (B.err ["invalid-bound" "text/limit" "invalid text byte limit" "none"] t) + _ = IO.pure (B.err ["invalid-bound" "text/limit" "invalid text byte limit" "none"] t) + +-- A bounded split, not a scalar validator: native encode validates the complete +-- prefix before it can produce bytes. [] signals an improper character spine. +split 0 rest = [[] rest] +split _ [] = [[] []] +split count [c | rest] = case split (N.predecessor count) rest of + [prefix after] = [(B.pair c prefix) after] + _ = [] +split _ _ = [] + +encodeLoop remaining text chunks = case split 1024 text of + [[] []] = IO.pure (B.ok (flatten chunks) t) + [part rest] = do IO.onOk + bytes <- IO.encodeUTF8 part + case N.lte (B.length bytes) remaining of + (t t) = encodeLoop (N.subtract remaining (B.length bytes)) rest (B.pair bytes chunks) + _ = IO.pure (B.err ["invalid-bound" "text/encode" "encoded text exceeds byte limit" "none"] t) + _ = IO.pure (B.err (problem "text/encode" "improper scalar list" "none") t) +encodeText limit text = checkedLimit limit (remaining : encodeLoop remaining text []) + +-- Force the small acknowledged-byte counter before the next request, without +-- retaining arithmetic that depends on previously emitted chunks. +forceCount t next = next t +forceCount (t bit rest) next = case bit of + t = forceCount rest next + (t t) = forceCount rest next + _ = IO.fail "Invalid text counter" +forceCount _ _ = IO.fail "Invalid text counter" +writeFailure confirmed cause = IO.pure (B.err ["write-text" confirmed cause] t) +writeLoop writer text confirmed = forceCount confirmed (_ : case split 1024 text of + [[] []] = IO.pure (B.ok t t) + [part rest] = IO.bind (IO.encodeUTF8 part) (encoded : case encoded of + ((t t), (bytes, t)) = IO.bind (IO.writeAll writer bytes) (written : case written of + ((t t), (t, t)) = writeLoop writer rest (N.add confirmed (B.length bytes)) + (t, (cause, t)) = writeFailure confirmed cause + _ = IO.fail "Invalid text write reply") + (t, (cause, t)) = writeFailure confirmed cause + _ = IO.fail "Invalid UTF-8 encode reply") + _ = writeFailure confirmed (problem "text/write" "improper scalar list" "none")) +writeText writer text = writeLoop writer text 0 + +-- Whole-source convenience, not a constant-memory text stream. Retain the exact +-- received octets and decoded scalars without normalization or BOM stripping. +-- A one-byte EOF probe detects excess input at the budget boundary. Callers must +-- not share this cursor concurrently; the individual reads are leased, not the +-- entire source convenience operation. Composite failure retains confirmed input +-- bytes (before any uncertain failing read) plus the unchanged native problem. +readFailure consumed cause = IO.pure (B.err ["read-source" consumed cause] t) +readCount 0 = 1 +readCount remaining = case N.lte remaining 4093 of + (t t) = remaining + _ = 4093 +readLoop reader limit remaining carry first rawChunks textChunks = + IO.bind (IO.readSome reader (readCount remaining)) (received : case received of + ((t t), (("eof", t), t)) = IO.bind (IO.decodeUTF8 1 carry) (decoded : case decoded of + ((t t), ([[] []], t)) = IO.pure (B.ok (B.pair (flatten rawChunks) (flatten textChunks)) t) + (t, (cause, t)) = readFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid UTF-8 final reply") + ((t t), (("data", bytes), t)) = let count = B.length bytes in + case N.lte count remaining of + (t t) = IO.bind (IO.decodeUTF8 0 (B.append carry bytes)) (decoded : case decoded of + ((t t), ([text pending], t)) = case first of + 1 = case text of + [] = readLoop reader limit (N.subtract remaining count) pending 1 (B.pair bytes rawChunks) textChunks + [65279 | _] = readFailure (N.add (N.subtract limit remaining) count) + (problem "text/read-source" "initial source BOM is not permitted" "none") + _ = readLoop reader limit (N.subtract remaining count) pending 0 (B.pair bytes rawChunks) (B.pair text textChunks) + _ = readLoop reader limit (N.subtract remaining count) pending 0 (B.pair bytes rawChunks) (B.pair text textChunks) + (t, (cause, t)) = readFailure (N.add (N.subtract limit remaining) count) cause + _ = IO.fail "Invalid UTF-8 decode reply") + _ = readFailure (N.add (N.subtract limit remaining) count) + ["invalid-bound" "text/read-source" "source exceeds byte limit" "none"] + (t, (cause, t)) = readFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid source read reply") +readSource reader limit = checkedLimit limit (count : readLoop reader count count [] 1 [] []) + +-- Single-consumer source cursor. Thread ONLY the returned cursor forward; do not +-- interleave raw reads, duplicate/reuse a cursor, or resume it after read failure. +-- Read-ahead is bounded to 4093 bytes. This is not a native resource or a lock. +lineCursor reader = [reader [] 0] + +-- LF is framing, not part of the line budget. CR, BOM and NUL remain content. +-- Scan before decoding: malformed bytes after LF belong to the NEXT line. +lineSplit [] = [[] [] 0] +lineSplit [10 | rest] = [[] rest 1] +lineSplit [byte | rest] = case lineSplit rest of + [prefix after ended] = [(B.pair byte prefix) after ended] + _ = [] +lineSplit _ = [] +lineFailure consumed cause = IO.pure (B.err ["read-line" consumed cause] t) +lineReady reader after ending chunks = IO.pure + (B.ok [["line" (flatten chunks) ending] [reader after 0]] t) +lineEOF reader consumed carry chunks = IO.bind (IO.decodeUTF8 1 carry) (decoded : case decoded of + ((t t), ([[] []], t)) = case consumed of + 0 = IO.pure (B.ok [["eof"] [reader [] 1]] t) + _ = IO.pure (B.ok [["line" (flatten chunks) "eof"] [reader [] 1]] t) + (t, (cause, t)) = lineFailure consumed cause + _ = IO.fail "Invalid line final UTF-8 reply") +lineLoop reader limit remaining carry chunks buffered = forceCount remaining (_ : case buffered of + [] = IO.bind (IO.readSome reader 4093) (received : case received of + ((t t), (("eof", t), t)) = lineEOF reader (N.subtract limit remaining) carry chunks + ((t t), (("data", bytes), t)) = lineLoop reader limit remaining carry chunks bytes + (t, (cause, t)) = lineFailure (N.subtract limit remaining) cause + _ = IO.fail "Invalid line read reply") + _ = case lineSplit buffered of + [part after ended] = let count = B.length part in + case N.lte count remaining of + (t t) = IO.bind (IO.decodeUTF8 ended (B.append carry part)) (decoded : case decoded of + ((t t), ([text pending], t)) = case ended of + 1 = lineReady reader after "lf" (B.pair text chunks) + _ = lineLoop reader limit (N.subtract remaining count) pending (B.pair text chunks) [] + (t, (cause, t)) = lineFailure (N.add (N.subtract limit remaining) count) cause + _ = IO.fail "Invalid line UTF-8 reply") + _ = lineFailure (N.add (N.subtract limit remaining) count) + ["invalid-bound" "text/read-line" "line exceeds byte limit" "none"] + _ = IO.fail "Invalid line cursor bytes") +readLine cursor limit = checkedLimit limit (count : case cursor of + [reader buffered 0] = lineLoop reader count count [] [] buffered + [reader [] 1] = IO.pure (B.ok [["eof"] cursor] t) + _ = IO.pure (B.err ["invalid-argument" "text/read-line" "invalid line cursor" "none"] t)) + +-- Generic bounded whole-text convenience. Unlike readSource, initial BOM is +-- ordinary content. It shares the incremental decoder, not the source policy. +readText reader limit = checkedLimit limit (count : IO.onOk + (readLoop reader count count [] 0 [] []) + (received : case received of + (_, text) = IO.pure (B.ok text t) + _ = IO.fail "Invalid text read result")) diff --git a/test/fixtures/reference-source/b1eccce1b5e4702466df56e1b48755797d6d148dcc38656f821faaa4d90e34da.tri b/test/fixtures/reference-source/b1eccce1b5e4702466df56e1b48755797d6d148dcc38656f821faaa4d90e34da.tri @@ -0,0 +1,277 @@ +!import prelude * +!import binary * +!import arboricx_common * +!export * + +readManifestMagic bytes = (matchResult + (code rest : err (lazyBool (_ : errInvalidManifestMagic) (_ : code) (equal? code errUnexpectedBytes)) rest) + ok + (expectBytes arboricxManifestMagic bytes)) + +-- Read a u32 BE length, then that many raw bytes. +-- Returns the payload bytes and remaining input. +readLengthPrefixedString = (do bindParser + lengthBytes <- readBytes 4 + payload <- readBytes (u32BEBytesToNat lengthBytes) + pureParser payload) + +readUtf8String bytes = (bindResult (readLengthPrefixedString bytes) + (text rest : lazyBool (_ : ok text rest) (_ : err errManifestValidationFailed rest) (binaryUtf8? text))) + +-- Helper: read a single capability string (length-prefixed string) +readCapability = (bs : + readUtf8String bs) + +-- Counted capability strings. +readCapabilities count = repeatParser count readCapability + +-- Helper: read a single root entry (4-byte u32 BE index + length-prefixed role) +readRootEntry = (do bindParser + indexRaw <- readBytes 4 + role <- readUtf8String + pureParser (pair indexRaw role)) + +-- Counted root entries. +readRoots count = repeatParser count readRootEntry + +-- Helper: read a single export entry +readExportEntry = (do bindParser + name <- readUtf8String + rootIndexRaw <- readBytes 4 + kind <- readUtf8String + abi <- readUtf8String + pureParser (pair name (pair rootIndexRaw (pair kind abi)))) + +-- Counted export entries. +readExports count = repeatParser count readExportEntry + +validateManifestVersion major minor bytes = (lazyBool + (_ : ok unit bytes) + (_ : err errUnsupportedManifestVersion bytes) + (and? (equal? major arboricxManifestMajorVersion) (equal? minor arboricxManifestMinorVersion))) + +-- Main core manifest parser. +-- Reads: magic, version, core strings, capabilities, closure, roots, exports. +readManifestCore = (do bindParser + readManifestMagic + major <- readBytes 2 + minor <- readBytes 2 + validateManifestVersion major minor + schema <- readUtf8String + bundleType <- readUtf8String + treeCalculus <- readUtf8String + treeHashAlgorithm <- readUtf8String + treeHashDomain <- readUtf8String + treeNodePayload <- readUtf8String + runtimeSemantics <- readUtf8String + runtimeEvaluation <- readUtf8String + runtimeAbi <- readUtf8String + capCountRaw <- readBytes 4 + capabilities <- readCapabilities (u32BEBytesToNat capCountRaw) + closureByte <- readBytes 1 + rootCountRaw <- readBytes 4 + roots <- readRoots (u32BEBytesToNat rootCountRaw) + exportCountRaw <- readBytes 4 + exports <- readExports (u32BEBytesToNat exportCountRaw) + pureParser (pair schema (pair bundleType (pair treeCalculus (pair treeHashAlgorithm (pair treeHashDomain (pair treeNodePayload (pair runtimeSemantics (pair runtimeEvaluation (pair runtimeAbi (pair capabilities (pair closureByte (pair roots exports))))))))))))) + +-- Metadata tag constants (u16 values) +tagPackage = [(0) (1)] +tagVersion = [(0) (2)] +tagDescription = [(0) (3)] +tagLicense = [(0) (4)] +tagCreatedBy = [(0) (5)] + +-- Read a single TLV entry: u16 tag + u32 length + value bytes. +-- Returns the pair (tag, value) and remaining input. +readTLV = (do bindParser + tag <- readBytes 2 + tlvLenRaw <- readBytes 4 + tlvValue <- readBytes (u32BEBytesToNat tlvLenRaw) + pureParser (pair tag tlvValue)) + +-- Counted TLV entries. +readTLVList count = repeatParser count readTLV + +-- Prefix parser: retain both opaque TLV lists and the actual remaining suffix. +readManifest = (do bindParser + core <- readManifestCore + metaCount <- readBytes 4 + metadata <- readTLVList (u32BEBytesToNat metaCount) + extCount <- readBytes 4 + extensions <- readTLVList (u32BEBytesToNat extCount) + pureParser (pair core (pair metadata extensions))) + +readManifestComplete bytes = (bindResult (readManifest bytes) + (value rest : case rest of + [] = ok value rest + _ = err errTrailingManifestBytes rest)) + +-- Lookup a metadata value by tag from a TLV list. +-- Returns nothing if not found, just value if found. +lookupMetadata tlvs tag = (maybeMap snd (find (entry : equal? (fst entry) tag) tlvs)) +exportName entry = fst entry +exportRoot entry = fst (snd entry) +exportNameEq? name entry = equal? name (exportName entry) +findExportByName exports name = find (exportNameEq? name) exports +getExportNames exports = map exportName exports + +mainExportName = "main" + +maybeExportToResult = (maybeExport : + triage + (err errMissingSection t) + (export : ok export t) + (_ _ : err errMissingSection t) + maybeExport) + +selectSingleExport = (exports : + matchList + (err errMissingSection t) + (export rest : + matchBool + (ok export t) + (err errMissingSection t) + (emptyList? rest)) + exports) + +selectDefaultExport = (exports : + triage + (selectSingleExport exports) + (export : ok export t) + (_ _ : err errMissingSection t) + (findExportByName exports mainExportName)) + +-- Select an export: explicit name if provided, otherwise "main", otherwise +-- the sole export if the bundle has exactly one export. +selectExport = (exports nameBytes : + matchBool + (selectDefaultExport exports) + (maybeExportToResult (findExportByName exports nameBytes)) + (emptyList? nameBytes)) + +-- Expected core string values (raw UTF-8 bytes, not decoded to Unicode characters). +expectedSchema = "arboricx.bundle.manifest.v1" +expectedBundleType = "tree-calculus-executable-object" +expectedTreeCalculus = "tree-calculus.v1" +expectedTreeHashAlgorithm = "indexed" +expectedTreeHashDomain = "arboricx.indexed.node.v1" +expectedTreeNodePayload = "arboricx.indexed.payload.v1" +expectedRuntimeSemantics = "tree-calculus.v1" +expectedRuntimeEvaluation = "normal-order" +expectedRuntimeAbi = "arboricx.abi.tree.v1" + +-- Manifest core field accessors. +-- readManifestCore returns: (pair schema (pair bundleType (... (pair closureByte (pair roots exports))))) +manifestSchema record = case record of + (value, _) = value + _ = t + +manifestBundleType record = case record of + (_, (value, _)) = value + _ = t + +manifestTreeCalculus record = case record of + (_, (_, (value, _))) = value + _ = t + +manifestTreeHashAlgorithm record = case record of + (_, (_, (_, (value, _)))) = value + _ = t + +manifestTreeHashDomain record = case record of + (_, (_, (_, (_, (value, _))))) = value + _ = t + +manifestTreeNodePayload record = case record of + (_, (_, (_, (_, (_, (value, _)))))) = value + _ = t + +manifestRuntimeSemantics record = case record of + (_, (_, (_, (_, (_, (_, (value, _))))))) = value + _ = t + +manifestRuntimeEvaluation record = case record of + (_, (_, (_, (_, (_, (_, (_, (value, _)))))))) = value + _ = t + +manifestRuntimeAbi record = case record of + (_, (_, (_, (_, (_, (_, (_, (_, (value, _))))))))) = value + _ = t + +manifestCapabilities record = case record of + (_, (_, (_, (_, (_, (_, (_, (_, (_, (value, _)))))))))) = value + _ = t + +manifestClosureByte record = case record of + (_, (_, (_, (_, (_, (_, (_, (_, (_, (_, (value, _))))))))))) = value + _ = t + +manifestRoots record = case record of + (_, (_, (_, (_, (_, (_, (_, (_, (_, (_, (_, (value, _)))))))))))) = value + _ = t + +manifestExports record = case record of + (_, (_, (_, (_, (_, (_, (_, (_, (_, (_, (_, (_, value)))))))))))) = value + _ = t + +-- Helper: compare a manifest field against an expected byte string. +manifestFieldMatch? = (actual expected : bytesEq? actual expected) + +-- Validate core manifest fields against expected values. +validateManifestCore = (core rest : + matchBool + (ok core rest) + (err errManifestValidationFailed rest) + (and? + (manifestFieldMatch? (manifestSchema core) expectedSchema) + (and? + (manifestFieldMatch? (manifestBundleType core) expectedBundleType) + (and? + (manifestFieldMatch? (manifestTreeCalculus core) expectedTreeCalculus) + (and? + (manifestFieldMatch? (manifestTreeHashAlgorithm core) expectedTreeHashAlgorithm) + (and? + (manifestFieldMatch? (manifestTreeHashDomain core) expectedTreeHashDomain) + (and? + (manifestFieldMatch? (manifestTreeNodePayload core) expectedTreeNodePayload) + (and? + (manifestFieldMatch? (manifestRuntimeSemantics core) expectedRuntimeSemantics) + (and? + (manifestFieldMatch? (manifestRuntimeEvaluation core) expectedRuntimeEvaluation) + (and? + (manifestFieldMatch? (manifestRuntimeAbi core) expectedRuntimeAbi) + (and? + (bytesEq? (manifestClosureByte core) [(0)]) + (and? + (not? (emptyList? (manifestRoots core))) + (and? (not? (emptyList? (manifestExports core))) + (and? (equal? (manifestCapabilities core) []) + (and? (manifestRootsValid? (manifestRoots core)) (manifestExportsValid? (manifestExports core))))))))))))))))) + + +-- Export names are UTF-8 byte strings interpreted as symbols. +-- Validate every export before selection and preserve its declared kind and ABI. +manifestExportsValid? exports = case exports of + [] = true + [(name, (root, (kind, abi))) | rest] = (and? (not? (equal? name [])) + (and? (binaryBytes? name) + (and? (and? (binaryBytes? root) (equal? (length root) 4)) + (and? (equal? kind "term") + (and? (equal? abi expectedRuntimeAbi) + (and? (not? (any? (exportNameEq? name) rest)) (manifestExportsValid? rest))))))) + _ = false + +manifestRootsValid? roots = case roots of + [] = true + [(index, role) | rest] = (and? (binaryBytes? index) + (and? (equal? (length index) 4) (and? (binaryBytes? role) (manifestRootsValid? rest)))) + _ = false + + +-- Unknown metadata and extensions remain opaque. Known metadata tags are text. +validateManifestMetadata fields rest = (lazyBool + (_ : ok fields rest) + (_ : err errManifestValidationFailed rest) + (all? (entry : let tag = beBytesToNat (fst entry) in + or? (not? (and? (gte_? tag 1) (lte_? tag 5))) (binaryUtf8? (snd entry))) fields)) diff --git a/test/fixtures/reference-source/b842a39f410e28c89c843ac8f1d3a8db7afa2e11a5ce12bf5c29193a6fb6b3c6.tri b/test/fixtures/reference-source/b842a39f410e28c89c843ac8f1d3a8db7afa2e11a5ce12bf5c29193a6fb6b3c6.tri @@ -0,0 +1,18 @@ +!import io_v2 as IO +!export * + +-- Scoped TCP facilities. Owner arguments and Result replies are explicit; +-- sockets are resources, not reusable numeric descriptors or detached workers. +ipv4 = IO.ipv4 +ipv6 = IO.ipv6 +resolveTCP = IO.resolveTCP +listenTCP = IO.listenTCP +connectTCP = IO.connectTCP +acceptTCP = IO.acceptTCP +localAddress = IO.localAddress +peerAddress = IO.peerAddress +shutdownWrite = IO.shutdownWrite +readSome = IO.readSome +writeSome = IO.writeSome +writeAll = IO.writeAll +close = IO.close diff --git a/test/fixtures/reference-source/b881a086294ff264567d758dd2c1ef7a3470faa909ec588e9f7bb8c4430f7bfc.tri b/test/fixtures/reference-source/b881a086294ff264567d758dd2c1ef7a3470faa909ec588e9f7bb8c4430f7bfc.tri @@ -0,0 +1,53 @@ +!import base as B +!import native_math as N +!import http as HTTP +!import io_v2 as IO +!export recvHeaders +!export readBody +!export respond +!export handle + +-- Reuse pure HTTP grammar/limits. The header cap covers the entire accumulated +-- receive buffer, including prefetched body bytes, exactly as in the v1 reader. +receiveHeaders client buffer count = IO.bind (IO.readSome client 4096) (reply : case reply of + ((t t), (("data", chunk), t)) = let + next = B.append buffer chunk + size = N.add count (HTTP.httpBodyLength chunk) + in case N.lte size HTTP.maxHeaderBytes of + (t t) = case B.contains? HTTP.crlfcrlf next of + (t t) = IO.pure (B.ok next t) + _ = receiveHeaders client next size + _ = IO.pure (B.err 431 t) + _ = IO.pure (B.err 400 t)) +recvHeaders client = receiveHeaders client [] 0 + +readState _ (0, (reversed, rest)) = IO.pure (B.ok (B.reverse reversed) rest) +readState client (remaining, (reversed, _)) = IO.bind + (IO.readSome client (HTTP.recvChunkMax4096 remaining)) (reply : case reply of + ((t t), (("data", chunk), t)) = readState client (HTTP.takeBodyBytes chunk remaining reversed) + _ = IO.pure (B.err 400 "Unexpected end of request body\n")) +readState _ _ = IO.pure HTTP.httpBadRequest +readBody client fields initial = case HTTP.contentLength fields of + ((t t), (t, _)) = IO.pure (B.ok [] initial) + ((t t), ((t count), _)) = readState client (HTTP.takeBodyBytes initial count []) + failure = IO.pure failure + +-- The connection's owning scope closes it even on partial write/failure. Do not +-- retry uncertain writes or let a cleanup failure turn into a second response. +send client bytes = case HTTP.takeBodyBytes bytes 4096 [] of + (_, ([], _)) = IO.pure t + (_, (reversed, rest)) = IO.require (IO.writeAll client (B.reverse reversed)) (_ : send client rest) + _ = IO.fail "Invalid response byte list" +respond client method response = send client (HTTP.responseForMethod method response) +body router client method target fields ((t t), (bytes, _)) = IO.bind + (router (HTTP.routerMethod method) target fields bytes) (respond client method) +body _ client method _ _ (t, (status, message)) = respond client method (HTTP.errorResponse status message) +body _ client method _ _ _ = respond client method (HTTP.badRequestResponse "Bad Request\n") +fields router client method target version raw = case HTTP.parseHeaders raw of + ((t t), (headers, initial)) = case HTTP.validateRequest method target version headers of + ((t t), _) = IO.bind (readBody client headers initial) (body router client method target headers) + (t, (status, message)) = respond client method (HTTP.errorResponse status message) + _ = respond client method (HTTP.badRequestResponse "Bad Request\n") + _ = respond client "" (HTTP.badRequestResponse "Bad Request\n") +handle router client ((t t), ((method, (target, version)), raw)) = fields router client method target version raw +handle _ client _ = respond client "" (HTTP.badRequestResponse "Bad Request\n") diff --git a/test/fixtures/reference-source/bc09110023edc9a6aa7d947e51a09385f600d4fef66e8fd765808ad14f7ecc34.tri b/test/fixtures/reference-source/bc09110023edc9a6aa7d947e51a09385f600d4fef66e8fd765808ad14f7ecc34.tri @@ -0,0 +1,17 @@ +!import base as B +!import conversions as C +!import io_v2 as IO +!import arboricx_streaming_v2 as Server +!import arboricx_handlers as Registry +!export main +!export server +-- Native streaming registry with selectable address and connection count. +-- Source-owned ./store, 1-MiB body/object budget and bounded FIFO16 admission. +port ["ipv4" _ value] = value +port _ = 0 +ready listener = do IO.require + address <- IO.localAddress listener + writer <- IO.stdout + IO.require (IO.writeAll writer (B.append (C.showNumber (port address)) [10])) IO.pure +server root address count = IO.io (Server.serveCount root 1048576 Registry.stdoutLogV2 address 16 count ready) +main = IO.io (Server.serve "./store" 1048576 Registry.stdoutLogV2 (IO.ipv4 [127 0 0 1] 9050) 16 ready) diff --git a/test/fixtures/reference-source/bea75689ba66f19e241f97369ea7ee1393fabbaa7c7eac9b9a2d0ca1e455a1bf.tri b/test/fixtures/reference-source/bea75689ba66f19e241f97369ea7ee1393fabbaa7c7eac9b9a2d0ca1e455a1bf.tri @@ -0,0 +1,158 @@ +!import base as B +!import native_math as N +!export * + +-- Experimental native structured IO with named, versioned facilities. +-- No runtime read/write grants: native execution uses OS process authority. +io action = B.pair "tricuIO" (B.pair 2 action) +pure value = B.pair 0 value +bind action next = B.pair 1 (B.pair action next) +fail diagnostic = B.pair 2 diagnostic +scope body = B.pair 3 body +currentScope = B.pair 4 t +-- Flags are canonical 0/1, not truthy values. Callback receives fresh handles. +-- Success is [task joinTicket]; non-result children receive t in the ticket slot. +spawn supervised keep handles body = B.pair 5 + (B.pair supervised (B.pair keep (B.pair handles body))) +join ticket = B.pair 6 ticket +discard ticket = B.pair 7 ticket +cancel task reason = B.pair 8 (B.pair task reason) +cancelled reason = B.pair 9 reason +-- Named, independently versioned host facilities; core sequencing stays separate. +request group revision operation arguments = B.pair 10 + (B.pair group (B.pair revision (B.pair operation arguments))) +capabilities = request "runtime" 1 "capabilities" t +inspect action = request "runtime" 1 "inspect" action +stdin = request "standard" 1 "input" t +stdout = request "standard" 1 "output" t +stderr = request "standard" 1 "error" t +openReader owner path = request "file" 1 "open-reader" (B.pair owner path) +openWriter owner path = request "file" 1 "open-writer" (B.pair owner path) +openAppender owner path = request "file" 1 "open-appender" (B.pair owner path) +openUpdate owner path = request "file" 1 "open-update" (B.pair owner path) +seekFile handle offset = request "file" 1 "seek" (B.pair handle offset) +filePosition handle = request "file" 1 "position" handle +syncFile handle = request "file" 1 "sync" handle +syncDirectory path = request "directory" 1 "sync" path +fileSize handle = request "file" 1 "size" handle +resizeFile handle size = request "file" 1 "resize" (B.pair handle size) +statPath path = request "path" 1 "stat" path +lstatPath path = request "path" 1 "lstat" path +removeFile path = request "path" 1 "remove-file" path +renamePath source destination = request "path" 1 "rename" (B.pair source destination) +createDirectory path = request "directory" 1 "create" path +removeDirectory path = request "directory" 1 "remove" path +openDirectory owner path = request "directory" 1 "open" (B.pair owner path) +nextDirectory directory = request "directory" 1 "next" directory +ipv4 octets port = ["ipv4" octets port] +ipv6 words scope port = ["ipv6" words scope port] +resolveTCP host family port limit = request "resolver" 1 "resolve" [host family port limit] +listenTCP owner address backlog = request "tcp" 1 "listen" (B.pair owner (B.pair address backlog)) +connectTCP owner address = request "tcp" 1 "connect" (B.pair owner address) +acceptTCP listener owner = request "tcp" 1 "accept" (B.pair listener owner) +localAddress socket = request "tcp" 1 "local-address" socket +peerAddress socket = request "tcp" 1 "peer-address" socket +shutdownWrite socket = request "tcp" 1 "shutdown-write" socket +-- One authenticated frame from the current cursor; no following-byte EOF probe. +-- Temporary disk <= size; limit is caller policy, not an aggregate scope quota. +readIndexedTree reader directory limit size digest = request "indexed-tree" 1 "read" + [reader directory limit size digest] +-- Canonical native encoding avoids expanded tree/byte lists. The writer checks +-- the size and expected digest before sending; failure is not retry permission. +hashIndexedTree limit tree = request "indexed-tree" 1 "hash" (B.pair limit tree) +writeIndexedTree writer limit digest tree = request "indexed-tree" 1 "write" + [writer limit digest tree] +-- Exact canonical-byte comparison consumes one frame; it does not probe EOF. +verifyIndexedTree reader limit tree = request "indexed-tree" 1 "verify" [reader limit tree] +-- Stateless bounded codecs: decode returns [text carry], with carry <=3 octets. +-- Set final=1 at EOF to reject incomplete suffixes. NUL and BOM are valid scalars; +-- source-file BOM rejection is library/application policy, not UTF-8 decoding. +encodeUTF8 text = request "utf8" 1 "encode" text +decodeUTF8 final bytes = request "utf8" 1 "decode" [final bytes] +readSome reader limit = request "stream" 1 "read" (B.pair reader limit) +writeSome writer bytes = request "stream" 1 "write" (B.pair writer bytes) +close handle = request "resource" 1 "close" handle +openStage owner directory = request "stage" 1 "open" (B.pair owner directory) +sealStage stage = request "stage" 1 "seal" stage +publishNew stage destination = request "stage" 1 "publish" (B.pair stage (B.pair 0 destination)) +publishReplace stage destination = request "stage" 1 "publish" (B.pair stage (B.pair 1 destination)) +openDigest owner algorithm = request "digest" 1 "open" (B.pair owner algorithm) +updateDigest digest bytes = request "digest" 1 "update" (B.pair digest bytes) +finishDigest digest = request "digest" 1 "finish" digest + +onOk action next = bind action (reply : case reply of + ((t t), (value, t)) = next value + (t, (_, t)) = pure reply + _ = fail "Invalid operation Result") + +-- Nonrecursive initialization for caller-ordered store directory creation. +-- An existing symlink to a directory is accepted through stat, not confinement. +ensureDirectory path = bind (createDirectory path) (reply : case reply of + (t, (["already-exists" _ _ _], t)) = onOk (statPath path) (status : case status of + ["directory"] = pure (B.ok t t) + _ = pure (B.err ["not-directory" "directory/ensure" "" "none"] t)) + _ = pure reply) + +-- Explicit sequential fallback policy, separate from native connect/resolve. +-- Stop on the first success; preserve the last ordinary failure unchanged. +connectFirstTCP owner addresses = connectCandidates owner addresses + (B.err ["not-found" "tcp/connect-first" "No endpoint candidates" "none"] t) +connectCandidates _ [] previous = pure previous +connectCandidates owner [address | rest] _ = bind (connectTCP owner address) (reply : case reply of + ((t t), (_, t)) = pure reply + (t, (_, t)) = connectCandidates owner rest reply + _ = fail "Invalid connection Result") +connectCandidates _ _ _ = pure (B.err ["invalid-argument" "tcp/connect-first" "Invalid endpoint list" "none"] t) + +-- Turn an explicitly selected operation failure into terminal task failure. +-- Ordinary Results elsewhere remain values, not implicit exceptions. +require action next = bind action (reply : case reply of + ((t t), (value, t)) = next value + (t, (problem, t)) = fail problem + _ = fail "Invalid operation Result") + +-- Whole-chunk convenience over the native positive-progress contract. Failure +-- stops immediately: unknown progress must never trigger a whole-chunk retry. +dropWritten 0 bytes = bytes +dropWritten count [_ | rest] = dropWritten (N.predecessor count) rest +dropWritten _ _ = [] +-- Failure carries confirmed progress BEFORE the failing writeSome, plus its +-- unchanged diagnostic (whose own progress can be unknown). Never report that +-- last operation's "none" as if the whole composite write had no effects. +writeAll writer bytes = let rec + walk accepted remaining = case remaining of + [] = pure (B.ok t t) + _ = bind (writeSome writer remaining) (reply : case reply of + ((t t), (count, t)) = walk (N.add accepted count) (dropWritten count remaining) + (t, (problem, t)) = pure (B.err ["write-all" accepted problem] t) + _ = fail "Invalid operation Result") + in walk 0 bytes + +-- Sequential bounded pumping, without acquiring or closing either endpoint. +-- Errors retain writeAll's confirmed prefix and original uncertain progress. +copyStream reader writer = do onOk + received <- readSome reader 4096 + case received of + ("eof", t) = pure (B.ok t t) + ("data", bytes) = do onOk + writeAll writer bytes + copyStream reader writer + _ = fail "Invalid read reply" + +-- Relative waits and native monotonic observations, not wall-clock timestamps or +-- hard deadlines. Yield only offers a scheduling opportunity, not alternation. +monotonicNanoseconds = request "clock" 1 "monotonic" t +sleepMilliseconds milliseconds = request "clock" 1 "sleep" milliseconds +yield = request "scheduler" 1 "yield" t + +-- Returns Result [task joinTicket stdinWriter stdoutReader stderrReader]. +-- cwd: ["inherit"] or ["at" path]; env: ["inherit"] or ["replace" [[key value] ...]]. +-- Normal scope exit waits. Close stdin for EOF; drain stdout/stderr concurrently. +-- Join completion holds ["exit" code] or ["signal" number], not an IO failure. +spawnProcess executable arguments directory environment graceMilliseconds = + request "process" 1 "spawn" [executable arguments directory environment graceMilliseconds] + +-- Read-only host context, not a language environment/state slot. Environment +-- query returns ["missing"] or ["value" text]; empty and absent remain distinct. +workingDirectory = request "process" 1 "working-directory" t +environmentVariable name = request "process" 1 "environment-variable" name diff --git a/test/fixtures/reference-source/c60b2bdf222db8ed812c2af4da3609cceefc8689c8aeb80fa52795d69193d0f5.tri b/test/fixtures/reference-source/c60b2bdf222db8ed812c2af4da3609cceefc8689c8aeb80fa52795d69193d0f5.tri @@ -0,0 +1,147 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export readBytes +!export hashBytes +!export publishBytes +!export readTree +!export publishTree +!export parentPath + +-- Caller-selected limits and directories. Whole byte lists are resident source +-- values; only transport chunks are bounded. This is not path confinement. +problem op detail = ["invalid-data" op detail "unknown"] +checkedLimit limit next = case B.isNat? limit of + (t t) = case N.lte limit 9223372036854775807 of + (t t) = next limit + _ = IO.pure (B.err ["invalid-bound" "storage/limit" "invalid byte limit" "none"] t) + _ = IO.pure (B.err ["invalid-bound" "storage/limit" "invalid byte limit" "none"] t) +split 0 rest = [[] rest] +split _ [] = [[] []] +split count [c | rest] = case split (N.predecessor count) rest of + [prefix after] = [(B.pair c prefix) after] + _ = [] +split _ _ = [] +flatten chunks = B.foldr (suffix chunk : B.append chunk suffix) [] (B.reverse chunks) +readCount 0 = 1 +readCount remaining = case N.lte remaining 4096 of + (t t) = remaining + _ = 4096 +readLoop reader remaining chunks = do IO.onOk + received <- IO.readSome reader (readCount remaining) + case received of + ("eof", t) = IO.pure (B.ok (flatten chunks) t) + ("data", bytes) = case N.lte (B.length bytes) remaining of + (t t) = readLoop reader (N.subtract remaining (B.length bytes)) (B.pair bytes chunks) + _ = IO.pure (B.err ["invalid-bound" "storage/read" "file exceeds byte limit" "unknown"] t) + _ = IO.fail "Invalid storage read reply" +-- May consume one excess byte in the EOF probe at the budget boundary. +readBytes path limit = checkedLimit limit (count : IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + readLoop reader count [])) + +hashLoop digest remaining bytes = case split 4096 bytes of + [[] []] = IO.finishDigest digest + [part rest] = case N.lte (B.length part) remaining of + (t t) = do IO.onOk + IO.updateDigest digest part + hashLoop digest (N.subtract remaining (B.length part)) rest + _ = IO.pure (B.err ["invalid-bound" "storage/hash" "bytes exceed limit" "none"] t) + _ = IO.pure (B.err ["invalid-argument" "storage/hash" "improper byte list" "none"] t) +hashBytes limit bytes = checkedLimit limit (count : IO.scope (owner : do IO.onOk + digest <- IO.openDigest owner "sha256" + hashLoop digest count bytes)) +writeLoop writer bytes = case split 4096 bytes of + [[] []] = IO.pure (B.ok t t) + [part rest] = do IO.onOk + IO.writeAll writer part + writeLoop writer rest + _ = IO.fail "Invalid preflighted byte list" + +verifyBytes reader remaining = do IO.onOk + received <- IO.readSome reader 4096 + case received of + ("eof", t) = case remaining of + [] = IO.pure (B.ok t t) + _ = IO.pure (B.err (problem "storage/verify" "different bytes") t) + ("data", bytes) = case split (B.length bytes) remaining of + [expected rest] = case B.equal? bytes expected of + (t t) = verifyBytes reader rest + _ = IO.pure (B.err (problem "storage/verify" "different bytes") t) + _ = IO.fail "Invalid preflighted byte list" + _ = IO.fail "Invalid verification read reply" +verified owner directory destination verify created = do IO.onOk + reader <- IO.openReader owner destination + verify reader + IO.syncFile reader + IO.syncDirectory directory + IO.pure (B.ok created t) +-- Immutable names verify every winner, through EOF, before syncing its opened +-- inode and directory. Never replace or delete a mismatch. Mutable replacement +-- instead acknowledges stage publication: do not race-read another alias writer +-- and mistakenly treat its legitimate replacement as corruption. +publishBytes directory destination replace limit bytes = case replace of + 0 = do IO.onOk + hashBytes limit bytes + IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + writeLoop stage bytes + IO.sealStage stage + status <- IO.publishNew stage destination + case status of + "published" = verified owner directory destination (reader : verifyBytes reader bytes) (t t) + "exists" = verified owner directory destination (reader : verifyBytes reader bytes) t + _ = IO.fail "Invalid publication reply") + 1 = do IO.onOk + hashBytes limit bytes + IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + writeLoop stage bytes + IO.sealStage stage + IO.publishReplace stage destination + IO.pure (B.ok (t t) t)) + _ = IO.pure (B.err ["invalid-argument" "storage/publish" "invalid replacement flag" "none"] t) + +verifyTree reader limit tree = do IO.onOk + IO.verifyIndexedTree reader limit tree + end <- IO.readSome reader 1 + case end of + ("eof", t) = IO.pure (B.ok t t) + _ = IO.pure (B.err (problem "storage/verify" "trailing bytes") t) +publishTree directory destination limit expected tree = do IO.onOk + info <- IO.hashIndexedTree limit tree + case info of + [_ actual] = case B.equal? actual expected of + (t t) = IO.scope (owner : do IO.onOk + stage <- IO.openStage owner directory + IO.writeIndexedTree stage limit expected tree + IO.sealStage stage + status <- IO.publishNew stage destination + case status of + "published" = verified owner directory destination (reader : verifyTree reader limit tree) (t t) + "exists" = verified owner directory destination (reader : verifyTree reader limit tree) t + _ = IO.fail "Invalid publication reply") + _ = IO.pure (B.err ["integrity-failed" "storage/publish" "digest mismatch" "none"] t) + _ = IO.fail "Invalid indexed hash reply" +readTree directory path limit expected = IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + size <- IO.fileSize reader + IO.readIndexedTree reader directory limit size expected) + +-- POSIX lexical parent, retaining separators rather than resolving symlinks or +-- '..'. Callers still pass complete paths to the native path validators. +slashes [] = t t +slashes [47 | rest] = slashes rest +slashes _ = t +trimSeparators [47 | rest] = trimSeparators rest +trimSeparators rest = rest +parentReverse [] = "." +parentReverse [47 | rest] = case slashes rest of + (t t) = B.pair 47 (B.reverse rest) + _ = B.reverse rest +parentReverse [_ | rest] = parentReverse rest +parentReverse _ = "." +parentPath [] = "." +parentPath path = case trimSeparators (B.reverse path) of + [] = path + rest = parentReverse rest diff --git a/test/fixtures/reference-source/c7c5c3133cf91206d8bdb13fd6972aed4bca3fae8b16308bafc6e25236c2cd0c.tri b/test/fixtures/reference-source/c7c5c3133cf91206d8bdb13fd6972aed4bca3fae8b16308bafc6e25236c2cd0c.tri @@ -0,0 +1,4 @@ +!export * from io_v2 + +-- Canonical application IO is protocol 2. Scoped ownership and OS process +-- authority replace the retired grant, environment and mutable-state actions. diff --git a/test/fixtures/reference-source/d52075fd243ce733529740023181faa44c7c3022a9f28ee7ea2aaa078d95f7b2.tri b/test/fixtures/reference-source/d52075fd243ce733529740023181faa44c7c3022a9f28ee7ea2aaa078d95f7b2.tri @@ -0,0 +1,15 @@ +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export run +!export safeRead +-- Ordinary read failures are application values; cleanup faults still fail the +-- scope. Generic UTF-8 (including initial BOM), with an explicit 1-MiB budget. +safeRead path = IO.bind (IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + Text.readText reader 1048576)) (reply : case reply of + ((t t), (text, t)) = IO.pure text + (t, (_, t)) = IO.pure "ERROR: Unable to read file" + _ = IO.fail "Invalid file reply") +main = IO.io (safeRead "demos/interactionTrees/greet.tri") +run path = IO.io (safeRead path) diff --git a/test/fixtures/reference-source/dd2f44109a0d14aa50e6a670067832720ff882f1ce7d8fac094d82280cbfb005.tri b/test/fixtures/reference-source/dd2f44109a0d14aa50e6a670067832720ff882f1ce7d8fac094d82280cbfb005.tri @@ -0,0 +1,25 @@ +!import base as B +!import io_v2 as IO +!import io_v2_text as Text +!export main +!export asyncDemo + +-- Explicit IO v2 sequencing, scoped children and consumed join tickets. Sleep +-- permits other work, but promises neither exact wakeup time nor task ordering. +-- Only the parent writes stdout: concurrent writers need explicit coordination, +-- not an assumption that yield/sleep makes a multi-write message atomic. +line text = IO.require IO.stdout (writer : Text.writeText writer (B.append text "\n")) +waitFor [_ ticket] = IO.require (IO.join ticket) (outcome : case outcome of + ["completed" value] = IO.pure value + ["cancelled" reason _] = IO.cancelled reason + ["failed" problem cleanup] = IO.fail [problem cleanup] + _ = IO.fail "Invalid task outcome") +waitFor _ = IO.fail "Invalid spawn reply" +worker milliseconds message = IO.require (IO.sleepMilliseconds milliseconds) (_ : IO.pure message) +asyncDemo = IO.scope (_ : do IO.require + first <- IO.spawn 0 1 [] (_ : worker 2000 "2000ms done sleeping!") + second <- IO.spawn 0 1 [] (_ : worker 5000 "5000ms done sleeping!") + line "Parent first!" + IO.bind (waitFor first) (message : IO.require (line message) (_ : + IO.bind (waitFor second) (message : IO.require (line message) (_ : IO.pure "child2000 done"))))) +main = IO.io asyncDemo diff --git a/test/fixtures/reference-source/de6cdc548d44df7d835386a49116364e4ce8df8729801ca85dc975000e66af7f.tri b/test/fixtures/reference-source/de6cdc548d44df7d835386a49116364e4ce8df8729801ca85dc975000e66af7f.tri @@ -0,0 +1,108 @@ +!import base as B +!import http as HTTP +!import conversions as Convert +!import arboricx_server as Store +!import handlers as H +!import io_v2 as V2 +!import io_v2_storage as Storage +!import handler_log as Log +!import handler_artifacts as Artifacts +!export request +!export packageLookupV2 +!export stdoutLogV2 +!export writeLogV2 +!export routerV2 + +-- HTTP policy is separate from storage, logging, and the socket lifecycle. +-- Outcomes carry a status beside the complete response so logging does not +-- parse response bytes. Only completed application routes emit access events; +-- parser/protocol rejections remain owned by the scoped HTTP transport. +reply status bytes = H.pure [status bytes] +error status message = reply status (HTTP.errorResponse status message) + +load digest = do H.bind + result <- Artifacts.load digest + case result of + ((t t), (bytes, t)) = reply 200 (HTTP.response 200 Store.bundleContentType bytes) + (t, (["invalid-digest" message], t)) = error 400 message + _ = error 404 "Artifact unavailable\n" + +publish bytes = do H.bind + result <- Artifacts.publish bytes + case result of + ((t t), (digest, t)) = reply 201 (HTTP.createdResponse digest) + _ = error 400 "Upload failed\n" + +-- Package storage is an explicit captured service: its identity is stable for +-- this server. Contextual artifact overrides need no package-path authority. +-- The HTTP parser supplies canonical target bytes, as in the original server. + +package lookup target = do H.bind + result <- lookup target + case result of + ((t t), (bytes, t)) = reply 200 (HTTP.response 200 "application/vnd.arboricx.package-release" bytes) + (t, (["invalid-package" message], t)) = error 400 message + _ = error 404 "Package release not found" + +get lookup target = case Store.stripPrefix Store.packagePrefix target of + (t suffix) = package lookup suffix + _ = case Store.stripPrefix Store.bundleHashPrefix target of + (t digest) = load digest + _ = reply 404 HTTP.notFoundResponse + +route _ "GET" "/" _ _ = reply 200 (HTTP.htmlResponse Store.htmlLandingPage) +route _ "GET" "/_arboricx/health" _ _ = reply 200 (HTTP.okResponse "OK\n") +route lookup "GET" target _ _ = get lookup target +route _ "POST" "/_arboricx/bundle" _ body = publish body +route _ _ _ _ _ = reply 404 HTTP.notFoundResponse + +request lookup method target headers body = H.label "registry-request" <| do H.bind + outcome <- route lookup method target headers body + case outcome of + [status response] = do H.bind + logged <- Log.info status + case logged of + ((t t), (t, t)) = H.pure response + (t, (message, t)) = H.fail "application-failure" "logging" "emit" message + _ = H.fail "application-failure" "logging" "emit" "Invalid logger reply" + _ = H.fail "application-failure" "registry" "route" "Invalid route outcome" + +-- The captured package service remains independent of contextual artifact routes. +packageLookupV2 root limit target = case Store.packageTarget target of + (t (name, version)) = H.liftIO (Storage.readBytes + (Store.pathJoin (Store.pathJoin (Store.pathJoin root "packages") name) version) limit) + _ = H.pure (B.err ["invalid-package" "Invalid package name/version"] t) + +-- Status-only ASCII logging. Ordinary write failures remain Results; publication +-- precedes logging and remains visible if the logger fails. +writeLogV2 bytes = do V2.onOk + writer <- V2.stdout + V2.writeAll writer bytes + V2.pure (B.ok t t) +stdoutLogV2 ["info" fields status] = case B.checkContract B.nat? status of + ((t t), _) = H.liftIO (writeLogV2 + (B.append "registry status=" (B.append (Convert.showNumber status) "\n"))) + _ = H.pure (B.err "Invalid access-log status" t) +stdoutLogV2 _ = H.pure (B.err "Invalid access-log event" t) + +-- A supervised child contains request interpretation/logger failure without +-- weakening native fail-fast semantics or retaining completed task tickets. +-- Cancellation remains cancellation; a failure becomes a redacted HTTP response. +-- The listener owns sockets and framing. +routerV2 root limit sink method target headers body = V2.scope (_ : do V2.require + spawned <- V2.spawn 1 1 [] (_ : H.runIOV2 + (request (packageLookupV2 root limit) method target headers body + |> Log.withContext [["method" method] ["target" target]] + |> Artifacts.withFilesystemV2 root limit + |> Log.withSink sink)) + case spawned of + [task ticket] = do V2.require + outcome <- V2.join ticket + case outcome of + ["completed" response] = V2.pure response + ["failed" _ _] = do V2.bind + writeLogV2 "registry request failed\n" + V2.pure (HTTP.errorResponse 500 "Internal Server Error\n") + ["cancelled" reason _] = V2.cancelled reason + _ = V2.fail "Invalid registry task outcome" + _ = V2.fail "Invalid registry spawn reply") diff --git a/test/fixtures/reference-source/e014061b2107cb0d8d1d2366742c8f35640c9a7dd85b80fc78d4afc7813bf0cc.tri b/test/fixtures/reference-source/e014061b2107cb0d8d1d2366742c8f35640c9a7dd85b80fc78d4afc7813bf0cc.tri @@ -0,0 +1,20 @@ +!import base as B +!import io_v2 as IO +!import io_v2_storage as Storage +!import io_v2_text as Text +!import arboricx as Bundle +!export main +!export run +!export runBundle +-- Whole-bundle source decoding is explicitly bounded, not a streaming decoder. +-- Selecting/executing this program is authority to execute the selected bundle; +-- bundle validation alone is not authentication or a computation-resource quota. +-- Container remainders are internal directory cursors, not unconsumed input; +-- bytes outside declared sections are permitted by the portable reader profile. +runBundle path argument = IO.bind (Storage.readBytes path 1048576) (read : case read of + ((t t), (bytes, t)) = case Bundle.runArboricx bytes argument of + ((t t), (value, _)) = IO.require IO.stdout (writer : IO.require (Text.writeText writer (B.append value "\n")) IO.pure) + _ = IO.fail "Could not validate/execute bundle" + _ = IO.fail "Could not read bundle file") +main = IO.io (runBundle "test/fixtures/id.arboricx" "hi") +run path argument = IO.io (runBundle path argument) diff --git a/test/fixtures/reference-source/e98fc7955feb673b86ac9176a211136493d6247b011cb80995fb53464028f976.tri b/test/fixtures/reference-source/e98fc7955feb673b86ac9176a211136493d6247b011cb80995fb53464028f976.tri @@ -0,0 +1,48 @@ +!import base * +!export * + +-- Entries are (name, dependency names). Names are opaque trees. Lists must be +-- proper, names unique, and every dependency present: frontend validation owns +-- these checks and their diagnostics before invoking this algorithm. +graphFind entries name = find (entry : equal? (fst entry) name) entries +graphContains names name = any? (equal? name) names +graphReady dependencies available = all? (graphContains available) dependencies +graphFirstReady entries available = find (entry : graphReady (snd entry) available) entries + +graphRemove entries name = (case entries of + [] = [] + [entry | rest] = (lazyBool + (_ : rest) + (_ : pair entry (graphRemove rest name)) + (equal? (fst entry) name)) + _ = []) + +graphFirstPending dependencies entries = find (name : maybe? (graphFind entries name)) dependencies + +-- Follow pending edges to the first repeated name: the witness belongs to the +-- cycle, even when the first blocked node merely leads into that cycle. +graphCycle current entries = (let rec + walk name seen = (lazyBool + (_ : name) + (_ : lazyMaybe + (_ : name) + (entry : lazyMaybe + (_ : name) + (dependency : walk dependency (pair name seen)) + (graphFirstPending (snd entry) entries)) + (graphFind entries name)) + (graphContains seen name)) + in walk current []) + +-- Select the earliest currently ready entry each round. This small-graph +-- algorithm uses linear membership/removal scans on each iteration. +graphSort entries = (let rec + walk pending available reversed = (case pending of + [] = ok (reverse reversed) t + [entry | _] = (lazyMaybe + (_ : err (graphCycle (fst entry) pending) t) + (ready : let name = fst ready in + walk (graphRemove pending name) (pair name available) (pair name reversed)) + (graphFirstReady pending available)) + _ = err t t) + in walk entries [] []) diff --git a/test/fixtures/reference-source/ee1531f86d1faaef4ce7b1ca9151bd2e7b10c3fc0574d0db67ddfce6c012c4e3.tri b/test/fixtures/reference-source/ee1531f86d1faaef4ce7b1ca9151bd2e7b10c3fc0574d0db67ddfce6c012c4e3.tri @@ -0,0 +1,120 @@ +!import base as B +!import handlers as H +!import io_v2 as V2 +!import io_v2_storage as Storage +!import io_v2_artifacts as Stream +!import arboricx_server as Store +!export load +!export loadFrom +!export publish +!export publishTo +!export withFilesystemV2 +!export withFilesystemForV2 +!export prepareFilesystemV2 +!export publishStreamV2 + +-- Domain Results always have empty remainders. Callers map them to HTTP statuses. +-- Invalid digests are ordinary input failures; +-- malformed request/reply protocols are interpreter failures. +-- Eight bounded bit steps, each checking exact Boolean shape and canonical +-- termination. The fixed continuation chain validates canonical octet shape +-- structurally, including the final high bit. +octetEnd = B.triage (t t) (_ : t) (_ _ : t) +octetStep next value = B.triage (t t) (_ : t) + (bit tail : B.triage + (B.triage t (_ : t) (_ _ : next tail) tail) + (child : B.triage (next tail) (_ : t) (_ _ : t) child) + (_ _ : t) bit) + value +octetValid = octetStep (octetStep (octetStep (octetStep + (octetStep (octetStep (octetStep (octetStep octetEnd))))))) +byteC = B.guardC "expected octet" octetValid + +-- These predicates preserve the original accepted list. The scanner allocates +-- one final Result and retains the first-error diagnostic and caller's remainder. +bytesC value rest = let rec + walk remaining = B.triage + (B.contractOk value rest) + (_ : B.contractErr "not a list" rest) + (byte tail : case octetValid byte of + (t t) = walk tail + _ = B.contractErr "expected octet" rest) + remaining + in walk value + +checkedDigest service digest next = H.checkWith Store.bundleHashC digest + (message : H.pure (B.err ["invalid-digest" message] t)) + (original : H.fail "invalid-contract" service "load" original) + next +loadFrom service digest = checkedDigest service digest + (checked : H.checkedRequest service "load" B.anyC loadReplyC checked) +load digest = loadFrom "artifacts" digest +publishTo service bytes = H.checkedRequest service "publish" bytesC publishReplyC bytes +publish bytes = publishTo "artifacts" bytes + +loadReplyC value{((t t), (bytes, t))} rest = case B.checkContract bytesC bytes of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "expected artifact bytes" rest +loadReplyC value{(t, (message, t))} rest = B.contractOk value rest +loadReplyC _ rest = B.contractErr "expected artifact-load Result with empty remainder" rest +publishReplyC value{((t t), (digest, t))} rest = case B.checkContract Store.bundleHashC digest of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "expected canonical artifact digest" rest +publishReplyC value{(t, (message, t))} rest = B.contractOk value rest +publishReplyC _ rest = B.contractErr "expected artifact-publication Result with empty remainder" rest + +-- Native v2 adapter for the domain interface. Limits count bytes; heap usage +-- requires a separate bound. Roots/paths follow OS authority. Verify and sync +-- every immutable winner, preserving corrupt winners for diagnosis. Loads +-- authenticate the exact bytes returned to the caller. +loadV2 root limit digest = do V2.onOk + bytes <- Storage.readBytes (Store.bundleObjectPath root digest) limit + actual <- Storage.hashBytes limit bytes + case B.equal? actual digest of + (t t) = V2.pure (B.ok bytes t) + _ = V2.pure (B.err ["integrity-failed" "artifacts/load" "digest mismatch" "unknown"] t) + +publishV2 root limit bytes = do V2.onOk + digest <- Storage.hashBytes limit bytes + case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = let directory = Store.objectDir root (Store.hashShard checked) in do V2.onOk + V2.ensureDirectory directory + V2.syncDirectory (Store.pathJoin root "objects") + Storage.publishBytes directory (Store.bundleObjectPath root checked) 0 limit bytes + V2.pure (B.ok checked t) + _ = V2.fail "Invalid artifact digest reply" + +filesystemV2 root limit service "load" digest = checkedDigest service digest + (checked : H.liftIO (loadV2 root limit checked)) +filesystemV2 root limit service "publish" bytes = H.checkWith bytesC bytes + (message : H.fail "invalid-request" service "publish" message) + (original : H.fail "invalid-contract" service "publish" original) + (checked : H.liftIO (publishV2 root limit checked)) +filesystemV2 _ _ service operation payload = H.forward service operation payload +withFilesystemForV2 service root limit action = H.handle service (filesystemV2 root limit service) action +withFilesystemV2 root limit action = withFilesystemForV2 "artifacts" root limit action + +-- Parent-first initialization; the configured root's parent must already exist. +-- Directory-entry synchronization follows host filesystem guarantees. +ensureV2 path = do V2.onOk + V2.ensureDirectory path + V2.syncDirectory (Storage.parentPath path) +prepareFilesystemV2 root = H.liftIO (do V2.onOk + ensureV2 root + ensureV2 (Store.pathJoin root "tmp") + ensureV2 (Store.pathJoin root "objects") + ensureV2 (Store.pathJoin root "aliases") + ensureV2 (Store.pathJoin (Store.pathJoin root "aliases") "names") + ensureV2 (Store.pathJoin (Store.pathJoin root "aliases") "packages") + ensureV2 (Store.pathJoin root "manifests") + V2.pure (B.ok t t)) + +-- Bounded producer, existing registry layout. The shard is chosen only after +-- hashing the complete stream incrementally. Temporary and object directories +-- must share a filesystem. +streamDestination root digest = H.liftIO (do V2.onOk + V2.ensureDirectory (Store.objectDir root (Store.hashShard digest)) + V2.syncDirectory (Store.pathJoin root "objects") + V2.pure (B.ok (Store.bundleObjectPath root digest) t)) +publishStreamV2 root expected producer = Stream.publishWith + (Store.pathJoin root "tmp") expected (streamDestination root) producer diff --git a/test/fixtures/reference-source/efa6213780eb641f7ec9ae6006d105a8110232ac06b455c335a5552324e58591.tri b/test/fixtures/reference-source/efa6213780eb641f7ec9ae6006d105a8110232ac06b455c335a5552324e58591.tri @@ -0,0 +1,286 @@ +!import prelude * +!import compiler_io * +!import source_object * +!import source_module * +!import source_resolve * +!import source_build * +!export * + +-- Explicit bootstrap staging map. Each canonical row is: +-- KIND<TAB>localAlias<TAB>legacyHeaderName<TAB>relativePath<TAB>comma-separated dependency aliases (or '-' for none) +-- KIND is unit (export local definitions only), reexport (also explicitly +-- re-export every declared dependency), or native (checked native headers and +-- explicit authored exports, staged without rewriting). The legacy name is checked against +-- an original !import header only. Paths and dependency edges come exclusively +-- from this file, never tricu.workspace. +sourceMapHeader = "arboricx.source-map.v1\n" +sourceMapSegmentsValid names = + lazyList + (_ : true) + (name rest : andLazy? (sourceModuleAliasValid name) + (_ : sourceMapSegmentsValid rest)) + names +sourceMapLegacyValid name = + sourceMapSegmentsValid (splitOnByte 46 name) +sourceMapPathValid path = + lazyList + (_ : false) + (stem suffix : lazyList + (_ : false) + (extension extra : and? (emptyList? extra) + (and? (equal? extension "tri") + (sourceMapSegmentsValid (splitOnByte 47 stem)))) + suffix) + (splitOnByte 46 path) +sourceMapDependenciesValid deps rows = + lazyList + (_ : true) + (dep rest : andLazy? + (and? (sourceModuleAliasValid dep) + (and? (not? (emptyList? dep)) + (lazyMaybe (_ : false) (_ : true) (sourceFindExternal rows dep)))) + (_ : sourceMapDependenciesValid rest rows)) + deps +sourceMapParseRow row seen = + lazyList + (_ : err "invalid source map row" t) + (tag rest1 : lazyList + (_ : err "invalid source map row" t) + (name rest2 : lazyList + (_ : err "invalid source map row" t) + (legacy rest3 : lazyList + (_ : err "invalid source map row" t) + (path rest4 : lazyList + (_ : err "invalid source map row" t) + (depString extra : lazyBool + (_ : lazyMaybe + (_ : ok (pair name + (pair legacy (pair path + (pair (lazyBool (_ : t) (_ : splitOnByte 44 depString) + (equal? depString "-")) tag)))) t) + (_ : err "duplicate source map name" t) + (sourceFindExternal seen name)) + (_ : err "invalid source map name, path or fields" t) + (and? (or? (or? (equal? tag "unit") (equal? tag "native")) + (and? (equal? tag "reexport") + (not? (equal? depString "-")))) + (and? (emptyList? extra) + (and? (sourceModuleAliasValid name) + (and? (sourceMapLegacyValid legacy) + (sourceMapPathValid path)))))) + rest4) + rest3) + rest2) + rest1) + (splitOnByte 9 row) +sourceMapParseRows rows seen = + lazyList + (_ : ok (reverse seen) t) + (row rest : lazyList + (_ : lazyBool + (_ : ok (reverse seen) t) + (_ : err "blank source map row" t) + (emptyList? rest)) + (_ _ : lazyResult + (problem unused : err problem t) + (entry unused : sourceMapParseRows rest (pair entry seen)) + (sourceMapParseRow row seen)) + row) + rows +-- A false dependency check is a Result error, not an implicit lookup. +sourceMapValidateDeps rows all = + lazyList + (_ : ok all t) + (row rest : lazyBool + (_ : sourceMapValidateDeps rest all) + (_ : err "undeclared source map dependency" t) + (sourceMapDependenciesValid (fst (snd (snd (snd row)))) all)) + rows +sourceMapDependencyField deps = + lazyList (_ : "-") (_ _ : intercalate "," deps) deps +sourceMapEncodeRows rows = + lazyList + (_ : t) + (row rest : append (snd (snd (snd (snd row)))) + (append "\t" (append (fst row) + (append "\t" (append (fst (snd row)) + (append "\t" (append (fst (snd (snd row))) + (append "\t" (append + (sourceMapDependencyField (fst (snd (snd (snd row))))) + (append "\n" (sourceMapEncodeRows rest))))))))))) + rows +sourceMapEncode rows = append sourceMapHeader (sourceMapEncodeRows rows) +sourceMapDecode bytes = + lazyList + (_ : err "empty source map" t) + (header rows : lazyBool + (_ : lazyResult + (problem unused : err problem t) + (entries unused : lazyList + (_ : err "source map requires a unit" t) + (_ _ : lazyResult + (problem unused : err problem t) + (validated unused : lazyBool + (_ : ok validated t) + (_ : err "noncanonical source map" t) + (equal? bytes (sourceMapEncode validated))) + (sourceMapValidateDeps entries entries)) + entries) + (sourceMapParseRows rows t)) + (_ : err "unsupported source map version" t) + (equal? header "arboricx.source-map.v1")) + (splitOnByte 10 bytes) + +sourceMapExpectedImport legacy = + pair "!import" (pair (append "\"" (append legacy "\"")) + (pair "!Local" t)) +sourceMapStripHeader original rows map = + lazyList + (_ : lazyList + (_ : ok t t) + (line rest : lazyList + (_ : ok (unlines (pair line rest)) t) + (head tail : lazyBool + (_ : err "undeclared bootstrap import" t) + (_ : ok (unlines (pair line rest)) t) + (equal? "!import" head)) + (words line)) + original) + (dep rest : lazyMaybe + (_ : err "undeclared source map dependency" t) + (entry : lazyList + (_ : err "missing bootstrap import" t) + (line tail : lazyBool + (_ : sourceMapStripHeader tail rest map) + (_ : err "bootstrap import differs from explicit source map" t) + (equal? (words line) (sourceMapExpectedImport (fst entry)))) + original) + (sourceFindExternal map dep)) + rows +-- Compare derived local edges, not the spelling or layout of declarations. +-- Native author bytes, namespaces and exports remain unchanged. +sourceMapCheckNativeHeader source deps = (bindResult + (sourceModuleHeaderStage source) (staged _ : lazyBool + (_ : ok t t) + (_ : err "native source differs from explicit source map" t) + (equal? (fst staged) deps))) +sourceMapPrepareBody row original all = + lazyBool + (_ : lazyResult + (problem unused : err problem t) + (_ unused : ok original t) + (sourceMapCheckNativeHeader original + (fst (snd (snd (snd row)))))) + (_ : sourceMapStripHeader (lines original) + (fst (snd (snd (snd row)))) all) + (equal? (snd (snd (snd (snd row)))) "native") +sourceMapUseHeader deps = + lazyList + (_ : t) + (dep rest : append "!import " + (append dep (append " *\n" (sourceMapUseHeader rest)))) + deps +sourceMapReexportHeader deps = + lazyList + (_ : t) + (dep rest : append "!export * from " + (append dep (append "\n" (sourceMapReexportHeader rest)))) + deps +sourceMapLegacyOverlay row body = + (append (sourceMapUseHeader (fst (snd (snd (snd row))))) + (append "!export *\n" + (append (lazyBool + (_ : sourceMapReexportHeader (fst (snd (snd (snd row))))) + (_ : t) + (equal? (snd (snd (snd (snd row)))) "reexport")) + body))) +sourceMapOverlay row body = (lazyBool + (_ : body) + (_ : sourceMapLegacyOverlay row body) + (equal? (snd (snd (snd (snd row)))) "native")) +sourceMapStageOne sourceRoot overlayRoot all row = + onResult_ (readSourceFile (append sourceRoot (append "/" + (fst (snd (snd row))))) ) + (problem : pure (err problem t)) + (original : lazyResult + (problem unused : pure (err problem t)) + (body unused : onOk_ (lazyBool + (_ : pure (ok (fst original) t)) + (_ : encodeSourceText (sourceMapOverlay row body)) + (equal? (snd (snd (snd (snd row)))) "native")) + (overlay : onOk_ + (sourceObjectDigest "arboricx.source.v1" (fst original)) + (rawDigest : onOk_ + (sourceObjectDigest "arboricx.source.v1" overlay) + (overlayDigest : onOk_ + (atomicWriteBytes (append overlayRoot + (append "/" (append (fst row) ".tri"))) overlay) + (_ : pure (ok + (pair (fst row) (pair rawDigest overlayDigest)) t)))))) + (sourceMapPrepareBody row (snd original) all)) +sourceMapStageRows rows sourceRoot overlayRoot all records = + lazyList + (_ : pure (ok (reverse records) t)) + (row rest : onResult_ (sourceMapStageOne sourceRoot overlayRoot + all row) + (problem : pure (err problem t)) + (record : sourceMapStageRows rest sourceRoot overlayRoot all + (pair record records))) + rows +sourceMapRecords records = + lazyList + (_ : t) + (record rest : append "input\t" (append (fst record) + (append "\t" (append (fst (snd record)) + (append "\t" (append (snd (snd record)) + (append "\n" (sourceMapRecords rest)))))))) + records +sourceMapBuildReceipt mapHash compiler rootName result records = + append "arboricx.source-map-build.v1\n" + (append "map\t" (append mapHash + (append "\ncompiler\t" (append compiler + (append "\nroot\t" (append rootName + (append "\t" (append (fst result) + (append "\t" (append (snd result) + (append "\n" (sourceMapRecords records)))))))))))) + +-- Prepare a checked overlay from a versioned, content-addressed source map. +-- Returns the immutable map ID and input (original, overlay) digest rows. +sourceMapStage sourceRoot overlayRoot casRoot mapBytes = + lazyResult + (problem unused : pure (err problem t)) + (rows unused : onResult_ (sourceObjectStore casRoot + "arboricx.source-map.v1" mapBytes) + (problem : pure (err problem t)) + (mapHash : onOk_ (createDirectory overlayRoot) (_ : + onResult_ (sourceMapStageRows rows sourceRoot overlayRoot + rows t) + (problem : pure (err problem t)) + (records : pure (ok (pair mapHash (pair rows records)) t))))) + (sourceMapDecode mapBytes) + +-- Root build receipt references the complete map and source input snapshot; +-- the regular module receipts still describe each generated source unit. +sourceMapBuildWith buildGraph sourceRoot overlayRoot casRoot compiler mapBytes rootName = + onResult_ (sourceMapStage sourceRoot overlayRoot casRoot mapBytes) + (problem : pure (err problem t)) + (staged : lazyMaybe + (_ : pure (err "root missing from source map" t)) + (_ : onResult_ (buildGraph overlayRoot casRoot compiler rootName) + (problem : pure (err problem t)) + (result : onResult_ (sourceObjectStore casRoot + "arboricx.source-map-build.v1" + (sourceMapBuildReceipt (fst staged) compiler rootName result + (snd (snd staged)))) + (problem : pure (err problem t)) + (buildReceipt : pure (ok (pair result buildReceipt) t)))) + (sourceFindExternal (fst (snd staged)) rootName)) + +-- Explicit generation backends share staging, graph policy and map receipts. +-- The selected graph owns its distinct module options identity; neither wrapper +-- catches compilation failure or attempts another backend. +sourceMapBuild sourceRoot overlayRoot casRoot compiler mapBytes rootName = + sourceMapBuildWith sourceBuildGraph sourceRoot overlayRoot casRoot compiler mapBytes rootName +sourceMapBuildStable sourceRoot overlayRoot casRoot compiler mapBytes rootName = + sourceMapBuildWith sourceBuildGraphStable sourceRoot overlayRoot casRoot compiler mapBytes rootName + diff --git a/test/fixtures/reference-source/fcd88bebeab55f69199836dfcdd8fe9007c5dcb7c7e951c7b4e7a4107ce85e44.tri b/test/fixtures/reference-source/fcd88bebeab55f69199836dfcdd8fe9007c5dcb7c7e951c7b4e7a4107ce85e44.tri @@ -0,0 +1,106 @@ +!import prelude * +!import http * +!import native_math as Nat +!export * + +-- Store root is trusted configuration. Every read/write entrypoint must validate +-- a hash before passing it to these pure path constructors. +pathJoin parent child = append parent (append "/" child) +objectDir root shard = pathJoin (pathJoin root "objects") shard + +hashShard [a | [b | [c | _]]] = [a b c] +hashShard [a | [b | _]] = [a b] +hashShard [a | _] = [a] +hashShard _ = [] + +bundleObjectPath root hash = pathJoin (objectDir root (hashShard hash)) (append hash ".arboricx") + +-- Exact canonical lowercase SHA-256 spelling. Literal patterns, unlike numeric +-- range predicates, reject malformed numeral trees without normalizing them. +-- Disjoint compact-bit ranges: 48..55, 56..57, 97, 98..99, +-- 100..101, 102. Every captured bit is checked as an exact Boolean. +bundleBit? t = true +bundleBit? (t t) = true +bundleBit? _ = false +bundleHexByte? (a, (b, (c, 6))) = and? (bundleBit? a) (and? (bundleBit? b) (bundleBit? c)) +bundleHexByte? (a, 28) = bundleBit? a +bundleHexByte? 97 = true +bundleHexByte? (a, 49) = bundleBit? a +bundleHexByte? (a, 50) = bundleBit? a +bundleHexByte? 102 = true +bundleHexByte? _ = false + +-- The counter is internal and canonical. At zero inspect only the list shape: +-- never force the 65th byte or its suffix, even for arbitrary malformed trees. +bundleHashBytes [] 0 = true +bundleHashBytes _ 0 = false +bundleHashBytes [byte | rest] remaining = case (bundleHexByte? byte) of + ^true = bundleHashBytes rest (Nat.subtract remaining 1) + _ = false + +bundleHashBytes _ _ = false + +-- The fixed SHA-256 boundary uses structural fuel instead of subtracting a +-- compact-natural counter per character. Retain bundleHashBytes for callers +-- that explicitly supply a length. Both stop before inspecting a 65th element. +bundleHashFuel8 tail = t (t (t (t (t (t (t (t tail))))))) +bundleHashFuel64 = bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 + (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 (bundleHashFuel8 t))))))) +bundleHashScan [] t = true +bundleHashScan _ t = false +bundleHashScan [byte | rest] (t fuel) = case bundleHexByte? byte of + ^true = bundleHashScan rest fuel + _ = false +bundleHashScan _ _ = false +bundleHashValid? hash = bundleHashScan hash bundleHashFuel64 +bundleHashC = guardC "expected 64 lowercase hexadecimal hash characters" bundleHashValid? + +-- A proper list prefix; return its exact suffix, without normalizing the tail. +stripPrefix [] input = just input +stripPrefix [wanted | prefix] [byte | rest] = case byte of + ^wanted = stripPrefix prefix rest + _ = nothing + +stripPrefix _ _ = nothing + +bundleHashPrefix = "/_arboricx/bundle/hash/" +bundlePath = "/_arboricx/bundle" +healthPath = "/_arboricx/health" +bundleContentType = "application/vnd.arboricx.bundle" + +htmlLandingPage = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width, initial-scale=1'><title>Arboricx Server</title></head><body><h1>Arboricx Server</h1><p>Bundle registry</p><p><a href='https://git.eversole.co/James/tricu'>Made with Love (and trees, lots of trees)</a></p></body></html>" + +bundleResponse bytes = response 200 bundleContentType bytes + +-- Read-only exact-version index. Administrative publication happens locally; +-- this route never resolves dependencies or executes fetched code. HTTP input +-- bytes are canonical naturals. Scan at most 128 bytes per path component. +packagePrefix = "/_arboricx/package/" +packageAlphaNum? byte = (or? (Nat.inRange byte 48 57) + (or? (Nat.inRange byte 65 90) (Nat.inRange byte 97 122))) +packageByte? byte = (or? (packageAlphaNum? byte) + (or? (equal? byte 46) (or? (equal? byte 95) (equal? byte 45)))) +packageVersion [] reversed _ = just (reverse reversed) +packageVersion _ _ 0 = nothing +packageVersion [byte | rest] reversed remaining = case (packageByte? byte) of + ^true = packageVersion rest (pair byte reversed) (Nat.subtract remaining 1) + _ = nothing + +packageVersion _ _ _ = nothing +packageName [47 | [first | rest]] reversed _ = case (packageAlphaNum? first) of + ^true = case (packageVersion rest [first] 127) of + (t version) = just (pair (reverse reversed) version) + _ = nothing + _ = nothing + +packageName _ _ 0 = nothing +packageName [byte | rest] reversed remaining = case (packageByte? byte) of + ^true = packageName rest (pair byte reversed) (Nat.subtract remaining 1) + _ = nothing + +packageName _ _ _ = nothing +packageTarget [first | rest] = case (packageAlphaNum? first) of + ^true = packageName rest [first] 127 + _ = nothing + +packageTarget _ = nothing diff --git a/test/fixtures/reference-source/fe110d74ff62d9f16683bae0adcf82c65ce5c4c18cf2ada50927bf2509e0fa5e.tri b/test/fixtures/reference-source/fe110d74ff62d9f16683bae0adcf82c65ce5c4c18cf2ada50927bf2509e0fa5e.tri @@ -0,0 +1,121 @@ +!import base as B +!import handlers as H +!import io_v2 as V2 +!import io_v2_storage as Storage +!import io_v2_artifacts as Stream +!import arboricx_server as Store +!export load +!export loadFrom +!export publish +!export publishTo +!export withFilesystemV2 +!export withFilesystemForV2 +!export prepareFilesystemV2 +!export publishStreamV2 + +-- Domain Results always have empty remainders. HTTP status mapping belongs to +-- the caller, not this interface. Invalid digests are ordinary input failures; +-- malformed request/reply protocols are interpreter failures. +-- Eight bounded bit steps, each checking exact Boolean shape and canonical +-- termination. The continuation chain replaces a per-bit recursive fuel test; +-- it neither accepts redundant high zeros nor delegates to unchecked arithmetic. +octetEnd = B.triage (t t) (_ : t) (_ _ : t) +octetStep next value = B.triage (t t) (_ : t) + (bit tail : B.triage + (B.triage t (_ : t) (_ _ : next tail) tail) + (child : B.triage (next tail) (_ : t) (_ _ : t) child) + (_ _ : t) bit) + value +octetValid = octetStep (octetStep (octetStep (octetStep + (octetStep (octetStep (octetStep (octetStep octetEnd))))))) +byteC = B.guardC "expected octet" octetValid + +-- These contracts are predicates, never element transformers. Scan without +-- rebuilding the accepted list or allocating a Result for each accepted byte. +-- Preserve the old first-error diagnostics and the caller's remainder. +bytesC value rest = let rec + walk remaining = B.triage + (B.contractOk value rest) + (_ : B.contractErr "not a list" rest) + (byte tail : case octetValid byte of + (t t) = walk tail + _ = B.contractErr "expected octet" rest) + remaining + in walk value + +loadFrom service digest = case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = H.checkedRequest service "load" B.anyC loadReplyC checked + (t, (message, _)) = H.pure (B.err ["invalid-digest" message] t) + _ = H.fail "invalid-contract" service "load" digest +load digest = loadFrom "artifacts" digest +publishTo service bytes = H.checkedRequest service "publish" bytesC publishReplyC bytes +publish bytes = publishTo "artifacts" bytes + +loadReplyC value{((t t), (bytes, t))} rest = case B.checkContract bytesC bytes of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "expected artifact bytes" rest +loadReplyC value{(t, (message, t))} rest = B.contractOk value rest +loadReplyC _ rest = B.contractErr "expected artifact-load Result with empty remainder" rest +publishReplyC value{((t t), (digest, t))} rest = case B.checkContract Store.bundleHashC digest of + ((t t), _) = B.contractOk value rest + _ = B.contractErr "expected canonical artifact digest" rest +publishReplyC value{(t, (message, t))} rest = B.contractOk value rest +publishReplyC _ rest = B.contractErr "expected artifact-publication Result with empty remainder" rest + +-- Native v2 adapter for the same domain interface. Limits are explicit byte +-- budgets, not heap guarantees. Roots/paths follow OS authority, not v1 grants. +-- Objects are immutable: verify and sync every winner; never repair corruption +-- by overwriting it. Loads authenticate the exact bytes returned to the caller. +loadV2 root limit digest = do V2.onOk + bytes <- Storage.readBytes (Store.bundleObjectPath root digest) limit + actual <- Storage.hashBytes limit bytes + case B.equal? actual digest of + (t t) = V2.pure (B.ok bytes t) + _ = V2.pure (B.err ["integrity-failed" "artifacts/load" "digest mismatch" "unknown"] t) + +publishV2 root limit bytes = do V2.onOk + digest <- Storage.hashBytes limit bytes + case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = let directory = Store.objectDir root (Store.hashShard checked) in do V2.onOk + V2.ensureDirectory directory + V2.syncDirectory (Store.pathJoin root "objects") + Storage.publishBytes directory (Store.bundleObjectPath root checked) 0 limit bytes + V2.pure (B.ok checked t) + _ = V2.fail "Invalid artifact digest reply" + +filesystemV2 root limit service "load" digest = case B.checkContract Store.bundleHashC digest of + ((t t), (checked, t)) = H.liftIO (loadV2 root limit checked) + (t, (message, _)) = H.pure (B.err ["invalid-digest" message] t) + _ = H.fail "invalid-contract" service "load" digest +filesystemV2 root limit service "publish" bytes = case B.checkContract bytesC bytes of + ((t t), (checked, t)) = H.liftIO (publishV2 root limit checked) + (t, (message, _)) = H.fail "invalid-request" service "publish" message + _ = H.fail "invalid-contract" service "publish" bytes +filesystemV2 _ _ service operation payload = H.forward service operation payload +withFilesystemForV2 service root limit action = H.handle service (filesystemV2 root limit service) action +withFilesystemV2 root limit action = withFilesystemForV2 "artifacts" root limit action + +-- Parent-first initialization; the configured root's parent must already exist. +-- Sync directory entries under host filesystem guarantees, not a crash theorem. +ensureV2 path = do V2.onOk + V2.ensureDirectory path + V2.syncDirectory (Storage.parentPath path) +prepareFilesystemV2 root = H.liftIO (do V2.onOk + ensureV2 root + ensureV2 (Store.pathJoin root "tmp") + ensureV2 (Store.pathJoin root "objects") + ensureV2 (Store.pathJoin root "aliases") + ensureV2 (Store.pathJoin (Store.pathJoin root "aliases") "names") + ensureV2 (Store.pathJoin (Store.pathJoin root "aliases") "packages") + ensureV2 (Store.pathJoin root "manifests") + V2.pure (B.ok t t)) + +-- Bounded producer, existing registry layout. The shard is chosen only after +-- hashing the complete stream, never by buffering the whole body or guessing its +-- digest. Temporary and object directories must share a filesystem. +streamDestination root digest = H.liftIO (do V2.onOk + V2.ensureDirectory (Store.objectDir root (Store.hashShard digest)) + V2.syncDirectory (Store.pathJoin root "objects") + V2.pure (B.ok (Store.bundleObjectPath root digest) t)) +publishStreamV2 root expected producer = Stream.publishWith + (Store.pathJoin root "tmp") expected (streamDestination root) producer diff --git a/test/fixtures/support-cohort-v4.v1 b/test/fixtures/support-cohort-v4.v1 @@ -0,0 +1,109 @@ +module socket socket lib/socket.tri 5014c5c41aa63d3b8a52e6cd89600b435505d93325f730b532c9eabaffd58421 +module intensional intensional lib/intensionalContracts.tri 4b9470311bb8984bf311371c6b1c056ac971e073c60be5a433a5826780c31d38 +module binary binary lib/binary.tri 6dcf2f2087adef04a9b60330563c58c782ab51678622fafdbd7c7a6b14d20483 +module http http lib/http.tri 137f8881288b64071a60fc4e29ae2b276d32bac79f748b8f2174838b9da1ffc0 +module arboricx.common arboricx_common lib/arboricx/common.tri a8bf1c965133ed0c2fd253c73c4a8cc5330f68de554b470c29f6e659a1174115 +module arboricx.nodes arboricx_nodes lib/arboricx/nodes.tri 6c0e9998875942bb19c1f783ebffe61b23aa1eb578a46a874034444c4a9a3c49 +module arboricx.manifest arboricx_manifest lib/arboricx/manifest.tri 880ca4eefe955ed2ac38ca0a6ce8f04e41e44aeaf32c82b1bd4d8fb7342d9dba +module arboricx arboricx lib/arboricx/arboricx.tri 0faaece47cc9b961a9d1e0ea0bca82c33453ad85b383c95152ed6db4fafd22b2 +module arboricx.dispatch arboricx_dispatch lib/arboricx/dispatch.tri ba6facabd08809542b8cbc57b81cbafeb9b94cf7c771d313702685e36d06ff7f +module arboricx.server arboricx_server lib/arboricx/server.tri 7bce76817a1c9a6df5253b7f983a395d833e3622533a2d209c12a5c0f111aa5f +module source.build-stable source_build_stable lib/source/build-stable.tri 240f25295a1fa23fe29faacb204a4bb5b0bda5b9b6d64185ee8135374e1c10c5 +module source.export-cache source_export_cache lib/source/export-cache.tri 378ca283a0ef856e6fde25e88ebd321b89eb348ef52232a5895f4240be0b5369 +module demos.equality demos_equality demos/equality.tri d14168490eeb3a4bab3c431d4ae936c51ead54e4e7155d9dded065442f2b9da0 +module demos.size demos_size demos/size.tri b8d20a3ef0666e85684b34079e438ffbb1c7a7444b9c3ae6472cefd4fd21dcfd +module demos.toSource demos_toSource demos/toSource.tri a1d060c00dae207ae61cdbfb15027a585cd650e748b83cf5b5c231f02fe5e1b7 +module demos.patternMatching demos_patternMatching demos/patternMatching.tri 82199ce4c4ba4c014c2cd09bdcc0644732c2f6863c0eff2710f4156dcb50a1e5 +module demos.levelOrderTraversal demos_levelOrderTraversal demos/levelOrderTraversal.tri 1278072b994e7b4f50b42f32ae7c94b09c9bea23a1c4aaee12ec2d14f2387cec +module demos.contractEffects demos_contractEffects demos/contractEffects.tri 67252839e937e841c9fdc37a2518622a1da5bfd907d983494e92ee30b10427f4 +module demos.interactionTrees demos_interactionTrees demos/interactionTrees.tri dd2f44109a0d14aa50e6a670067832720ff882f1ce7d8fac094d82280cbfb005 +module demos.runArboricxBundle demos_runArboricxBundle demos/runArboricxBundle.tri e014061b2107cb0d8d1d2366742c8f35640c9a7dd85b80fc78d4afc7813bf0cc +module demos.interactionTrees.greet demos_interactionTrees_greet demos/interactionTrees/greet.tri 40783b2c9c5750583151dda9d04b435d3d0d8a63b6f33cccfa94fd8ed958c14a +module demos.interactionTrees.environment demos_interactionTrees_environment demos/interactionTrees/environment.tri 4dcb43187b3cbd168fcb3ad5269be3e96257aa30773bca933e6cf37dfbdee066 +module demos.interactionTrees.state demos_interactionTrees_state demos/interactionTrees/state.tri 61799bafcab20f90faea5562f2c25c89bdab164571ac6073e4c029c1f86e5b66 +module demos.interactionTrees.shout demos_interactionTrees_shout demos/interactionTrees/shout.tri 060a440835999d443ef767ea5d4e9efe616a7186e1bb04947d6f9f80a21b0a4b +module demos.interactionTrees.forkAwait demos_interactionTrees_forkAwait demos/interactionTrees/forkAwait.tri 79c07186e9a3a36ce4c1de54b331c42dec6b95aa974156677c9336fd1c0849d7 +module demos.interactionTrees.writeThenRead demos_interactionTrees_writeThenRead demos/interactionTrees/writeThenRead.tri 65c006672139d98c27b5022a8641c73b6d0c9c29dd42f6cfe8b4820928664807 +module demos.interactionTrees.safeRead demos_interactionTrees_safeRead demos/interactionTrees/safeRead.tri d52075fd243ce733529740023181faa44c7c3022a9f28ee7ea2aaa078d95f7b2 +module demos.interactionTrees.yield demos_interactionTrees_yield demos/interactionTrees/yield.tri 50691f992e348fb47baccc119c3809f7e33fdcce5c801b23ed8635962f346655 +module demos.interactionTrees.getLineAsync demos_interactionTrees_getLineAsync demos/interactionTrees/getLineAsync.tri 2b971f5cfdfea12210f6ed1801a3660735e1da97d4d372cf751eb76879750991 +module demos.interactionTrees.echoServer demos_interactionTrees_echoServer demos/interactionTrees/echoServer.tri 834a2c8b9c9007364036bb22159ca0c5050c1536e87c4c40bf5573efee863e30 +module demos.interactionTrees.arboricxServer demos_interactionTrees_arboricxServer demos/interactionTrees/arboricxServer.tri 4dc6a84c739f524f09e5378ddb79140fd11629988a8a451c0e823f6c1dc21e96 +module demos.contractBasics demos_contractBasics demos/contractBasics.tri 6ffab139ecc27fd6b8ddbe6f8a134c91abba2b0491f5e28602dbe2dcd773928f +module demos.interactionTrees.httpServer demos_interactionTrees_httpServer demos/interactionTrees/httpServer.tri 386257391872e7b73befc2bcc3634706acf4acf0ff791bc60161179434c9a813 +module source.staging-experiment source_staging_experiment lib/source/staging-experiment.tri 1993cce4a57c4225bf46d355be9436b48ceeffbcaddffd2f5e8184550f0fe7b4 +module source.staged-list-experiment source_staged_list_experiment lib/source/staged-list-experiment.tri 179aa28c5e59df55dc1fe9abdf616b509e8f6cb485ba5e615d956aadfd1d4a54 +module source.staged-conditional-experiment source_staged_conditional_experiment lib/source/staged-conditional-experiment.tri 989783bbdb71be5447803503f60925672ff29fad15804da31c8f7f83b8c402ad +module tests.stagingExperiment tests_stagingExperiment test/stagingExperiment.tri 7a5a63f29ea34723bf72330c66f6c4b1b9a9a58988c5b8bd2d04ca9ab861fb03 +module tests.stagedConditionalRunner tests_stagedConditionalRunner test/stagedConditionalRunner.tri 9f9f7c11a470595fa8d92933219bdb7653bc5ff647eff7afc352504e0c29eb66 +module patterns patterns lib/patterns.tri d61f2de7b3dce93c9b8914fb980224437d0ea4207787caab9098041cf5bfe896 +module arboricx_handlers arboricx_handlers lib/arboricx/handlers.tri 06944982576cd127727cf48915b1842237308ac7e1b517b3b21a8846fd577dc3 +module arboricx_streaming_v2 arboricx_streaming_v2 lib/arboricx/streaming-v2.tri 870df776ac7142fac5aad7f0169dfd08b2934f2bfa61ad3ae1c9b949f8f75088 +module handler_artifacts handler_artifacts lib/handlers/artifacts.tri 5e4d9186f5235078c786432b6ac8a2952ee8c87bed8f808503d5107daaa07bac +module handler_log handler_log lib/handlers/log.tri 06e81c8b62b8f26ba20faa3280b381ddd55ecf246876a21b028175bbeb87f896 +module handlers handlers lib/handlers.tri 369b3d37d4704c1c28f038df3392572ab906c11bbdde16710f97b33fdc3e54fd +module io_v2 io_v2 lib/io-v2.tri bea75689ba66f19e241f97369ea7ee1393fabbaa7c7eac9b9a2d0ca1e455a1bf +module io_v2_artifacts io_v2_artifacts lib/io-v2/artifacts.tri 4e2c4278cd4efaa9c2d9d7db51fb2a7a0c99fe2e9ac76bc3ea89bd26e47ba4b7 +module io_v2_http io_v2_http lib/io-v2/http-transport.tri b881a086294ff264567d758dd2c1ef7a3470faa909ec588e9f7bb8c4430f7bfc +module io_v2_storage io_v2_storage lib/io-v2/storage.tri c60b2bdf222db8ed812c2af4da3609cceefc8689c8aeb80fa52795d69193d0f5 +module io_v2_text io_v2_text lib/io-v2/text.tri 9acae0f91625a1f6e13601276f2e11b319d346d872e85d758377fd649da5d5d8 +relocated compiler-library patterns patterns lib/patterns.tri +added arboricx_handlers arboricx_handlers lib/arboricx/handlers.tri +added arboricx_streaming_v2 arboricx_streaming_v2 lib/arboricx/streaming-v2.tri +added handler_artifacts handler_artifacts lib/handlers/artifacts.tri +added handler_log handler_log lib/handlers/log.tri +added handlers handlers lib/handlers.tri +added io_v2 io_v2 lib/io-v2.tri +added io_v2_artifacts io_v2_artifacts lib/io-v2/artifacts.tri +added io_v2_http io_v2_http lib/io-v2/http-transport.tri +added io_v2_storage io_v2_storage lib/io-v2/storage.tri +added io_v2_text io_v2_text lib/io-v2/text.tri +rebuilt socket f0105c508a0d5edb2511541126145533330447d4f106cb4900a5d567982a9982 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt intensional 08343113b4851f67766002014148543a11ecc317abbe54918c0b6a8c143387db 7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt binary effa7bb25fbd71e7ee54d9ba5ba53e76b05aebb2eb7b50e021a12d9b2a3bda94 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt http 904bfaf20a524483b9d62f223eb680cb9d91274c1ef468c35c586707ce656347 280dea7b29a3dc280a5cc5fda0c096a7698b36226d22c3e51ca93d39336dfa02 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt arboricx_common b4990c9065292e67a8630930dd74228daa8d9eeeafa5b9dea3b3c853de2a2435 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt arboricx_nodes 4e0f65135ce3db43c32eb43e72d99e9cc42979f28fec0df0b3cfaae2006c9018 d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt arboricx_manifest dabe4637c448146d99055588096e1485678d8d7637c97f247b76e956bd0ec9d3 2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt arboricx 0c9361d459580541fa1962a9c43d747a3564378beb2266b52f765465518517ea 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt arboricx_dispatch 754c396fa5c584d78405957e577f037eda481c6abd2ad4634487355f5e135ed1 eda078ba018de68718b2b7aa2d790578ac079cf98c963a78f7fcae5178fd2c19 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt arboricx_server 6854f28b311d2c556dabfed3cf3793a7f804c908f5101b2fab0f23cad7ef4d48 800fd0557eb020bf4395e5b73d9a819d9c730da60f83351261b4cd8c07610ca6 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt source_build_stable 509b96ab0d3f88813e3c7d989f150cb0da768f8b7789c421e8fbae68b394a68a 899ebd78231147cbf6b72a71298a72c7047d6fb1d8033fac88898e178613e6a7 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt source_export_cache b313a63a7fcfffe7ef2d6606dc9aa3c27b85bcecc004ceb232935583a97f68ef 7f29e52b9154b0ba6410836b055f8628f76bc51aa572efa2ef691f37ecaec077 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_equality 96db578fc577cc690363849555686cfb01a3157f17bcab42e0c2bbc5f1f06287 96bb649c2206620cd514820a8d8ca9dedb3cb3b713744bdff1ef500d9ec55ab0 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_size 3fb10da4b1085e3fd35a862ff6bb4ba93160b0997f0b3382f916bdee73b3c051 c628107a60003ede5cac28ee6f6f20e2fc74d01f699924733a1c54001ebee934 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_toSource c67281b62fc8831ec5f638f5416dc2564e9dd8d885caf4dd9d9ee7b1a89bad09 31148e782eb67ea2265ee9e0fd2a186ef2d2c24c1c8f38f98c0f3e8df955cb69 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_patternMatching 2c8624382d69872088eeb9950b33cf39b314678ec2089af9dd41a2ae61d8d972 6ce77cdea44f0bbb9ff4b8e59b07b48b79a2aff349f07b6e9c57a225a0ae1f79 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_levelOrderTraversal 7d04b0eb83f80e0b13d6ce8689d9a6f1372c426f586e6dd26d1649be4c0b54d4 a83874999378a9ff9b15ea99ed7f51c90a2dc48b0fd6b7d8980a29ffa088582c 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_contractEffects 6d3b99bae3a9bdc38c9f0d7c60c0ac368e1c0297d6dd44907c0ae009b38e0b26 8ef18c3d4c4a817d6afc966295a2783f4caaa3411e7ff4081f44fd8af6f7e872 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees 4f51c4872fbcb378391aee31026c4ec7f0fd22d94491a5b83fb5670c66ffcb3c d660fd8fd3543687f0c9efb715a8e16b1e7cc194e127ee6741be3079e7d9eb7f 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_runArboricxBundle 1bc9d3708247ada3bc8ada1f25ba19b11b69bcd8b1f86917977c52f87cdefc61 cf6c20277f27afe0677b012920a10bf32a12929b8cfaa310620cf33143cd4ce1 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_greet 2861b4cb304e27c7a290e56e94092c54f716cb1088deb5db87e2b2cd7021f86a bde116bd78a59e12c6add24a004d0941b82e89bba5d3282c570559d930dd0d17 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_environment b0fa3427d4d048440063fcf7758b84045da66d54e02a05e355f3ac97fa779c26 e645ead315c7c3c16a0d4b43d27a89ebc0d54495a4ca69e82c3b583de224f43c 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_state 592be139e21fac8cdc4cfd4decaa54e6c4f73fda87856f51d7b82647ede963e4 763bc6ea4cb0bcab7b9b92aed6947d9220a2823a6bd9885d3a013a98b92c86ca 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_shout 1263206f723df4f53acf6f0799946556db1638e8933c9646f10da9f1640d514a bc75e4f1698d736428575fb190f188b185f2e668db10f62fe7886866c579da84 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_forkAwait df829c9212ef1ea94e630340c0e9ef3d7e7b24d75e79422f8bdbad97a7e9da3b 2512a36cfe8af8f4166ba25c9b88d5c398e1535483370fe3c3e1379786d5caae 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_writeThenRead c10d2d047b292a31796738fd9c57d3093d73e60a2eb639f392efdf5f8172113c 4ea3868438079b5153746f618b98c190a8f9be5bc0a0035298f65612380adb9c 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_safeRead e2c902689b9e6ff217f5cdf7e7f6cb99f233bebd7b0dead679f8c34548d903c4 107bb9e32aeabf317a50c555fd2c82fda73c54654ee95d4c185717c7d0a06673 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_yield a3fd6f9052341b2c0ef1bdc8ecf565c3f34736a239697c6414b40262e911d3c1 35de7c2e18beed6606727dc63b85a0bc201d96490cf2b33763e846cedd53d54d 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_getLineAsync 48ba422d654e0f46267f5e5f12054f37e95822b48e2b485d05042853d949747a 54f2765098bd2580e08d2b8390899cbe49e956fbf037e7e4697e867d3001e464 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_echoServer 468865595f6251f67bbdaa4ac4f0358d5cab7fd2cfa79978c622456574830022 75aee7d8167931634b5bd5566aa0c06a45be0f5a3c5c16aa86d2b1a75d684a42 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_arboricxServer 37f55fcdc76034f9073367b6d92c96841e8f608f2a80880dbe7ff11da97c88e6 be7195286228517d2789a3efb88b84ad3d5c97ec04426a31de66ec92a1114ae9 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_contractBasics fadba29322d6a70d71e5df057b1599ba493929684f232c2ddb96caebf21cbdd9 3bc358514f4ea4b65afca8a8e04f26bc616d88d98e41a8ced1cade820a3a2b35 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt demos_interactionTrees_httpServer c7e0e43222968585b0d3b04799f8a0c3a52d389cb607c10a30fe7c92618128d9 7bf87e78b8a173b29555509fdc24536674c1dac39b61fa5d089ae9958c7bad16 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt source_staging_experiment 0d2b58ab1f75f21368a5d5cabdfb3466e5d521bac9b18aa6c6ac07626ab9ebbc db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt source_staged_list_experiment 84036fb35637c1e1e7eb7c3c1b3481c9af9ad6d59fe62ba2fa8eec1d2f08fe0c c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt source_staged_conditional_experiment 5ae47c992ad2130d251d8767c1b1136b57c66cf64a95facce55f409b8279f8ec 43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt tests_stagingExperiment a3052beed1f6f999eea83325ec17808d0e38a186b5dfe0f0b9d6e47054397588 a28053bcf2363d097dca2b03d8f55614c90898333ca1e893078ef5015a18f687 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt tests_stagedConditionalRunner abcf3af01c00de58e1a7fec3fc30c389cca5fa00b46dc9a76fef32f5455b1e79 5ade29f516881ba475705960a54294300c1ae56a50296d1c6cec2a8f8478f159 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt patterns 7d1e465f4c0732d87e843a2c3f08085c81f060a97b651181c06fa4127e1cc4f5 703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt arboricx_handlers a8865fb943d106367d44f012c2af3ab34c1ae08930580ff6bcba4ebcd94f46f0 8a75e3d3c2fe9f4f535d1e220c90b5743ea4299890d73b334f502bf28f645dc0 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt arboricx_streaming_v2 6936d7eb649f2d8852106516fa325447e87ce910ccb0d9c2c53b67776b862a28 6a3c9f666801c3d29aaa71e22400a5726e137ea2fe28693217db6160a9d65db1 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt handler_artifacts 292e05ac1675c8ebb66a8dad691560c8e4f4f06a4b5a15c53387e86fabc531c6 0786c1fc251e88b12e081dc098a9bc7037d7589d251d6c84d594a9232b5ae117 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt handler_log 61ef9504bd657cfcc1e3f721e409955ecd3bb26c5319a96683da54b50ed69bbb ea8a3d3db064d9cd2a6a6ab0725f2db5741f65a2b3ec76349abe14dfcc915b77 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt handlers 80c5351291b073caf6851276a5112b363d55ffe8580411d92ae0c95864c22bcc 48f0ae7349309cd4f445115b52ca638f53c87e69be7f8a59c08038d102813712 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt io_v2 7e0ab1a91b3af07ec100d6e61766235b741100cd311777f1eb4a0b52c35f35b1 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt io_v2_artifacts a6115f68a707cd89159185ee46d1c4da18150d55719de139aee9db1533659555 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt io_v2_http 4f889d339ac05a2cf3a99aeab18b92b3f84ecbff58352da48533a0619e9430aa 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt io_v2_storage 69940ab4e4e8b639f4ca3cc0fd094a590fde9ffd237b66fff01fa60487a12ea5 e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +rebuilt io_v2_text 33c6215b6b779395a8c7933b2137416b1fbaba3682897b49796b061593fd4367 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 diff --git a/test/fixtures/support-library.arboricx b/test/fixtures/support-library.arboricx Binary files differ. diff --git a/test/fixtures/support-library.v1 b/test/fixtures/support-library.v1 @@ -1,86 +1,113 @@ -tricu.support-library-fixture.v3 -module socket socket lib/socket.tri 5014c5c41aa63d3b8a52e6cd89600b435505d93325f730b532c9eabaffd58421 +tricu.support-library-fixture.v5 +module socket socket lib/socket.tri b842a39f410e28c89c843ac8f1d3a8db7afa2e11a5ce12bf5c29193a6fb6b3c6 module intensional intensional lib/intensionalContracts.tri 4b9470311bb8984bf311371c6b1c056ac971e073c60be5a433a5826780c31d38 module binary binary lib/binary.tri 6dcf2f2087adef04a9b60330563c58c782ab51678622fafdbd7c7a6b14d20483 -module http http lib/http.tri d93ca3d550c9d177d9968bd65b0aad0aa4a31f20b8274cfadcaae778f20f1cb4 +module http http lib/http.tri 8338f32bdf08b7743933809b2acce42899d0020e83c95cd0b4d25b0a8b5c07df module arboricx.common arboricx_common lib/arboricx/common.tri a8bf1c965133ed0c2fd253c73c4a8cc5330f68de554b470c29f6e659a1174115 module arboricx.nodes arboricx_nodes lib/arboricx/nodes.tri 6c0e9998875942bb19c1f783ebffe61b23aa1eb578a46a874034444c4a9a3c49 -module arboricx.manifest arboricx_manifest lib/arboricx/manifest.tri 880ca4eefe955ed2ac38ca0a6ce8f04e41e44aeaf32c82b1bd4d8fb7342d9dba +module arboricx.manifest arboricx_manifest lib/arboricx/manifest.tri b1eccce1b5e4702466df56e1b48755797d6d148dcc38656f821faaa4d90e34da module arboricx arboricx lib/arboricx/arboricx.tri 0faaece47cc9b961a9d1e0ea0bca82c33453ad85b383c95152ed6db4fafd22b2 module arboricx.dispatch arboricx_dispatch lib/arboricx/dispatch.tri ba6facabd08809542b8cbc57b81cbafeb9b94cf7c771d313702685e36d06ff7f -module arboricx.server arboricx_server lib/arboricx/server.tri e577638282739414d645feea13edb8a30615af7f2da6838e7a7d9aa6ea475f98 +module arboricx.server arboricx_server lib/arboricx/server.tri fcd88bebeab55f69199836dfcdd8fe9007c5dcb7c7e951c7b4e7a4107ce85e44 module source.build-stable source_build_stable lib/source/build-stable.tri 240f25295a1fa23fe29faacb204a4bb5b0bda5b9b6d64185ee8135374e1c10c5 -module source.export-cache source_export_cache lib/source/export-cache.tri 378ca283a0ef856e6fde25e88ebd321b89eb348ef52232a5895f4240be0b5369 +module source.export-cache source_export_cache lib/source/export-cache.tri 5445a4f505c572889455435960a144663e224fa40a9c1cce1d9a96a8e3678fef module demos.equality demos_equality demos/equality.tri d14168490eeb3a4bab3c431d4ae936c51ead54e4e7155d9dded065442f2b9da0 module demos.size demos_size demos/size.tri b8d20a3ef0666e85684b34079e438ffbb1c7a7444b9c3ae6472cefd4fd21dcfd module demos.toSource demos_toSource demos/toSource.tri a1d060c00dae207ae61cdbfb15027a585cd650e748b83cf5b5c231f02fe5e1b7 module demos.patternMatching demos_patternMatching demos/patternMatching.tri 82199ce4c4ba4c014c2cd09bdcc0644732c2f6863c0eff2710f4156dcb50a1e5 module demos.levelOrderTraversal demos_levelOrderTraversal demos/levelOrderTraversal.tri 1278072b994e7b4f50b42f32ae7c94b09c9bea23a1c4aaee12ec2d14f2387cec module demos.contractEffects demos_contractEffects demos/contractEffects.tri 67252839e937e841c9fdc37a2518622a1da5bfd907d983494e92ee30b10427f4 -module demos.interactionTrees demos_interactionTrees demos/interactionTrees.tri 1d81c9b0665494ef2acdfc60a3b4c243518121dc5a7a35bdb4a31244eee5f076 -module demos.runArboricxBundle demos_runArboricxBundle demos/runArboricxBundle.tri dba78c003920b0e9d459c94cbf929b29c440ddb9f1d7bf79d917ca68279c5850 -module demos.interactionTrees.greet demos_interactionTrees_greet demos/interactionTrees/greet.tri ced74e1406629ffec2a9eeae58b87828f9507cae7d2f112a2bdd65ff74034f93 -module demos.interactionTrees.environment demos_interactionTrees_environment demos/interactionTrees/environment.tri abe18fb13b4fdafce7d6d7a011bf230b21d081ddbc7c9c842ede42a9b6db4594 -module demos.interactionTrees.state demos_interactionTrees_state demos/interactionTrees/state.tri aa0d8294256048c5caaa9e079a79ad729d0d77b182fa93d86e390bae008189c0 -module demos.interactionTrees.shout demos_interactionTrees_shout demos/interactionTrees/shout.tri 2beaabce9c2d9c6ee299d8ccbd3be62499782b9c10b6439c1027c8da7452a166 -module demos.interactionTrees.forkAwait demos_interactionTrees_forkAwait demos/interactionTrees/forkAwait.tri 4911d6c33e4c159fa6723fa9849681666724a9b7247ab982decf347dab5fb6e5 -module demos.interactionTrees.writeThenRead demos_interactionTrees_writeThenRead demos/interactionTrees/writeThenRead.tri 32496972f1c1b6b06eb6fb58b83e054d692ea9e5e4b601947c20c72c92a0751b -module demos.interactionTrees.safeRead demos_interactionTrees_safeRead demos/interactionTrees/safeRead.tri e9435aaeafcafcdc5ad778a1360328490306ce44395713d318edaa82119a3d10 -module demos.interactionTrees.yield demos_interactionTrees_yield demos/interactionTrees/yield.tri 17faf5560459e4c0ad2aa1724846d73614dd71b68198684b0613aba49866f0d2 -module demos.interactionTrees.getLineAsync demos_interactionTrees_getLineAsync demos/interactionTrees/getLineAsync.tri 7eaf0dbf9f3ed9b6e1098c1ee0d5953668d835070976596b8fc20e40c7d859d9 -module demos.interactionTrees.echoServer demos_interactionTrees_echoServer demos/interactionTrees/echoServer.tri 2c0562ca2688383132093fca8365ab15012cfab0b8d918e566902c82ed9eb42f -module demos.interactionTrees.arboricxServer demos_interactionTrees_arboricxServer demos/interactionTrees/arboricxServer.tri 26fd628be229f54f5376a0a8addc32fe79fc34e7a8941ecfe488a7323a47924e +module demos.interactionTrees demos_interactionTrees demos/interactionTrees.tri dd2f44109a0d14aa50e6a670067832720ff882f1ce7d8fac094d82280cbfb005 +module demos.runArboricxBundle demos_runArboricxBundle demos/runArboricxBundle.tri e014061b2107cb0d8d1d2366742c8f35640c9a7dd85b80fc78d4afc7813bf0cc +module demos.interactionTrees.greet demos_interactionTrees_greet demos/interactionTrees/greet.tri 40783b2c9c5750583151dda9d04b435d3d0d8a63b6f33cccfa94fd8ed958c14a +module demos.interactionTrees.environment demos_interactionTrees_environment demos/interactionTrees/environment.tri 4dcb43187b3cbd168fcb3ad5269be3e96257aa30773bca933e6cf37dfbdee066 +module demos.interactionTrees.state demos_interactionTrees_state demos/interactionTrees/state.tri 61799bafcab20f90faea5562f2c25c89bdab164571ac6073e4c029c1f86e5b66 +module demos.interactionTrees.shout demos_interactionTrees_shout demos/interactionTrees/shout.tri 060a440835999d443ef767ea5d4e9efe616a7186e1bb04947d6f9f80a21b0a4b +module demos.interactionTrees.forkAwait demos_interactionTrees_forkAwait demos/interactionTrees/forkAwait.tri 79c07186e9a3a36ce4c1de54b331c42dec6b95aa974156677c9336fd1c0849d7 +module demos.interactionTrees.writeThenRead demos_interactionTrees_writeThenRead demos/interactionTrees/writeThenRead.tri 65c006672139d98c27b5022a8641c73b6d0c9c29dd42f6cfe8b4820928664807 +module demos.interactionTrees.safeRead demos_interactionTrees_safeRead demos/interactionTrees/safeRead.tri d52075fd243ce733529740023181faa44c7c3022a9f28ee7ea2aaa078d95f7b2 +module demos.interactionTrees.yield demos_interactionTrees_yield demos/interactionTrees/yield.tri 50691f992e348fb47baccc119c3809f7e33fdcce5c801b23ed8635962f346655 +module demos.interactionTrees.getLineAsync demos_interactionTrees_getLineAsync demos/interactionTrees/getLineAsync.tri 2b971f5cfdfea12210f6ed1801a3660735e1da97d4d372cf751eb76879750991 +module demos.interactionTrees.echoServer demos_interactionTrees_echoServer demos/interactionTrees/echoServer.tri 834a2c8b9c9007364036bb22159ca0c5050c1536e87c4c40bf5573efee863e30 +module demos.interactionTrees.arboricxServer demos_interactionTrees_arboricxServer demos/interactionTrees/arboricxServer.tri bc09110023edc9a6aa7d947e51a09385f600d4fef66e8fd765808ad14f7ecc34 module demos.contractBasics demos_contractBasics demos/contractBasics.tri 6ffab139ecc27fd6b8ddbe6f8a134c91abba2b0491f5e28602dbe2dcd773928f -module demos.interactionTrees.httpServer demos_interactionTrees_httpServer demos/interactionTrees/httpServer.tri 664cc315f466f9a8ea5b39e1b7e16febd78e1e7b950f86b0cecc0ec6d82fefa7 +module demos.interactionTrees.httpServer demos_interactionTrees_httpServer demos/interactionTrees/httpServer.tri 386257391872e7b73befc2bcc3634706acf4acf0ff791bc60161179434c9a813 module source.staging-experiment source_staging_experiment lib/source/staging-experiment.tri 1993cce4a57c4225bf46d355be9436b48ceeffbcaddffd2f5e8184550f0fe7b4 module source.staged-list-experiment source_staged_list_experiment lib/source/staged-list-experiment.tri 179aa28c5e59df55dc1fe9abdf616b509e8f6cb485ba5e615d956aadfd1d4a54 module source.staged-conditional-experiment source_staged_conditional_experiment lib/source/staged-conditional-experiment.tri 989783bbdb71be5447803503f60925672ff29fad15804da31c8f7f83b8c402ad module tests.stagingExperiment tests_stagingExperiment test/stagingExperiment.tri 7a5a63f29ea34723bf72330c66f6c4b1b9a9a58988c5b8bd2d04ca9ab861fb03 module tests.stagedConditionalRunner tests_stagedConditionalRunner test/stagedConditionalRunner.tri 9f9f7c11a470595fa8d92933219bdb7653bc5ff647eff7afc352504e0c29eb66 -module patterns patterns lib/patterns.tri d61f2de7b3dce93c9b8914fb980224437d0ea4207787caab9098041cf5bfe896 +module patterns patterns lib/patterns.tri 64ba09f05b561ed1effe383dfad832e354f306a40675f478c7aecfaefd6d78d5 +module arboricx_handlers arboricx_handlers lib/arboricx/handlers.tri de6cdc548d44df7d835386a49116364e4ce8df8729801ca85dc975000e66af7f +module arboricx_streaming_v2 arboricx_streaming_v2 lib/arboricx/streaming-v2.tri 870df776ac7142fac5aad7f0169dfd08b2934f2bfa61ad3ae1c9b949f8f75088 +module handler_artifacts handler_artifacts lib/handlers/artifacts.tri ee1531f86d1faaef4ce7b1ca9151bd2e7b10c3fc0574d0db67ddfce6c012c4e3 +module handler_log handler_log lib/handlers/log.tri 4f64f9f4175b18c4e97caa24249d3bbe1261a57c6215de53c5ed4efe7fc546ab +module handlers handlers lib/handlers.tri 73cfe94509cdbc3c234bf4dabfac1796f5c6cf92565fcdd5a673a865bb8cd65b +module io_v2_artifacts io_v2_artifacts lib/io-v2/artifacts.tri 811886e206ba2912893ebba28afe4a4085b8838fc5a097b165ffb56637c93524 +module io_v2_http io_v2_http lib/io-v2/http-transport.tri b881a086294ff264567d758dd2c1ef7a3470faa909ec588e9f7bb8c4430f7bfc +module io io lib/io.tri c7c5c3133cf91206d8bdb13fd6972aed4bca3fae8b16308bafc6e25236c2cd0c relocated compiler-library patterns patterns lib/patterns.tri -compiler-fixture aa29656c146d2fc239d27da869498670c401cf344516314d06b7e5634c02d6f1 +added arboricx_handlers arboricx_handlers lib/arboricx/handlers.tri +added arboricx_streaming_v2 arboricx_streaming_v2 lib/arboricx/streaming-v2.tri +added handler_artifacts handler_artifacts lib/handlers/artifacts.tri +added handler_log handler_log lib/handlers/log.tri +added handlers handlers lib/handlers.tri +added io_v2_artifacts io_v2_artifacts lib/io-v2/artifacts.tri +added io_v2_http io_v2_http lib/io-v2/http-transport.tri +added io io lib/io.tri +compiler-provider io_v2 lib/io-v2.tri +compiler-provider io_v2_storage lib/io-v2/storage.tri +compiler-provider io_v2_text lib/io-v2/text.tri +compiler-fixture 11e9432733bb2b4579b65c4479b1b7dda5b5a56a4fd837fdd88f9a6ebc9093dd origin-metadata f7c9bdccb402d1b7ea41f7a63b2e78e18546d2129ad25faed7b3871663d91c0a origin-compiler 91b04273bf18429028e12a35126fc98fbad82911a2699a8937379573908ca78d origin-support f15f1e3740e5639e1fb08877ad2bf27af9758c74e468fae474d5ec4a71894b85 -rebuilt-support ee4907c11429ebd064efa8d414e634c813a4aed1d08360b337187d6bc3f55b44 -rebuilt socket 4d83b01a38a451728c81488e3ad4ddff15bf982cad2d743694203583184410cf 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt intensional 08343113b4851f67766002014148543a11ecc317abbe54918c0b6a8c143387db 7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt binary effa7bb25fbd71e7ee54d9ba5ba53e76b05aebb2eb7b50e021a12d9b2a3bda94 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt http f0afce4e54933c592e6987610423d4d192c7a4f22e7eac1a6250e9825e4a861e e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt arboricx_common b4990c9065292e67a8630930dd74228daa8d9eeeafa5b9dea3b3c853de2a2435 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt arboricx_nodes 4e0f65135ce3db43c32eb43e72d99e9cc42979f28fec0df0b3cfaae2006c9018 d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt arboricx_manifest dabe4637c448146d99055588096e1485678d8d7637c97f247b76e956bd0ec9d3 2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt arboricx 0c9361d459580541fa1962a9c43d747a3564378beb2266b52f765465518517ea 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt arboricx_dispatch 754c396fa5c584d78405957e577f037eda481c6abd2ad4634487355f5e135ed1 eda078ba018de68718b2b7aa2d790578ac079cf98c963a78f7fcae5178fd2c19 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt arboricx_server 17480c86cdec6a8463dfcb8c6003fb5e0061b4629ad5b58160b799a3d4e38f44 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt source_build_stable 509b96ab0d3f88813e3c7d989f150cb0da768f8b7789c421e8fbae68b394a68a 899ebd78231147cbf6b72a71298a72c7047d6fb1d8033fac88898e178613e6a7 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt source_export_cache 248a1ade9b70c2753b9526748766de589294a512a827d28409fc9778bb172a2b 7f29e52b9154b0ba6410836b055f8628f76bc51aa572efa2ef691f37ecaec077 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_equality 96db578fc577cc690363849555686cfb01a3157f17bcab42e0c2bbc5f1f06287 96bb649c2206620cd514820a8d8ca9dedb3cb3b713744bdff1ef500d9ec55ab0 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_size 3fb10da4b1085e3fd35a862ff6bb4ba93160b0997f0b3382f916bdee73b3c051 c628107a60003ede5cac28ee6f6f20e2fc74d01f699924733a1c54001ebee934 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_toSource c67281b62fc8831ec5f638f5416dc2564e9dd8d885caf4dd9d9ee7b1a89bad09 31148e782eb67ea2265ee9e0fd2a186ef2d2c24c1c8f38f98c0f3e8df955cb69 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_patternMatching 2c8624382d69872088eeb9950b33cf39b314678ec2089af9dd41a2ae61d8d972 6ce77cdea44f0bbb9ff4b8e59b07b48b79a2aff349f07b6e9c57a225a0ae1f79 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_levelOrderTraversal 7d04b0eb83f80e0b13d6ce8689d9a6f1372c426f586e6dd26d1649be4c0b54d4 a83874999378a9ff9b15ea99ed7f51c90a2dc48b0fd6b7d8980a29ffa088582c 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_contractEffects 6d3b99bae3a9bdc38c9f0d7c60c0ac368e1c0297d6dd44907c0ae009b38e0b26 8ef18c3d4c4a817d6afc966295a2783f4caaa3411e7ff4081f44fd8af6f7e872 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees 9788057da1c16087de608d1d163f00050714829fc75be3206a47707543a6dd48 806c9fa617d5c1397ee5573d22f9e03aa0bbde13f3100450591926566015a1a7 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_runArboricxBundle a5477a4ea34b086e5388d7db289247d9b6d8e516dc28f77097f8aa562e86bec2 8f0b54be481f1a6d123748689e5a4ee26b98f4f415d396056e62cc5c644ee608 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_greet 101c50431f4b0b2fe1a7e7537862f354fe9745ab173dc37a8cee380aae4950aa cc8b75304621d4747a6ba5951d63726776b311b423cb3cf4190cd61be43ea706 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_environment 503cc9b6a82d4cd5e4c2efedf13b0fafb2732504e8c5cfae6c0064da99695c14 73aa85f792fa244c5ef5f21cee568764b6b10974c606091b30c421cb333a44c9 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_state 00c9b3dfdbd7bda41c541916a87ddea59312860eab20a23212c1057998ada242 0a1a75ed6807a13528e087c23812c1b0b7f7cba916d7d697332e197d44beecde 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_shout 81da0d56bb7b6fc2ac9887212ca164e6bbedea975c4bc370b4fbfac0428e3698 f45d98fc0f3b982734ee2ade6f3cf93975e3101756be38cf6ca2dec2f2fc0ace 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_forkAwait 153e2308b47e2ba07de978ef2f8349e1ba244a41f7240c39637aeac9bd4ed30e d6b939af9c141227cf383f359ef4418702813e7426db43fc1dcd6a179ad5059b 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_writeThenRead a94fe429eac7a04ab564c549285c7b90ba8270dbb46678c35d664fc6d5e3e011 79fe12a0c2f6761514069b31e611ae97e7ca69e0c6ae6bd2d2606e4971c9667b 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_safeRead bcf0029f99d28bb6f8b61760c16186b8e2af6e9f3fc01560adb7b2177a039795 1bafa9ab78a08f434958517c79ef9e9d3518fcd4e91f4235b4a920bdc5cfc936 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_yield cc0a7d67fcdb08304f86254b2473767530e301cfeac15ad9ed6b598f83e7a41d f276979eefa248d6af204a721b8737c057eacc3a27d211286db3d92bd36441f7 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_getLineAsync 6112bf99aa32a2c933850e5499bfd5e24b096736971cde3bc26a40e592604378 3341c45590f6fc86ddff6720fb52fb4dc058a27e4f60d9e488a16fe1d5ada90d 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_echoServer a6ead80eb6a554523773eb767ece4421937b7b489443d3f75e6ca597d6c2eb02 b95fe82d3ef87fe7b3a1aa213dc4dd9ccf867dff464e540e7a762f1875517546 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_arboricxServer 622dbc0ae369cac9ac23d7ddd0249f84a406106aa4b8e1aa75bcdaf5310531d0 4dffa5e8e3c9b3ab58f9b8e8d21360f9d41267ff22baf569be3a34ce07836816 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_contractBasics fadba29322d6a70d71e5df057b1599ba493929684f232c2ddb96caebf21cbdd9 3bc358514f4ea4b65afca8a8e04f26bc616d88d98e41a8ced1cade820a3a2b35 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt demos_interactionTrees_httpServer de9ebdf71b8129c0d352692e89477e6a83ddff936b292d7dbac57f3241ca1b7f e8809f0967a2f4a6bac9004c28e316072ea17d924e6e5996f73961121d8e4de0 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt source_staging_experiment 0d2b58ab1f75f21368a5d5cabdfb3466e5d521bac9b18aa6c6ac07626ab9ebbc db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt source_staged_list_experiment 84036fb35637c1e1e7eb7c3c1b3481c9af9ad6d59fe62ba2fa8eec1d2f08fe0c c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt source_staged_conditional_experiment 5ae47c992ad2130d251d8767c1b1136b57c66cf64a95facce55f409b8279f8ec 43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt tests_stagingExperiment a3052beed1f6f999eea83325ec17808d0e38a186b5dfe0f0b9d6e47054397588 a28053bcf2363d097dca2b03d8f55614c90898333ca1e893078ef5015a18f687 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt tests_stagedConditionalRunner abcf3af01c00de58e1a7fec3fc30c389cca5fa00b46dc9a76fef32f5455b1e79 5ade29f516881ba475705960a54294300c1ae56a50296d1c6cec2a8f8478f159 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -rebuilt patterns 7d1e465f4c0732d87e843a2c3f08085c81f060a97b651181c06fa4127e1cc4f5 703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 -bundle ee4907c11429ebd064efa8d414e634c813a4aed1d08360b337187d6bc3f55b44 +rebuilt-support 10009626f1dfd1af3e84b1fd6f55cb30452e8ce8591515abe6a85eba471567e4 +rebuilt socket 352be66ab7939f6e6d59e2d58cbe463e7129a3251f463fe5a09558a1ded0820d f43fa8a321b5cd25954941270ea7f56d68115c77fac007f4e899dbc221f2cd38 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt intensional 85e8e60d58f9bfdc08a885b799958762318ad71a8a533adfc3cc12756a2ea964 7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt binary 8a455fd92e6924d11915a343260a8d8a23e100529e6e106bd6f7b7707139e274 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt http 85e6f4f5df9e72ca19d0d1ba874262b7cd277d1854a295790d707b184fc6f0c8 b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt arboricx_common ab9f4bc22ae9d9b3184f835b4e09d2d25352908eb2ef4949df689a4919286abd 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt arboricx_nodes 3fef291322a2a395037cb64902d1ee45ed87d6491b96021c7460465f82ba8a03 d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt arboricx_manifest a6c91275cec331c5650ecac671b2e51458965e956391d7c4cfbb1e0f302116bc 2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt arboricx 129af3b31050fe21ec49c20d730bda8e66c485974f642a62cd0e68e7b4229116 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt arboricx_dispatch 640f2055102b8bd0d090b3133085ae418626a0a234209e18ae9031029e340661 eda078ba018de68718b2b7aa2d790578ac079cf98c963a78f7fcae5178fd2c19 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt arboricx_server 9ba1f4a3231a3290ca4c02d19759079452f8ad0f3b7a05c83ac970c69732f067 a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt source_build_stable 7f6cd865f08be34441bac6cf2104f16c92aedfc830f2dbd1b6a7949995a20e32 d4c7521ff4bfea552124779a06ae3616e0dbbb3f00f711903b6f0f85902268df 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt source_export_cache c668902d1aa49692ca7120640fe23c818aeb5eb6ffffe7d14d68969ef24247b7 adb94daf05edaab172bf385632665cf03122ff71a288199243062f0a6c72b573 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_equality c2ef264dbd2b3a8a7a816a7f02dd0bcc49d656e864cd1e7f4407a9cbe5f3714a 96bb649c2206620cd514820a8d8ca9dedb3cb3b713744bdff1ef500d9ec55ab0 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_size f3d49770e7c5fd4f7fb8a64cd39847b64767fa0728807b723d73c8904d853a5a c628107a60003ede5cac28ee6f6f20e2fc74d01f699924733a1c54001ebee934 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_toSource 128f4b708e98b763e476ca0a15cc1ec19fe48828d4983b27bed2b425291cf4ae 31148e782eb67ea2265ee9e0fd2a186ef2d2c24c1c8f38f98c0f3e8df955cb69 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_patternMatching 95a7879b4bb29d550fc609a28be2b6692c839e1ee914863023712cf7f85528a5 6ce77cdea44f0bbb9ff4b8e59b07b48b79a2aff349f07b6e9c57a225a0ae1f79 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_levelOrderTraversal 7b8f03f27d0d1e8cdb5785f0ad1e95193bfbde1d99f1871231c2063dbe24c980 a83874999378a9ff9b15ea99ed7f51c90a2dc48b0fd6b7d8980a29ffa088582c 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_contractEffects 3664fac09988616665d74e8e165939314f80b73599b35a71b6cd71da08a08507 8ef18c3d4c4a817d6afc966295a2783f4caaa3411e7ff4081f44fd8af6f7e872 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees f7c112907745012e5244761291c21de517601cac0730df993da60a5e0b7a3eb9 d660fd8fd3543687f0c9efb715a8e16b1e7cc194e127ee6741be3079e7d9eb7f 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_runArboricxBundle 95ae14408bef2349c85cc1fe5b143941be6627f016ed705e308164364002be52 cf6c20277f27afe0677b012920a10bf32a12929b8cfaa310620cf33143cd4ce1 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_greet 714653de3602edc54b63fb968c7018f04b98fdb855e94518ccf21633f44c83cf bde116bd78a59e12c6add24a004d0941b82e89bba5d3282c570559d930dd0d17 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_environment 4cf1c7be044eee27c0022807a5536459dd7b435926ce96843dd440291948c9e5 e645ead315c7c3c16a0d4b43d27a89ebc0d54495a4ca69e82c3b583de224f43c 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_state 411901249b261a22264679126102f6ac5f617287aa003e0d959bb2f5ecef40d2 763bc6ea4cb0bcab7b9b92aed6947d9220a2823a6bd9885d3a013a98b92c86ca 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_shout 110f033e6ccf404cf6ba5adbe33a4ff9f8b1a5e502e210a99526c04810e704f4 bc75e4f1698d736428575fb190f188b185f2e668db10f62fe7886866c579da84 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_forkAwait a83e6293e0fc9ea89650594faa82e6fb6e750d0e6b317cbac2c214061307826d 2512a36cfe8af8f4166ba25c9b88d5c398e1535483370fe3c3e1379786d5caae 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_writeThenRead d0858025bd6ce38aa6feb191a4b1097a8df5ca5ad0d1b088c21e2dbcf79e5496 4ea3868438079b5153746f618b98c190a8f9be5bc0a0035298f65612380adb9c 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_safeRead 681624d8c8a83f1e9bf69d6b0883a73407b5a388134b0b8b9b2bec9d742fac53 107bb9e32aeabf317a50c555fd2c82fda73c54654ee95d4c185717c7d0a06673 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_yield 0eb597f338a547f60e983c0c1e06d6296e502600fdb1176a0f104c0cd0478030 a605f6ba37ea84fedea127c610453bec9dfabf045e0834eb63742e4ed9267f9d 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_getLineAsync f940f69927eae0b5ec426fd213fe6c761cd4526b4e6260b7673737a62c95f4ba 54f2765098bd2580e08d2b8390899cbe49e956fbf037e7e4697e867d3001e464 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_echoServer d18a331c393614ed218452682560af34496e1fc39e4f27ed46c4ce5dbe81d4bb 75aee7d8167931634b5bd5566aa0c06a45be0f5a3c5c16aa86d2b1a75d684a42 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_arboricxServer f36b19e1bebb4f5617f840bc12fd59d3e853cfeeb8c3ae8df48fa38eaaee3220 191f14d90b99d21abcf13026c642c4e5179281f828eb509fdef0ecaf8485243a 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_contractBasics a4cee90b3ba8d1d872c3d2da6e8924b12e0b56756d7f08e269a1f9a96140aea2 3bc358514f4ea4b65afca8a8e04f26bc616d88d98e41a8ced1cade820a3a2b35 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt demos_interactionTrees_httpServer fd897d80f1eee807ea9c2bb17f9f74b75043c7e8cbff3d982f5f983b300a8b40 7bf87e78b8a173b29555509fdc24536674c1dac39b61fa5d089ae9958c7bad16 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt source_staging_experiment 11c1393dde53422e7cb5e201507cd6cb44e1021590fac92b1029d5e1e04eafae db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt source_staged_list_experiment 2fa7f7ffcf59d70767c293e0f6199e8769b6540525f43efd674fdaf087e5873a c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt source_staged_conditional_experiment 5d755b7b8fa91c39198c419729e397ee2db5c269b87beaf9f2fca5a40ae766ae 43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt tests_stagingExperiment ab7434b48b9c7e5d690787841f8e83b68cb59468064f170581797ac93826c8b3 a28053bcf2363d097dca2b03d8f55614c90898333ca1e893078ef5015a18f687 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt tests_stagedConditionalRunner 92f6067355cc3341386cc2b32c3144b3637e353a4fe975b3cfb02291ec6705e3 5ade29f516881ba475705960a54294300c1ae56a50296d1c6cec2a8f8478f159 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt patterns 27a82746684d606eeb9b90f69e89981ea6bd589a5ed130ab2f25e424b90ad9a0 703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt arboricx_handlers e4b04f56866e47e5304580a73fdbf6dd6229cecd37a139c73911746252244dcc ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt arboricx_streaming_v2 03933bc5c701092038c9c2f717a2825ce57f969eb3550cba28e0d82db38a89d1 7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt handler_artifacts 9e9e9ad74ef20ee432cba6beb5d870af9115523bf6eadfb03ceea7514ec8484e fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt handler_log 88c5038b061b27c46603f7441133d67c6e21451f249a6015233bd1a4dde31b0b 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt handlers bce8dbaff65ded636ca85f0a04f0cc7f5ed78e59c717990697239888dab5c1bc 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt io_v2_artifacts f3d1847ca412cffd8ab9840906cf7e28bbc64a7b613d7d592be3de6717012b8b 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt io_v2_http 92cd4388ee95bd4452c9f146077f97ed5528c475e421e1d032a537181a682f15 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +rebuilt io 0347b92f6468a34669965354771c59ab6b9e71eadb58f0c1c8ad4ebe126d8287 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +bundle 10009626f1dfd1af3e84b1fd6f55cb30452e8ce8591515abe6a85eba471567e4 diff --git a/test/fixtures/support-rebuilt.arboricx b/test/fixtures/support-rebuilt.arboricx Binary files differ. diff --git a/test/fixtures/support-receipts/arboricx.receipt b/test/fixtures/support-receipts/arboricx.receipt @@ -1,14 +1,14 @@ arboricx.build-receipt.v1 source 0faaece47cc9b961a9d1e0ea0bca82c33453ad85b383c95152ed6db4fafd22b2 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local binary 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc effa7bb25fbd71e7ee54d9ba5ba53e76b05aebb2eb7b50e021a12d9b2a3bda94 -local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 3c2b355c055b99e9555b359ab75d92f95deb68ab17b893ecc79245b3f80d4b0f -local arboricx_common 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 b4990c9065292e67a8630930dd74228daa8d9eeeafa5b9dea3b3c853de2a2435 -local arboricx_manifest 2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa dabe4637c448146d99055588096e1485678d8d7637c97f247b76e956bd0ec9d3 -local arboricx_nodes d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23 4e0f65135ce3db43c32eb43e72d99e9cc42979f28fec0df0b3cfaae2006c9018 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local binary 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc 8a455fd92e6924d11915a343260a8d8a23e100529e6e106bd6f7b7707139e274 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 +local arboricx_common 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 ab9f4bc22ae9d9b3184f835b4e09d2d25352908eb2ef4949df689a4919286abd +local arboricx_manifest 2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa a6c91275cec331c5650ecac671b2e51458965e956391d7c4cfbb1e0f302116bc +local arboricx_nodes d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23 3fef291322a2a395037cb64902d1ee45ed87d6491b96021c7460465f82ba8a03 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/arboricx_common.receipt b/test/fixtures/support-receipts/arboricx_common.receipt @@ -1,11 +1,11 @@ arboricx.build-receipt.v1 source a8bf1c965133ed0c2fd253c73c4a8cc5330f68de554b470c29f6e659a1174115 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local binary 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc effa7bb25fbd71e7ee54d9ba5ba53e76b05aebb2eb7b50e021a12d9b2a3bda94 -local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 3c2b355c055b99e9555b359ab75d92f95deb68ab17b893ecc79245b3f80d4b0f +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local binary 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc 8a455fd92e6924d11915a343260a8d8a23e100529e6e106bd6f7b7707139e274 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/arboricx_dispatch.receipt b/test/fixtures/support-receipts/arboricx_dispatch.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source ba6facabd08809542b8cbc57b81cbafeb9b94cf7c771d313702685e36d06ff7f -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output eda078ba018de68718b2b7aa2d790578ac079cf98c963a78f7fcae5178fd2c19 -local arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 0c9361d459580541fa1962a9c43d747a3564378beb2266b52f765465518517ea +local arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 129af3b31050fe21ec49c20d730bda8e66c485974f642a62cd0e68e7b4229116 pin arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 readArboricxDecoded b59eefef9ea509792ac66aac8f9e770af09d58370dc34f3ffa2fcf72d1a4b673 pin arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 readArboricxBundle 78e9ef86e33bfa86a8eee8395cbb4cba9359c7408623c9fae23a1ad10f9b0ae7 pin arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 readArboricxExecutableByName a90d75ca259e957c031492e93de8d365a7cac3f54470c09d75484bf6391fa55f diff --git a/test/fixtures/support-receipts/arboricx_handlers.manifest b/test/fixtures/support-receipts/arboricx_handlers.manifest @@ -0,0 +1,6 @@ +arboricx.module-manifest.v1 +export request arboricx.indexed-tree.v1 f430e7f24a57d747a74070bf07061f3d87b62771e2a4cdc4364e2d4a2be11ee1 arboricx.abi.tree.v1 +export packageLookupV2 arboricx.indexed-tree.v1 f3af424161bd70a46f7848fad4d56e2f15e6ba93847a9575c6e25a9b1f8e0193 arboricx.abi.tree.v1 +export stdoutLogV2 arboricx.indexed-tree.v1 a75bf5bec72aca440ddeb04237f8f3525be5c47e3501ef7a54d4105803249373 arboricx.abi.tree.v1 +export writeLogV2 arboricx.indexed-tree.v1 f9ea00f45d2c41925580dc7ba40bdf83be58b109c2ce0c03fdd1b38ac6dd39e4 arboricx.abi.tree.v1 +export routerV2 arboricx.indexed-tree.v1 bc649518d542283be5012b08e2a418d63785c8caf3914d128acc68037db65327 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/arboricx_handlers.receipt b/test/fixtures/support-receipts/arboricx_handlers.receipt @@ -0,0 +1,365 @@ +arboricx.build-receipt.v1 +source de6cdc548d44df7d835386a49116364e4ce8df8729801ca85dc975000e66af7f +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f +output ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 85e6f4f5df9e72ca19d0d1ba874262b7cd277d1854a295790d707b184fc6f0c8 +local conversions 78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e ccaba9486c6d41f3ce3872b0e45477e65ab94e14cb3aef114fb6b7b0d7819811 +local arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d 9ba1f4a3231a3290ca4c02d19759079452f8ad0f3b7a05c83ac970c69732f067 +local handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bce8dbaff65ded636ca85f0a04f0cc7f5ed78e59c717990697239888dab5c1bc +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 fda4bfec1f3837c0e51c458ccb092419842b7189f850e58861760f1fc21b8b8f +local handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 88c5038b061b27c46603f7441133d67c6e21451f249a6015233bd1a4dde31b0b +local handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 9e9e9ad74ef20ee432cba6beb5d870af9115523bf6eadfb03ceea7514ec8484e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxHeaderBytes e62a642ffd71dff46c4c03641e08f13490469e84bbd0815403b577a0f4916f90 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxBodyBytes 40a6c6220b30ead48df9abc0cc7f1b6202197de18d97434061fc1d4ccd6d1292 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 crlfcrlf f77e4d0100d99afad2fa15decbeb38aae4ed8c3135d5ea9c2e5a44537194a871 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrases 0bcd79d7b0f770431eee50a6be0f938b32458a41f7f57ba9b2edb56baf607e97 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 lookupStatusPhrase ad3e569281eedcff04104de5e3da2170f57046bdb8528857b92e9d4dcb78f97d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrase 67d1a009c420ebc91c9ba7533209c485bb9bbd4c7249f4ac48be6b5e598f809d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusLine ce0da21e44a60d7d7c1b09c43142df1dcbfa38b5a6830ffcd3d0a7704cae67af +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerLine 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpAppendResponseHeader 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 buildResponse 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyLength 005020fe1d72f85901d6d1911c7865245972a2be15b070ed07415082b6a68ecc +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 response 3a029a2a767699d24f1cb4bc4346dd3ccce2940ef4395eeb09b1f8fe89d44613 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 emptyResponse 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 okResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 textResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 jsonResponse 2abfe36d0337304c8c78bcb87e2f132db6d6acf2385e10cd3e1a92336c447acb +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 htmlResponse 5aaa86ca299dc2ce72f64a5abfc4e07ca0e949bd751669ed8171d4995152725b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 createdResponse 22d380835f4efd58877c625e8de7f08dd5dc1b5b6b30fdf6553e533116166bff +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 notFoundResponse 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 badRequestResponse 1b533e6b41f50c40a6d0e61d2e5661ede925fd4ad473f016b47186f4e9522097 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 errorResponse 7b6e86847a366c23c27ad86f767947425bf04a987b5f48451f8d714e102c22b5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerEndState e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeadersOnly 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headersOnly f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 responseForMethod 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineError 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpScanLine 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineReply 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBadRequest e11a0cdf150e1784e00dcebff8906c033c6695ebc3e7c5716b72715dde8695a6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 readLineBytes 7bed187a6276169e2eac95cdbb4f2fc53f3625dc6b183b4b9e56e0c5957c34b6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpNonSpace? f5f2f658784d8ded1ec828967e19324ffd4fd2438e5a6ebcad0a6c07f9feb908 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpThreeWordsReply 5a71c4b351610b15374829ba12e3f6a5f793760c5927d414c944d1060df60161 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseThreeWords 1f04df81381567601348b1442947ed8792f3dd0424eb9393b3c147f33493d94a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseRequestLine ace457a20b9f1ee3e757a622f3ba03b191873e4d4835cc7ebdfd947332561769 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 toLowerAsciiByte 4a9d74221452385910ba4d4b87cd60d31efe31132b056f32777ebeec6b457d9e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTokenByte? d0c894a224efa6fb17498ee6791f4598a26e38419b51d099af9e80035e3cf85e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpOws? 14e51851f694336c78a7a25b88289c6687b8e0a19af8b220c3a027d5270522c2 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpFieldByte? b7b24f9ed6ee56a602e896fde3aee4c07fde3dee849b608fa451128d5ce4d196 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTrimOws 7ab13fefc873a02011c645a56f65ca119806b1f7ab740e6bf56d1fb02ec7067c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeaderLineReply 962626d5659cce124775e1b8d98482db74433856dc2701767286121681223c56 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpParseHeaderLines 382f455876fd6d1fb704e3d96a1d33bb569211d3e6de1ccb604337266c0ba744 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseHeaders a0fe7e14eacc4024e5812aa0443a8345954ff7db342218db523258cee87aa39b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyTooLarge a35fe61177a0ecce71ff585252f6df5f81e9201ad24e1c238e034c5500ef4d60 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthOverflow 4a4e6626a22d3ee2d2d435940b99e86e0589945c66b3cf185f4fa64ff32b3e76 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthDigits 5c312192ada0032aadbe847e6c6164a786deb66a8ae0e0adc6af140e4791da6b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseContentLengthValue 330f1d204f021be9370b7fb01e3f11703a860532ce46f6f276e0f45d4f59356c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpSelectLength 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 contentLength 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 bodyReadState 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 takeBodyBytes 303837e8c16c2b3e76f4076ceb28e22ead58735b4796d72ad185fa22c01aa97e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpRecvBucket 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 recvChunkMax4096 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validMethod? ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validVersion? 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validTarget? 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validateRequest 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 routerMethod c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c +pin conversions 78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d pathJoin 829f14f27a485a4b6c46b43e7738745a7de75055f29d17a185eca46eba15f7da +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d objectDir 8a7f9fc2cdb834eab42af178576aa9208658f3050dc6358a18647170ef34713c +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d hashShard 1f3d07a8d7cd99b856642914d152f7ebea5755263480690b2922b6514a5e587b +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleObjectPath d640ad60309411f5ec38b4909f9f5ec21059cfa5f7e3a16d6f56d36af62f7624 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleBit? 65884ff45b474eace191b13b20f765a5c5d1ecc40e537c80ce0497af1e5bc636 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHexByte? 9c57b3fe1c129ee921946078e79855a5a753f1d08fd7c6576fa1c7ecb02d127e +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashBytes ee0ee6b5285b7dbaf28282c4c90df8dafbe7b6d8e9bd6a8ce32669ecb5b3c77e +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashFuel8 2288aaff65180213c9f2a0320465102cd4b7e23d08c4ceca63f66362f05b1c1b +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashFuel64 befaa254fea1150958c6834ad270fc6b558eae5df5f671998f66819db17e91b4 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashScan 231d71dd3ca456c9119d220caed59625f1e9e55b49905e79b2e42275444edc62 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashValid? 65a921dc150476b2c6cc91719ee5f6856e84fb5f87930407b63c6f606ecdeded +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashC 9e1ea088465773f583d1d6cb59b4b5bf1e97975bc8acba92b2d688f98fcea754 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d stripPrefix c5567e6c98ffe51fd7d9502efcec133c5665602b692bf197fa7557a3ced86705 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashPrefix 04ef2d1fce822846b3aae9b70795fdebcdcc8be1a841f35b04a16ac23b8a73e1 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundlePath 8c0b567fd42473cb50c21667e711d0a1c2a703088e9f419326186488174a83c0 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d healthPath c62b994110ddef7b756d7778ae2c1c8f598685733ea33dcc7a8870c14813f244 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleContentType 193f98476a4848c8a543af754a2c98ab842032db4c0721a22e21a607975d112a +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d htmlLandingPage 13e9d1c402f9008f18b36172d573fc1585e6303633b739ba10a265d3239037dd +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleResponse 2c2508dc1a9907a5d3907a297e15449f674c1df649d948208405e71ac66f00f8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packagePrefix 4863ca9af271ee8d26de1c3670e7f7aadd47f9f4de8b2a838ead8903a3604425 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageAlphaNum? 6fc7db89f4e88912c897ef3f334cdd598fe1f62e14a61e7be183827f4c269cd8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageByte? 540d4151153d268fd54cb13c31f237a23f95e37c1b1adc2c6bff96d87f9546f8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageVersion c315d31ca807d567a2031b0d268989b971bbdc3b970cacc79b095d6114cd00fb +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageName cbd8d3ecf8cbc5d455d4a7cd07d099984a5064f7b1dbb822c4e1d631f361c2d8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageTarget 523ebe158e800b99ef8d34b5d81335c67c6dc9dece752fe7140a8c79ff9c49de +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d pure dab5acd986e6ef2cfd473cdfda2291978bdd05d53d654568aee54a44035cb126 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bind b4edbddf65d1f40c6c6c069ddb37c1c763b47a1a57a303ac1ee00ae75594d2a0 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d request 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkedRequest 1c58f5c798102b7dbfe5ef63251298c3cdb35c7e47f2f24dc0d72db7581e9b51 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkWith 911a2190a2a0bf023ad23badeea4756df03a3f7f02139581b19bc0f2011011c6 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkRequest 583ccf6db95ddb1be9e6f5afc1ca98ae6b410945d3d1643084d896f1737c16a5 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d handle d17093c9654d57c134b6d0f00580c1710e552f34d7583b9a1e438eb073a22661 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d forward 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d label f4325f96264534084186bae09455f2a8060568d39e45d57692fa896ad4bf3138 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d fail ab8e873ce69d0305b9e50f7d0fd0c02503c09d352f4fe6f1a80b6f471724e7d8 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d liftIO 47be69da2dca13f8c6a3e3c9107cd843158b3bf576fbde3d156b1daf927dba64 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runPure 79d3a676f798e1691119cd442473d17a86e00bc26a56843f4715f70ba5886c37 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIO 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d collect 28b9fa70f3c2243be4e459f9bbc356893c2dcd2bcabdbd9106f09025cbd96546 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d view 989468627f11a7589c918e56fb01da459397726dda8c07c7808c30f1eb0f613f +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d diagnostic 0a05b217b6ff9aef546f4f6d8c4f699f6ad8fa5bb1a73fafdeebd60609f2a912 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d scopeV2 0df99b6d6d7b1f1559b83c2980e1e25c72a1c43b32b758c295a35ad23ce60f0a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d spawnV2 8043d72533c0d1b1ca655ea4ecd12bbafd5e89402b9cab9ef3071201a4fbf8b2 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIOV2 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 readBytes a7ed7716ca188b830f261d06119648a929bafb8d896293234339c94bbc357d8c +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 hashBytes 3ea7a52e3e4dfc2941b5332d7fbb0c2b66fb72d36954351c00b263bb55cca6bc +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 publishBytes 2906ae1e2de27fd8b1fe2c71f3bfeb81ea5467d622a4f47b009179135452ec95 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 readTree f8a6d56fe51dc20d86f616c26fda2999d948e2232fc11c0553c3161afe700a69 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 publishTree b196ac6adb2ebeb4f51d9b6fe678b6c94e986b1d07690caba73af6a8844212d9 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 parentPath 5f34aa8a2a12d814575223fd7f5b077a692c1d2272a7572aae8b21bb9334189f +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 emit a6a2f37fe7003899b0524b86764b82e30844089d77821df7b3224c25024c23bb +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 info 68623a558ecdeae85e94bbc33fc7d7df4c22facc3e1c0866b59cc6034228b4c3 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 warn 06eff0e9177ff92b11a22fafd1595926b632c9e1733883b029f25a6beba0c4f1 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 withContext 48b6e19d4cdebb789b69332bf7414fee3c188978fedf233a929e9b5a6bedb810 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 withSink 6b5180a9838e9b0bdb808828e874385f058bcd256ce4817dfdcabe4cc33dae7a +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 capture 9ad5e65ce08131170cab6963b1fc52ba942c7b025371ab415ac7b721db84aa6f +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 discard 49d4e552a7ef1e30eaf2bb3d7bd4447a5d8508e04af35c9509c6e936bc954cee +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 load a328711380ff8a816961cbb63745d9056234c1d3895abbc5553a01e26cf4f731 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 loadFrom 31e7a145e15592233983722218d1cc06d9e05503597c7bc79c0fd119b2418d46 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 publish b31d4d11fbb7e86e3873f9173194941eec340748a3c3551ce77d4fbbeb429494 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 publishTo bff1bb880d4b22d21e52a0b22f3cf3ee7da5840e4518208adae2cae094d7f0dd +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 withFilesystemV2 76e916cab925bb60e90111c8ab500e0df8fe9a30dee40f7149508665ffb782fa +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 withFilesystemForV2 9c0765155fc6a0493ca3c4a604a7d3c12c1d4d4897045f2e6d5d1234fe017799 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 prepareFilesystemV2 ac24c7d1b00cb8a3e4b38cd67c9ca389514c84092b49a45d168c1c7ce7193b29 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 publishStreamV2 c9a57e8267ad1a3644b50f663cce0144546c531376ee6d02f765acba450ad41a diff --git a/test/fixtures/support-receipts/arboricx_manifest.receipt b/test/fixtures/support-receipts/arboricx_manifest.receipt @@ -1,11 +1,11 @@ arboricx.build-receipt.v1 -source 880ca4eefe955ed2ac38ca0a6ce8f04e41e44aeaf32c82b1bd4d8fb7342d9dba -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source b1eccce1b5e4702466df56e1b48755797d6d148dcc38656f821faaa4d90e34da +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 2a8a2b591dfc4192b8ded6e2ea75e94f6ff41183636f6024b6d5eb590ec8d2aa -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local binary 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc effa7bb25fbd71e7ee54d9ba5ba53e76b05aebb2eb7b50e021a12d9b2a3bda94 -local arboricx_common 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 b4990c9065292e67a8630930dd74228daa8d9eeeafa5b9dea3b3c853de2a2435 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local binary 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc 8a455fd92e6924d11915a343260a8d8a23e100529e6e106bd6f7b7707139e274 +local arboricx_common 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 ab9f4bc22ae9d9b3184f835b4e09d2d25352908eb2ef4949df689a4919286abd pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/arboricx_nodes.receipt b/test/fixtures/support-receipts/arboricx_nodes.receipt @@ -1,12 +1,12 @@ arboricx.build-receipt.v1 source 6c0e9998875942bb19c1f783ebffe61b23aa1eb578a46a874034444c4a9a3c49 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output d158e4ebe9fd6cfb752023bbe031614b190b9eab79e9680875a1fc55dc481a23 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local binary 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc effa7bb25fbd71e7ee54d9ba5ba53e76b05aebb2eb7b50e021a12d9b2a3bda94 -local arboricx_common 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 b4990c9065292e67a8630930dd74228daa8d9eeeafa5b9dea3b3c853de2a2435 -local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 3c2b355c055b99e9555b359ab75d92f95deb68ab17b893ecc79245b3f80d4b0f +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local binary 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc 8a455fd92e6924d11915a343260a8d8a23e100529e6e106bd6f7b7707139e274 +local arboricx_common 0d779adef29b51ee875bf2f46ec03bef30ec48d200e7e05c9e20bd90b28ac4a4 ab9f4bc22ae9d9b3184f835b4e09d2d25352908eb2ef4949df689a4919286abd +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/arboricx_server.manifest b/test/fixtures/support-receipts/arboricx_server.manifest @@ -3,33 +3,24 @@ export pathJoin arboricx.indexed-tree.v1 829f14f27a485a4b6c46b43e7738745a7de7505 export objectDir arboricx.indexed-tree.v1 8a7f9fc2cdb834eab42af178576aa9208658f3050dc6358a18647170ef34713c arboricx.abi.tree.v1 export hashShard arboricx.indexed-tree.v1 1f3d07a8d7cd99b856642914d152f7ebea5755263480690b2922b6514a5e587b arboricx.abi.tree.v1 export bundleObjectPath arboricx.indexed-tree.v1 d640ad60309411f5ec38b4909f9f5ec21059cfa5f7e3a16d6f56d36af62f7624 arboricx.abi.tree.v1 -export bundleHexByte? arboricx.indexed-tree.v1 926b2a24871bf471ca1dab8e33cda8f7d9be5c050055fe60ce08c485c65a1280 arboricx.abi.tree.v1 -export bundleHashBytes arboricx.indexed-tree.v1 60ad71a82050e5e3a27f0c02942d2cace652ff0c1388cabc0c2296e840101f7b arboricx.abi.tree.v1 -export bundleHashValid? arboricx.indexed-tree.v1 e7131e7ad5f7733a858e2838a156c6967b0cf653fffc78e75d7c6f9bd11aa084 arboricx.abi.tree.v1 -export bundleHashC arboricx.indexed-tree.v1 e1b0b6e4a5f17c8361bf89023def1725ce4110db10d07163fcb94b8138678cd5 arboricx.abi.tree.v1 -export ensureDir arboricx.indexed-tree.v1 1148afd31ccf07b90bf34fd26fee1f8758039feaf00e33b48983468df65e3641 arboricx.abi.tree.v1 -export ensureStore arboricx.indexed-tree.v1 21c3a92eb520185088809ea569920b48cacb0736d96910c597707f79d73f99fe arboricx.abi.tree.v1 -export putBundle arboricx.indexed-tree.v1 058651f00287b672eddcb8eacbf388817e3205266ff6afedf26ca4e8e1e448dc arboricx.abi.tree.v1 -export getBundleByHash arboricx.indexed-tree.v1 1bc08de4eb1740eb87ec9304f616aa2d57ddf833115a2400dc95c31dd215d54f arboricx.abi.tree.v1 +export bundleBit? arboricx.indexed-tree.v1 65884ff45b474eace191b13b20f765a5c5d1ecc40e537c80ce0497af1e5bc636 arboricx.abi.tree.v1 +export bundleHexByte? arboricx.indexed-tree.v1 9c57b3fe1c129ee921946078e79855a5a753f1d08fd7c6576fa1c7ecb02d127e arboricx.abi.tree.v1 +export bundleHashBytes arboricx.indexed-tree.v1 ee0ee6b5285b7dbaf28282c4c90df8dafbe7b6d8e9bd6a8ce32669ecb5b3c77e arboricx.abi.tree.v1 +export bundleHashFuel8 arboricx.indexed-tree.v1 2288aaff65180213c9f2a0320465102cd4b7e23d08c4ceca63f66362f05b1c1b arboricx.abi.tree.v1 +export bundleHashFuel64 arboricx.indexed-tree.v1 befaa254fea1150958c6834ad270fc6b558eae5df5f671998f66819db17e91b4 arboricx.abi.tree.v1 +export bundleHashScan arboricx.indexed-tree.v1 231d71dd3ca456c9119d220caed59625f1e9e55b49905e79b2e42275444edc62 arboricx.abi.tree.v1 +export bundleHashValid? arboricx.indexed-tree.v1 65a921dc150476b2c6cc91719ee5f6856e84fb5f87930407b63c6f606ecdeded arboricx.abi.tree.v1 +export bundleHashC arboricx.indexed-tree.v1 9e1ea088465773f583d1d6cb59b4b5bf1e97975bc8acba92b2d688f98fcea754 arboricx.abi.tree.v1 export stripPrefix arboricx.indexed-tree.v1 c5567e6c98ffe51fd7d9502efcec133c5665602b692bf197fa7557a3ced86705 arboricx.abi.tree.v1 export bundleHashPrefix arboricx.indexed-tree.v1 04ef2d1fce822846b3aae9b70795fdebcdcc8be1a841f35b04a16ac23b8a73e1 arboricx.abi.tree.v1 export bundlePath arboricx.indexed-tree.v1 8c0b567fd42473cb50c21667e711d0a1c2a703088e9f419326186488174a83c0 arboricx.abi.tree.v1 export healthPath arboricx.indexed-tree.v1 c62b994110ddef7b756d7778ae2c1c8f598685733ea33dcc7a8870c14813f244 arboricx.abi.tree.v1 export bundleContentType arboricx.indexed-tree.v1 193f98476a4848c8a543af754a2c98ab842032db4c0721a22e21a607975d112a arboricx.abi.tree.v1 export htmlLandingPage arboricx.indexed-tree.v1 13e9d1c402f9008f18b36172d573fc1585e6303633b739ba10a265d3239037dd arboricx.abi.tree.v1 -export bundleResponse arboricx.indexed-tree.v1 67a86a9a02376a8bb0f5d63fb2c4ede8d595cd084fc69bbf16498443b8390520 arboricx.abi.tree.v1 -export serveBundleHash arboricx.indexed-tree.v1 8e34251a1e18f485a6f293702bc003bff765519eea94c28fa0a8b303dc1f1766 arboricx.abi.tree.v1 -export healthRoute arboricx.indexed-tree.v1 5b5215ffafcfdf3a8f5c7a46808eff960c10c0262732dc7ee01718e61ea3da1d arboricx.abi.tree.v1 -export putBundleRoute arboricx.indexed-tree.v1 8933ea81efa2fa62fac97df494840bb3f05ad8e7a725ee9d46e9f7142050e52d arboricx.abi.tree.v1 -export getBundleRoute arboricx.indexed-tree.v1 dbceb10c93169cc67774a1d989f37474a13124d154c20d998af4c936f4e5f36a arboricx.abi.tree.v1 +export bundleResponse arboricx.indexed-tree.v1 2c2508dc1a9907a5d3907a297e15449f674c1df649d948208405e71ac66f00f8 arboricx.abi.tree.v1 export packagePrefix arboricx.indexed-tree.v1 4863ca9af271ee8d26de1c3670e7f7aadd47f9f4de8b2a838ead8903a3604425 arboricx.abi.tree.v1 export packageAlphaNum? arboricx.indexed-tree.v1 6fc7db89f4e88912c897ef3f334cdd598fe1f62e14a61e7be183827f4c269cd8 arboricx.abi.tree.v1 export packageByte? arboricx.indexed-tree.v1 540d4151153d268fd54cb13c31f237a23f95e37c1b1adc2c6bff96d87f9546f8 arboricx.abi.tree.v1 export packageVersion arboricx.indexed-tree.v1 c315d31ca807d567a2031b0d268989b971bbdc3b970cacc79b095d6114cd00fb arboricx.abi.tree.v1 export packageName arboricx.indexed-tree.v1 cbd8d3ecf8cbc5d455d4a7cd07d099984a5064f7b1dbb822c4e1d631f361c2d8 arboricx.abi.tree.v1 export packageTarget arboricx.indexed-tree.v1 523ebe158e800b99ef8d34b5d81335c67c6dc9dece752fe7140a8c79ff9c49de arboricx.abi.tree.v1 -export servePackage arboricx.indexed-tree.v1 83a674b06af81df4bcb491637917db4e6b280c1b94d0ac2cd17661cb039640be arboricx.abi.tree.v1 -export getRegistryRoute arboricx.indexed-tree.v1 3fd2d0b3febd4424b550b2e09867575257bb9dc5dc3744ba4edc4fc6b10eb4d0 arboricx.abi.tree.v1 -export arboricxRouter arboricx.indexed-tree.v1 2076924505c32731ca5d2b3283782e085c4838ff19f98d29510d28885670002c arboricx.abi.tree.v1 -export arboricxHandler arboricx.indexed-tree.v1 598db219bafacbc1b49d0262505edef0457197e7369d39614f64a7211552cbb0 arboricx.abi.tree.v1 -export arboricxServer arboricx.indexed-tree.v1 b7a040cb073aad8579c4c479af8c2f8c23f277e94a003e4ea177cbd55ce011cc arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/arboricx_server.receipt b/test/fixtures/support-receipts/arboricx_server.receipt @@ -1,13 +1,11 @@ arboricx.build-receipt.v1 -source e577638282739414d645feea13edb8a30615af7f2da6838e7a7d9aa6ea475f98 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source fcd88bebeab55f69199836dfcdd8fe9007c5dcb7c7e951c7b4e7a4107ce85e44 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -local http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 f0afce4e54933c592e6987610423d4d192c7a4f22e7eac1a6250e9825e4a861e -local socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 4d83b01a38a451728c81488e3ad4ddff15bf982cad2d743694203583184410cf -local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 3c2b355c055b99e9555b359ab75d92f95deb68ab17b893ecc79245b3f80d4b0f +output a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 85e6f4f5df9e72ca19d0d1ba874262b7cd277d1854a295790d707b184fc6f0c8 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 @@ -172,176 +170,63 @@ pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 byt pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 maxHeaderBytes e62a642ffd71dff46c4c03641e08f13490469e84bbd0815403b577a0f4916f90 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 maxBodyBytes 40a6c6220b30ead48df9abc0cc7f1b6202197de18d97434061fc1d4ccd6d1292 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 crlfcrlf f77e4d0100d99afad2fa15decbeb38aae4ed8c3135d5ea9c2e5a44537194a871 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 statusPhrases 0bcd79d7b0f770431eee50a6be0f938b32458a41f7f57ba9b2edb56baf607e97 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 lookupStatusPhrase ad3e569281eedcff04104de5e3da2170f57046bdb8528857b92e9d4dcb78f97d -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 statusPhrase 67d1a009c420ebc91c9ba7533209c485bb9bbd4c7249f4ac48be6b5e598f809d -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 statusLine ce0da21e44a60d7d7c1b09c43142df1dcbfa38b5a6830ffcd3d0a7704cae67af -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 headerLine 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpAppendResponseHeader 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 buildResponse 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 response 6d37791d690e6d36d3ca8b5541391b6b629c1b90b5d3da2a4101610840b657b8 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 emptyResponse 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 okResponse 1d039ba7479cfd48d6a4ed6fb194f16697c63e4c34338343f12a0e35c6c6c6c8 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 textResponse 1d039ba7479cfd48d6a4ed6fb194f16697c63e4c34338343f12a0e35c6c6c6c8 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 jsonResponse efa5ea017979655879b4be69b5a08251035283f4678e5ffbd5e9fd933d7e590c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 htmlResponse 9a1fa84e4e9147107b3c2b36756638f9e847163fba25e502951eb64de1d6a5fc -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 createdResponse 8a3469d3a2cce957b1571d4f84881d13f41d1e58db82d8d5f33bd3baaf961235 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 notFoundResponse 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 badRequestResponse 674fe3821cd49c8a73c6f6f3abfe3004ca33b393de879969e3b768bf6abfbc6a -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 errorResponse a83633de4defc0b4a70603c6b5d0fdf57be6567db629ab9347a7cbd7fd13cb9f -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 headerEndState e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHeadersOnly 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 headersOnly f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 responseForMethod 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpRecvUntil 6dea4b3cdf235c3a453e51049cc4fcb3ee3a0216e0dbe0a6987b43a33355db5f -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 recvUntilMax aab00d1f8d9ec906fb402fbfa3ef5cb9ab301bec0d6b5d609c9fb4e17ccf430c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 recvUntil 3ab70212ad49e37e8c6a29e393a9e5790d3bda04b5b56fca1ced5932bca79a43 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 recvHeaders 6c9519ee8c6c45ef98745ad799df591dc46acbf8eaa6219659bf03de38fbfeff -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpLineError 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpScanLine 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpLineReply 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpBadRequest e11a0cdf150e1784e00dcebff8906c033c6695ebc3e7c5716b72715dde8695a6 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 readLineBytes 7bed187a6276169e2eac95cdbb4f2fc53f3625dc6b183b4b9e56e0c5957c34b6 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpNonSpace? f5f2f658784d8ded1ec828967e19324ffd4fd2438e5a6ebcad0a6c07f9feb908 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpThreeWordsReply 5a71c4b351610b15374829ba12e3f6a5f793760c5927d414c944d1060df60161 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 parseThreeWords 1f04df81381567601348b1442947ed8792f3dd0424eb9393b3c147f33493d94a -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 parseRequestLine ace457a20b9f1ee3e757a622f3ba03b191873e4d4835cc7ebdfd947332561769 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 toLowerAsciiByte 4a9d74221452385910ba4d4b87cd60d31efe31132b056f32777ebeec6b457d9e -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpTokenByte? d0c894a224efa6fb17498ee6791f4598a26e38419b51d099af9e80035e3cf85e -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpOws? 14e51851f694336c78a7a25b88289c6687b8e0a19af8b220c3a027d5270522c2 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpFieldByte? b7b24f9ed6ee56a602e896fde3aee4c07fde3dee849b608fa451128d5ce4d196 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpTrimOws 7ab13fefc873a02011c645a56f65ca119806b1f7ab740e6bf56d1fb02ec7067c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHeaderLineReply 962626d5659cce124775e1b8d98482db74433856dc2701767286121681223c56 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpParseHeaderLines 382f455876fd6d1fb704e3d96a1d33bb569211d3e6de1ccb604337266c0ba744 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 parseHeaders a0fe7e14eacc4024e5812aa0443a8345954ff7db342218db523258cee87aa39b -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpBodyTooLarge a35fe61177a0ecce71ff585252f6df5f81e9201ad24e1c238e034c5500ef4d60 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpLengthOverflow 4a4e6626a22d3ee2d2d435940b99e86e0589945c66b3cf185f4fa64ff32b3e76 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpLengthDigits 5c312192ada0032aadbe847e6c6164a786deb66a8ae0e0adc6af140e4791da6b -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 parseContentLengthValue 330f1d204f021be9370b7fb01e3f11703a860532ce46f6f276e0f45d4f59356c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpSelectLength 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 contentLength 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 bodyReadState 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 takeBodyBytes 6996dc93fdff652211977ed68ef5db211c3df7a1db86c958035db653c1b228a7 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpRecvBucket 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 recvChunkMax4096 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpReadBodyState 8d477bdab5f8db48905a5f6b8c19bc69654a0bad367d2fe1680303e7a28b53c7 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 readBodyMore 6d75303c7d3fdda892b2b276dbb96c01019b1c25b8153f695e6c799c03c40355 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 readBodyExact dda235ff519ff0ac146c9e170d18f218a80e829c8cc034d2e7825e6c23d1506a -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 readBody b5280a0a7555b439dd5ce42bf592a165da51f95398087ea43774d6b45510fddc -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 validMethod? ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 validVersion? 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 validTarget? 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 validateRequest 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 routerMethod c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 respondAndClose 467c1b022f83b6fd3fb3e9fb7ccc6cd78d15c2b9b8a936987088c7c46639aebf -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpRespond 4c15cdb6bd52d2a638c93cc3719374cf4eb91e53eac55c58195b1ac56bdbe540 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandleBody 55672ac8616e621c775081a59ad851ff50fcb247dd11d31cf773875fb7fa1c55 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandleHeaders d21b4ed9b21aa5af60b775ad96959bb15648a5b19171691d555f3c71f8f90d6b -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandleRequest 939d41afd142bc74e6a4ee1b0cbde8cfa5d06c87c29ebd9ce68aaf161d98e586 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandleReceived e40bd55458aa83659301d11b22764e03d38567791a23a472d33fb981974260e6 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandlerIO ba715371563139d668ec9ec2afc8bb27407d4a6795cd449dd87afa11a9e698f4 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpPureRouter 373f2de1bdb7cb038c91aa75737a3abfc95c2655ac4bc1d4a89b6739f73c0849 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandler 777fe83b3bf5ebb3b36d9a10d202ce381e1c488beb6667f70dd9f58961e4352a -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 socket d0962df2eb672cddf52b8ddf4a76f643a76280b4905c97495efb4af859265516 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocket 4b5cf72937d3fc20f6b23496986227b44a1dcaa1d18a17d74da2a3dcb01d9b6b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 bindSocket 007c736fd19010f007eef45cc4d649b5de7b0f77d042a6c17e4eaea6ccfd007f -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 listen 960de6adb3cce7b002e915e47f405294bf3687d724faff2a6ca5f8926e7d7d93 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 accept d5af3a6c23868f6c35b58488dee362395d7e1d54b94915432f2c1f221b5e7be3 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 connect 5ae01fe00d755ca83680ab01b27ddbaa9f4713b3832de18d714d07c7c14a4317 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 recv acaf941bcc0b17a09d151ed9cf70c23ea3f33a897ce99dbdd6ef5e354bc5514b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 send 641b2ed091cb6eddc1dfd51038f21ac0afaf8e6122440f40b709ab63c8c9d953 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 getSocketName 9ed5573f5c861837f24b973c84a456d5a47081cd000ebf71e2d1ce0995463468 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSocket d2dfa18c4c469383d96d14ff08646db8b345f1bad52d8772b9b59e2b5225315b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onBindSocket ae3e99d9ebed09010981a11eb788cb572cef63a4ca625d86f7196fde97527530 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onListen 5a6660028564f14fbe8bd68e0574ae31c4eb254064169438957864d62bf16868 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onAccept ea248480f5681c96d255c317a0783dc98acd028c8ec07d324ac2ae02a764374d -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onConnect 5ba2194bee14d9fad3c282147bebd0c03bc55b76c518bb684350c7901bc296c9 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onRecv d5f6bf951f02d8415354bbab1e720d1d87e3d39caf26dc6a0b9801cd03b42ed2 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSend 112d48a3a067379fcfaa56e894ffe0aaf02e3f3fc44bc2fe8472f0d49a47aeca -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onGetSocketName 99d9c9dff1637310ff2c9a001cd718a36cccc03a18d84a3294f2ab7874af1f76 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSocket_ 3c394ed1369781ee2254ded5956687c27971c495548b6278e5e62efd757370a6 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onBindSocket_ 7c91b3aabf7daffd7da4e525db14be852b1e3b874212d7da444467d6de0585ed -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onListen_ e9f2c57f616bed580f21e58bd01b327d4615680d6209574cfeb6a7da85758025 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onAccept_ e4b93ffb876803acb6700e33127436cc53f348efbd0d422ebca09a3371b1614e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onConnect_ e0baf23e45038091b6c6ccea70941dd3fdf96128f94a595ae0f320f095979025 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onRecv_ dc391a9c916186a16974abf368dd23d0bdb3fced7b5f3cffe8a655aa41f230ea -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSend_ c746325d45b9a88f5519a5399bc784bd7b3eb5ba53a1aa37f6030e64b0d109bd -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onGetSocketName_ 31d562c8271942cd7eadefdde5bac73abbcdbcbf63ffc4e9d83b68ce39929988 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocket_ e10c012b9860dd7629b768fcb75126eff626b2f80ea1a0095ee2efcaa68b304e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocketOnError 21d850f11f110d1bdb7d4cabd44913473f997337ded0df8b79e3f94034c0bdf0 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 listenSocket 8a248c717b8216370b40ddc355851282362dd2bc130df5adf5f8f0aa2a3d9dcc -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 withAccepted 7838f3c8b28ede95756dd9ca6283db1a3519bb95e0b19ab3bcfcabedabb1c900 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 withAccepted_ b33b77bfe9094c3938bbc8684d668da222d2b461cf020fe927f848c1bb4c26a7 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveOnce 2e39131bc2e390022e72a22235cde46ecf0a0b54a46618719ccaab660acbc306 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveForkingOnce 1194e71d76856c2a519ef62c0197d4399fa30ece80ff756eb1419f57197aa927 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveForever ac87d6be311b022bce24cbbb1f75b5e80db4f672c04d695afb800e81cd1dc95e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 connectTo 1c2bee16cdfaa4046a2f173d0210f26c0eb48f356dafa00a84cec4e34f553bcb +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxHeaderBytes e62a642ffd71dff46c4c03641e08f13490469e84bbd0815403b577a0f4916f90 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxBodyBytes 40a6c6220b30ead48df9abc0cc7f1b6202197de18d97434061fc1d4ccd6d1292 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 crlfcrlf f77e4d0100d99afad2fa15decbeb38aae4ed8c3135d5ea9c2e5a44537194a871 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrases 0bcd79d7b0f770431eee50a6be0f938b32458a41f7f57ba9b2edb56baf607e97 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 lookupStatusPhrase ad3e569281eedcff04104de5e3da2170f57046bdb8528857b92e9d4dcb78f97d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrase 67d1a009c420ebc91c9ba7533209c485bb9bbd4c7249f4ac48be6b5e598f809d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusLine ce0da21e44a60d7d7c1b09c43142df1dcbfa38b5a6830ffcd3d0a7704cae67af +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerLine 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpAppendResponseHeader 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 buildResponse 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyLength 005020fe1d72f85901d6d1911c7865245972a2be15b070ed07415082b6a68ecc +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 response 3a029a2a767699d24f1cb4bc4346dd3ccce2940ef4395eeb09b1f8fe89d44613 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 emptyResponse 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 okResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 textResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 jsonResponse 2abfe36d0337304c8c78bcb87e2f132db6d6acf2385e10cd3e1a92336c447acb +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 htmlResponse 5aaa86ca299dc2ce72f64a5abfc4e07ca0e949bd751669ed8171d4995152725b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 createdResponse 22d380835f4efd58877c625e8de7f08dd5dc1b5b6b30fdf6553e533116166bff +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 notFoundResponse 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 badRequestResponse 1b533e6b41f50c40a6d0e61d2e5661ede925fd4ad473f016b47186f4e9522097 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 errorResponse 7b6e86847a366c23c27ad86f767947425bf04a987b5f48451f8d714e102c22b5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerEndState e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeadersOnly 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headersOnly f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 responseForMethod 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineError 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpScanLine 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineReply 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBadRequest e11a0cdf150e1784e00dcebff8906c033c6695ebc3e7c5716b72715dde8695a6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 readLineBytes 7bed187a6276169e2eac95cdbb4f2fc53f3625dc6b183b4b9e56e0c5957c34b6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpNonSpace? f5f2f658784d8ded1ec828967e19324ffd4fd2438e5a6ebcad0a6c07f9feb908 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpThreeWordsReply 5a71c4b351610b15374829ba12e3f6a5f793760c5927d414c944d1060df60161 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseThreeWords 1f04df81381567601348b1442947ed8792f3dd0424eb9393b3c147f33493d94a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseRequestLine ace457a20b9f1ee3e757a622f3ba03b191873e4d4835cc7ebdfd947332561769 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 toLowerAsciiByte 4a9d74221452385910ba4d4b87cd60d31efe31132b056f32777ebeec6b457d9e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTokenByte? d0c894a224efa6fb17498ee6791f4598a26e38419b51d099af9e80035e3cf85e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpOws? 14e51851f694336c78a7a25b88289c6687b8e0a19af8b220c3a027d5270522c2 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpFieldByte? b7b24f9ed6ee56a602e896fde3aee4c07fde3dee849b608fa451128d5ce4d196 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTrimOws 7ab13fefc873a02011c645a56f65ca119806b1f7ab740e6bf56d1fb02ec7067c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeaderLineReply 962626d5659cce124775e1b8d98482db74433856dc2701767286121681223c56 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpParseHeaderLines 382f455876fd6d1fb704e3d96a1d33bb569211d3e6de1ccb604337266c0ba744 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseHeaders a0fe7e14eacc4024e5812aa0443a8345954ff7db342218db523258cee87aa39b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyTooLarge a35fe61177a0ecce71ff585252f6df5f81e9201ad24e1c238e034c5500ef4d60 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthOverflow 4a4e6626a22d3ee2d2d435940b99e86e0589945c66b3cf185f4fa64ff32b3e76 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthDigits 5c312192ada0032aadbe847e6c6164a786deb66a8ae0e0adc6af140e4791da6b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseContentLengthValue 330f1d204f021be9370b7fb01e3f11703a860532ce46f6f276e0f45d4f59356c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpSelectLength 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 contentLength 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 bodyReadState 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 takeBodyBytes 303837e8c16c2b3e76f4076ceb28e22ead58735b4796d72ad185fa22c01aa97e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpRecvBucket 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 recvChunkMax4096 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validMethod? ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validVersion? 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validTarget? 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validateRequest 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 routerMethod c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db compare 25d460f10cf8893795a8a5eac6a951b40459ef90e35f6945f72c3f53868f94f5 pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db equal 07e8af1f8ef6366cab90420cf1ec97a15b434ba4ee3966af235579a05935180e pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lt 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 diff --git a/test/fixtures/support-receipts/arboricx_streaming_v2.manifest b/test/fixtures/support-receipts/arboricx_streaming_v2.manifest @@ -0,0 +1,5 @@ +arboricx.module-manifest.v1 +export pump arboricx.indexed-tree.v1 4fa48f8af8ecff423e13ef5a6256673b93a44deb1b70250e16ab873943c6d422 arboricx.abi.tree.v1 +export connection arboricx.indexed-tree.v1 78c39e29f3aeeda76afa2c892e0e4ede951a01602685f3acdf3fd634de9aa402 arboricx.abi.tree.v1 +export serve arboricx.indexed-tree.v1 f379736c9deb17fd019c713611abb5757e64eb316e93cb3345c500956b43a911 arboricx.abi.tree.v1 +export serveCount arboricx.indexed-tree.v1 b7732c4b0614999ba1a5465de00ca177520ccb1394b6db74edc30fa2fa6fbec5 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/arboricx_streaming_v2.receipt b/test/fixtures/support-receipts/arboricx_streaming_v2.receipt @@ -0,0 +1,373 @@ +arboricx.build-receipt.v1 +source 870df776ac7142fac5aad7f0169dfd08b2934f2bfa61ad3ae1c9b949f8f75088 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f +output 7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 92cd4388ee95bd4452c9f146077f97ed5528c475e421e1d032a537181a682f15 +local io_v2_artifacts 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 f3d1847ca412cffd8ab9840906cf7e28bbc64a7b613d7d592be3de6717012b8b +local http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 85e6f4f5df9e72ca19d0d1ba874262b7cd277d1854a295790d707b184fc6f0c8 +local handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bce8dbaff65ded636ca85f0a04f0cc7f5ed78e59c717990697239888dab5c1bc +local handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 9e9e9ad74ef20ee432cba6beb5d870af9115523bf6eadfb03ceea7514ec8484e +local handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 88c5038b061b27c46603f7441133d67c6e21451f249a6015233bd1a4dde31b0b +local arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c e4b04f56866e47e5304580a73fdbf6dd6229cecd37a139c73911746252244dcc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db compare 25d460f10cf8893795a8a5eac6a951b40459ef90e35f6945f72c3f53868f94f5 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db equal 07e8af1f8ef6366cab90420cf1ec97a15b434ba4ee3966af235579a05935180e +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lt 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lte a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gt d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gte a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db inRange 62a3f72adbb9030425f0e63e43568c0eae069d7293763fab3a83123edef50aaa +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db minimum f515c4e49514b4631ca183a2a2dfcfce46d5ef8a8338a09496ce9a0ca2f6502c +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db maximum dfcf25622f229c0ae04441aae5884a38d0ba046ec512c8ac1fde56290657938b +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db zero 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db odd 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db even 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db successor 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db predecessor dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db add 066aebbf5d3b48d5e6c9c35b5c59e9cc5172f47d09899684475738eafb4fbe4a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db subtract a5a9212b766933b7c8f07b3f98f7d9d789d0ae45e1301042da712fefa3dd0a3a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db multiply 320a41f19797fbb9a05d6615b53be543b7acec4e3e535ad81c8f1e5f16db3de0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db divMod 068932c208504ea9a547086ba761d6975ee3173d495b80addc056999b7fc557f +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db quotient bdfe626a727affba89d4c11e489eca0086f4c42facfb4ee9f248e69a47abed8a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db remainder 80f8dd39e11423480ce2f5f5edde99104824bab147a88a2c331c8f643a0e6817 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db power 3b59e090c4ea2d7966c2bc7bf21c9ed67940c1c7a41f01f6e04db93a69ca54a1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db decimal 644d54084c9734a5b58ebc7d9bb973eea48890665db1617679db9bd1c8bd68b0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db ascii e0df024c0168229497859a5e770f7e36594d9d266c3933102d1fa1997d4b3225 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db digit eed46692dadce061198cf3cafd6c00bbc5561d9fb637706d16bde41552a1b9bd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db letter ddca67e3c882ecee1239775aecf0d31b8ce136f86e6a6eea70e5d8b5979b0b30 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lowerHex 3b752ef4b4b89ef102dc4c43110ee8522922dbaadf77495713fc465d9e718eb4 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db printable 9c88d7eaba58c0771da373821f7223c298bea7697647f03f6b1a4d922ff8a6e5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 recvHeaders fae92c42be9ddf04847aa2f6e1fcc670841d313d09bc648328095e68f3a6c7fb +pin io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 readBody cebdd476321f81788fd529ed96f2bf4d52bd7ca14d05eacc050b76ea2e552c03 +pin io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 respond 534ffe1c7a933f21a35fefb17f6f7bd78d98c3fe45adf5b19edb68e661242952 +pin io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 handle dba8f168d9e14aefd8b4c10bdc08fa796fc706b3d9e492bd299382d0abdea3b8 +pin io_v2_artifacts 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 write 3958b3975aea86f8e939f2d59d1f674f98a10db0feb7d45ee075c105fa68394f +pin io_v2_artifacts 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 publish 3a922deae98e27749857ff024d79a321093627d6d36656ec5048725073d1fa5d +pin io_v2_artifacts 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 publishWith 5524cf19c7627fdad48f0a28b8c89646b468429bb12cb8db5f82636a86f07124 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxHeaderBytes e62a642ffd71dff46c4c03641e08f13490469e84bbd0815403b577a0f4916f90 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxBodyBytes 40a6c6220b30ead48df9abc0cc7f1b6202197de18d97434061fc1d4ccd6d1292 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 crlfcrlf f77e4d0100d99afad2fa15decbeb38aae4ed8c3135d5ea9c2e5a44537194a871 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrases 0bcd79d7b0f770431eee50a6be0f938b32458a41f7f57ba9b2edb56baf607e97 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 lookupStatusPhrase ad3e569281eedcff04104de5e3da2170f57046bdb8528857b92e9d4dcb78f97d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrase 67d1a009c420ebc91c9ba7533209c485bb9bbd4c7249f4ac48be6b5e598f809d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusLine ce0da21e44a60d7d7c1b09c43142df1dcbfa38b5a6830ffcd3d0a7704cae67af +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerLine 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpAppendResponseHeader 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 buildResponse 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyLength 005020fe1d72f85901d6d1911c7865245972a2be15b070ed07415082b6a68ecc +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 response 3a029a2a767699d24f1cb4bc4346dd3ccce2940ef4395eeb09b1f8fe89d44613 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 emptyResponse 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 okResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 textResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 jsonResponse 2abfe36d0337304c8c78bcb87e2f132db6d6acf2385e10cd3e1a92336c447acb +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 htmlResponse 5aaa86ca299dc2ce72f64a5abfc4e07ca0e949bd751669ed8171d4995152725b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 createdResponse 22d380835f4efd58877c625e8de7f08dd5dc1b5b6b30fdf6553e533116166bff +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 notFoundResponse 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 badRequestResponse 1b533e6b41f50c40a6d0e61d2e5661ede925fd4ad473f016b47186f4e9522097 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 errorResponse 7b6e86847a366c23c27ad86f767947425bf04a987b5f48451f8d714e102c22b5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerEndState e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeadersOnly 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headersOnly f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 responseForMethod 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineError 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpScanLine 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineReply 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBadRequest e11a0cdf150e1784e00dcebff8906c033c6695ebc3e7c5716b72715dde8695a6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 readLineBytes 7bed187a6276169e2eac95cdbb4f2fc53f3625dc6b183b4b9e56e0c5957c34b6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpNonSpace? f5f2f658784d8ded1ec828967e19324ffd4fd2438e5a6ebcad0a6c07f9feb908 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpThreeWordsReply 5a71c4b351610b15374829ba12e3f6a5f793760c5927d414c944d1060df60161 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseThreeWords 1f04df81381567601348b1442947ed8792f3dd0424eb9393b3c147f33493d94a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseRequestLine ace457a20b9f1ee3e757a622f3ba03b191873e4d4835cc7ebdfd947332561769 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 toLowerAsciiByte 4a9d74221452385910ba4d4b87cd60d31efe31132b056f32777ebeec6b457d9e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTokenByte? d0c894a224efa6fb17498ee6791f4598a26e38419b51d099af9e80035e3cf85e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpOws? 14e51851f694336c78a7a25b88289c6687b8e0a19af8b220c3a027d5270522c2 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpFieldByte? b7b24f9ed6ee56a602e896fde3aee4c07fde3dee849b608fa451128d5ce4d196 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTrimOws 7ab13fefc873a02011c645a56f65ca119806b1f7ab740e6bf56d1fb02ec7067c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeaderLineReply 962626d5659cce124775e1b8d98482db74433856dc2701767286121681223c56 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpParseHeaderLines 382f455876fd6d1fb704e3d96a1d33bb569211d3e6de1ccb604337266c0ba744 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseHeaders a0fe7e14eacc4024e5812aa0443a8345954ff7db342218db523258cee87aa39b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyTooLarge a35fe61177a0ecce71ff585252f6df5f81e9201ad24e1c238e034c5500ef4d60 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthOverflow 4a4e6626a22d3ee2d2d435940b99e86e0589945c66b3cf185f4fa64ff32b3e76 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthDigits 5c312192ada0032aadbe847e6c6164a786deb66a8ae0e0adc6af140e4791da6b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseContentLengthValue 330f1d204f021be9370b7fb01e3f11703a860532ce46f6f276e0f45d4f59356c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpSelectLength 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 contentLength 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 bodyReadState 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 takeBodyBytes 303837e8c16c2b3e76f4076ceb28e22ead58735b4796d72ad185fa22c01aa97e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpRecvBucket 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 recvChunkMax4096 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validMethod? ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validVersion? 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validTarget? 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validateRequest 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 routerMethod c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d pure dab5acd986e6ef2cfd473cdfda2291978bdd05d53d654568aee54a44035cb126 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bind b4edbddf65d1f40c6c6c069ddb37c1c763b47a1a57a303ac1ee00ae75594d2a0 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d request 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkedRequest 1c58f5c798102b7dbfe5ef63251298c3cdb35c7e47f2f24dc0d72db7581e9b51 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkWith 911a2190a2a0bf023ad23badeea4756df03a3f7f02139581b19bc0f2011011c6 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkRequest 583ccf6db95ddb1be9e6f5afc1ca98ae6b410945d3d1643084d896f1737c16a5 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d handle d17093c9654d57c134b6d0f00580c1710e552f34d7583b9a1e438eb073a22661 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d forward 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d label f4325f96264534084186bae09455f2a8060568d39e45d57692fa896ad4bf3138 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d fail ab8e873ce69d0305b9e50f7d0fd0c02503c09d352f4fe6f1a80b6f471724e7d8 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d liftIO 47be69da2dca13f8c6a3e3c9107cd843158b3bf576fbde3d156b1daf927dba64 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runPure 79d3a676f798e1691119cd442473d17a86e00bc26a56843f4715f70ba5886c37 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIO 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d collect 28b9fa70f3c2243be4e459f9bbc356893c2dcd2bcabdbd9106f09025cbd96546 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d view 989468627f11a7589c918e56fb01da459397726dda8c07c7808c30f1eb0f613f +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d diagnostic 0a05b217b6ff9aef546f4f6d8c4f699f6ad8fa5bb1a73fafdeebd60609f2a912 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d scopeV2 0df99b6d6d7b1f1559b83c2980e1e25c72a1c43b32b758c295a35ad23ce60f0a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d spawnV2 8043d72533c0d1b1ca655ea4ecd12bbafd5e89402b9cab9ef3071201a4fbf8b2 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIOV2 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 load a328711380ff8a816961cbb63745d9056234c1d3895abbc5553a01e26cf4f731 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 loadFrom 31e7a145e15592233983722218d1cc06d9e05503597c7bc79c0fd119b2418d46 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 publish b31d4d11fbb7e86e3873f9173194941eec340748a3c3551ce77d4fbbeb429494 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 publishTo bff1bb880d4b22d21e52a0b22f3cf3ee7da5840e4518208adae2cae094d7f0dd +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 withFilesystemV2 76e916cab925bb60e90111c8ab500e0df8fe9a30dee40f7149508665ffb782fa +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 withFilesystemForV2 9c0765155fc6a0493ca3c4a604a7d3c12c1d4d4897045f2e6d5d1234fe017799 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 prepareFilesystemV2 ac24c7d1b00cb8a3e4b38cd67c9ca389514c84092b49a45d168c1c7ce7193b29 +pin handler_artifacts fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 publishStreamV2 c9a57e8267ad1a3644b50f663cce0144546c531376ee6d02f765acba450ad41a +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 emit a6a2f37fe7003899b0524b86764b82e30844089d77821df7b3224c25024c23bb +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 info 68623a558ecdeae85e94bbc33fc7d7df4c22facc3e1c0866b59cc6034228b4c3 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 warn 06eff0e9177ff92b11a22fafd1595926b632c9e1733883b029f25a6beba0c4f1 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 withContext 48b6e19d4cdebb789b69332bf7414fee3c188978fedf233a929e9b5a6bedb810 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 withSink 6b5180a9838e9b0bdb808828e874385f058bcd256ce4817dfdcabe4cc33dae7a +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 capture 9ad5e65ce08131170cab6963b1fc52ba942c7b025371ab415ac7b721db84aa6f +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 discard 49d4e552a7ef1e30eaf2bb3d7bd4447a5d8508e04af35c9509c6e936bc954cee +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c request f430e7f24a57d747a74070bf07061f3d87b62771e2a4cdc4364e2d4a2be11ee1 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c packageLookupV2 f3af424161bd70a46f7848fad4d56e2f15e6ba93847a9575c6e25a9b1f8e0193 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c stdoutLogV2 a75bf5bec72aca440ddeb04237f8f3525be5c47e3501ef7a54d4105803249373 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c writeLogV2 f9ea00f45d2c41925580dc7ba40bdf83be58b109c2ce0c03fdd1b38ac6dd39e4 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c routerV2 bc649518d542283be5012b08e2a418d63785c8caf3914d128acc68037db65327 diff --git a/test/fixtures/support-receipts/binary.receipt b/test/fixtures/support-receipts/binary.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source 6dcf2f2087adef04a9b60330563c58c782ab51678622fafdbd7c7a6b14d20483 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc -local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 2b3ccb358f6e262bbb6f9a28c17c9c96a8e3a07a162d4069a8226c9208be03f3 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/demos_contractBasics.receipt b/test/fixtures/support-receipts/demos_contractBasics.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source 6ffab139ecc27fd6b8ddbe6f8a134c91abba2b0491f5e28602dbe2dcd773928f -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 3bc358514f4ea4b65afca8a8e04f26bc616d88d98e41a8ced1cade820a3a2b35 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/demos_contractEffects.receipt b/test/fixtures/support-receipts/demos_contractEffects.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source 67252839e937e841c9fdc37a2518622a1da5bfd907d983494e92ee30b10427f4 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 8ef18c3d4c4a817d6afc966295a2783f4caaa3411e7ff4081f44fd8af6f7e872 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/demos_equality.receipt b/test/fixtures/support-receipts/demos_equality.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source d14168490eeb3a4bab3c431d4ae936c51ead54e4e7155d9dded065442f2b9da0 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 96bb649c2206620cd514820a8d8ca9dedb3cb3b713744bdff1ef500d9ec55ab0 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/demos_interactionTrees.manifest b/test/fixtures/support-receipts/demos_interactionTrees.manifest @@ -1,3 +1,3 @@ arboricx.module-manifest.v1 -export asyncDemo arboricx.indexed-tree.v1 e6db43b844fe89c3187ec105187d1a344d9238775b804484fa55b312623b931a arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 ec630badb3a088eb11028a7170635325e85f2405396515c14d37f7e21b43eb7e arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 c20d67be9790c82e2ee24d887bf25936b2ae9105a9f666884d5b7c94facf7781 arboricx.abi.tree.v1 +export asyncDemo arboricx.indexed-tree.v1 66de61f03bfa993d4a24d32b585dd82e22bf5885f59c10d371636ade9fff4a69 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees.receipt b/test/fixtures/support-receipts/demos_interactionTrees.receipt @@ -1,234 +1,242 @@ arboricx.build-receipt.v1 -source 1d81c9b0665494ef2acdfc60a3b4c243518121dc5a7a35bdb4a31244eee5f076 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source dd2f44109a0d14aa50e6a670067832720ff882f1ce7d8fac094d82280cbfb005 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 806c9fa617d5c1397ee5573d22f9e03aa0bbde13f3100450591926566015a1a7 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output d660fd8fd3543687f0c9efb715a8e16b1e7cc194e127ee6741be3079e7d9eb7f +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_arboricxServer.manifest b/test/fixtures/support-receipts/demos_interactionTrees_arboricxServer.manifest @@ -1,2 +1,3 @@ arboricx.module-manifest.v1 -export main arboricx.indexed-tree.v1 3ad75a5383393ff66fb596efe8b70b90b6805fc0af35bd4cb55d7df1d18d376e arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 bafcd8f639d9ec4f1bd5fa5bd5a96b6c53b4e44f36c590bbfd57e60b1c640437 arboricx.abi.tree.v1 +export server arboricx.indexed-tree.v1 ed28852631a482008248cb3f472ade645e8e613110187800bf9b310f36f90c31 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_arboricxServer.receipt b/test/fixtures/support-receipts/demos_interactionTrees_arboricxServer.receipt @@ -1,269 +1,248 @@ arboricx.build-receipt.v1 -source 26fd628be229f54f5376a0a8addc32fe79fc34e7a8941ecfe488a7323a47924e -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source bc09110023edc9a6aa7d947e51a09385f600d4fef66e8fd765808ad14f7ecc34 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 4dffa5e8e3c9b3ab58f9b8e8d21360f9d41267ff22baf569be3a34ce07836816 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -local arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 17480c86cdec6a8463dfcb8c6003fb5e0061b4629ad5b58160b799a3d4e38f44 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 pathJoin 829f14f27a485a4b6c46b43e7738745a7de75055f29d17a185eca46eba15f7da -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 objectDir 8a7f9fc2cdb834eab42af178576aa9208658f3050dc6358a18647170ef34713c -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 hashShard 1f3d07a8d7cd99b856642914d152f7ebea5755263480690b2922b6514a5e587b -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundleObjectPath d640ad60309411f5ec38b4909f9f5ec21059cfa5f7e3a16d6f56d36af62f7624 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundleHexByte? 926b2a24871bf471ca1dab8e33cda8f7d9be5c050055fe60ce08c485c65a1280 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundleHashBytes 60ad71a82050e5e3a27f0c02942d2cace652ff0c1388cabc0c2296e840101f7b -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundleHashValid? e7131e7ad5f7733a858e2838a156c6967b0cf653fffc78e75d7c6f9bd11aa084 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundleHashC e1b0b6e4a5f17c8361bf89023def1725ce4110db10d07163fcb94b8138678cd5 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 ensureDir 1148afd31ccf07b90bf34fd26fee1f8758039feaf00e33b48983468df65e3641 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 ensureStore 21c3a92eb520185088809ea569920b48cacb0736d96910c597707f79d73f99fe -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 putBundle 058651f00287b672eddcb8eacbf388817e3205266ff6afedf26ca4e8e1e448dc -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 getBundleByHash 1bc08de4eb1740eb87ec9304f616aa2d57ddf833115a2400dc95c31dd215d54f -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 stripPrefix c5567e6c98ffe51fd7d9502efcec133c5665602b692bf197fa7557a3ced86705 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundleHashPrefix 04ef2d1fce822846b3aae9b70795fdebcdcc8be1a841f35b04a16ac23b8a73e1 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundlePath 8c0b567fd42473cb50c21667e711d0a1c2a703088e9f419326186488174a83c0 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 healthPath c62b994110ddef7b756d7778ae2c1c8f598685733ea33dcc7a8870c14813f244 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundleContentType 193f98476a4848c8a543af754a2c98ab842032db4c0721a22e21a607975d112a -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 htmlLandingPage 13e9d1c402f9008f18b36172d573fc1585e6303633b739ba10a265d3239037dd -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 bundleResponse 67a86a9a02376a8bb0f5d63fb2c4ede8d595cd084fc69bbf16498443b8390520 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 serveBundleHash 8e34251a1e18f485a6f293702bc003bff765519eea94c28fa0a8b303dc1f1766 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 healthRoute 5b5215ffafcfdf3a8f5c7a46808eff960c10c0262732dc7ee01718e61ea3da1d -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 putBundleRoute 8933ea81efa2fa62fac97df494840bb3f05ad8e7a725ee9d46e9f7142050e52d -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 getBundleRoute dbceb10c93169cc67774a1d989f37474a13124d154c20d998af4c936f4e5f36a -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 packagePrefix 4863ca9af271ee8d26de1c3670e7f7aadd47f9f4de8b2a838ead8903a3604425 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 packageAlphaNum? 6fc7db89f4e88912c897ef3f334cdd598fe1f62e14a61e7be183827f4c269cd8 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 packageByte? 540d4151153d268fd54cb13c31f237a23f95e37c1b1adc2c6bff96d87f9546f8 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 packageVersion c315d31ca807d567a2031b0d268989b971bbdc3b970cacc79b095d6114cd00fb -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 packageName cbd8d3ecf8cbc5d455d4a7cd07d099984a5064f7b1dbb822c4e1d631f361c2d8 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 packageTarget 523ebe158e800b99ef8d34b5d81335c67c6dc9dece752fe7140a8c79ff9c49de -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 servePackage 83a674b06af81df4bcb491637917db4e6b280c1b94d0ac2cd17661cb039640be -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 getRegistryRoute 3fd2d0b3febd4424b550b2e09867575257bb9dc5dc3744ba4edc4fc6b10eb4d0 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 arboricxRouter 2076924505c32731ca5d2b3283782e085c4838ff19f98d29510d28885670002c -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 arboricxHandler 598db219bafacbc1b49d0262505edef0457197e7369d39614f64a7211552cbb0 -pin arboricx_server 451e24aeec39ff1295c0c735897dcefd34b9219ef72a8b0c619b678373afcfa1 arboricxServer b7a040cb073aad8579c4c479af8c2f8c23f277e94a003e4ea177cbd55ce011cc +output 191f14d90b99d21abcf13026c642c4e5179281f828eb509fdef0ecaf8485243a +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local conversions 78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e ccaba9486c6d41f3ce3872b0e45477e65ab94e14cb3aef114fb6b7b0d7819811 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local arboricx_streaming_v2 7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17 03933bc5c701092038c9c2f717a2825ce57f969eb3550cba28e0d82db38a89d1 +local arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c e4b04f56866e47e5304580a73fdbf6dd6229cecd37a139c73911746252244dcc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin conversions 78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin arboricx_streaming_v2 7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17 pump 4fa48f8af8ecff423e13ef5a6256673b93a44deb1b70250e16ab873943c6d422 +pin arboricx_streaming_v2 7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17 connection 78c39e29f3aeeda76afa2c892e0e4ede951a01602685f3acdf3fd634de9aa402 +pin arboricx_streaming_v2 7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17 serve f379736c9deb17fd019c713611abb5757e64eb316e93cb3345c500956b43a911 +pin arboricx_streaming_v2 7657a7852f0788708c427dc235dabe5de8fa8aa8387cf34319fcd53796b8db17 serveCount b7732c4b0614999ba1a5465de00ca177520ccb1394b6db74edc30fa2fa6fbec5 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c request f430e7f24a57d747a74070bf07061f3d87b62771e2a4cdc4364e2d4a2be11ee1 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c packageLookupV2 f3af424161bd70a46f7848fad4d56e2f15e6ba93847a9575c6e25a9b1f8e0193 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c stdoutLogV2 a75bf5bec72aca440ddeb04237f8f3525be5c47e3501ef7a54d4105803249373 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c writeLogV2 f9ea00f45d2c41925580dc7ba40bdf83be58b109c2ce0c03fdd1b38ac6dd39e4 +pin arboricx_handlers ff73cbc4cc3a91dbfe3179f6ab3a5bd2bfa16ef5bdb2f0368b5b4a17fdb6129c routerV2 bc649518d542283be5012b08e2a418d63785c8caf3914d128acc68037db65327 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_echoServer.manifest b/test/fixtures/support-receipts/demos_interactionTrees_echoServer.manifest @@ -1,3 +1,3 @@ arboricx.module-manifest.v1 -export echoLoop arboricx.indexed-tree.v1 0b8bb82f133a6b5778f56f2c973add3f768216192ebd8aa53535ab67a5853c7e arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 9c068e7b9de20a527b197e5ca656af236cf093270126c5e5dadf7c0f5359deac arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 989105e71f4e0a19bc2f11e350c1f097bd20d1a6d2cb24d9fe676c89580dd3b1 arboricx.abi.tree.v1 +export server arboricx.indexed-tree.v1 9c8027818a31b9b561669236a83a15d2f4d64ca26f8fbbb8d01737f73639693d arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_echoServer.receipt b/test/fixtures/support-receipts/demos_interactionTrees_echoServer.receipt @@ -1,269 +1,265 @@ arboricx.build-receipt.v1 -source 2c0562ca2688383132093fca8365ab15012cfab0b8d918e566902c82ed9eb42f -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 834a2c8b9c9007364036bb22159ca0c5050c1536e87c4c40bf5573efee863e30 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output b95fe82d3ef87fe7b3a1aa213dc4dd9ccf867dff464e540e7a762f1875517546 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -local socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 4d83b01a38a451728c81488e3ad4ddff15bf982cad2d743694203583184410cf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 socket d0962df2eb672cddf52b8ddf4a76f643a76280b4905c97495efb4af859265516 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocket 4b5cf72937d3fc20f6b23496986227b44a1dcaa1d18a17d74da2a3dcb01d9b6b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 bindSocket 007c736fd19010f007eef45cc4d649b5de7b0f77d042a6c17e4eaea6ccfd007f -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 listen 960de6adb3cce7b002e915e47f405294bf3687d724faff2a6ca5f8926e7d7d93 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 accept d5af3a6c23868f6c35b58488dee362395d7e1d54b94915432f2c1f221b5e7be3 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 connect 5ae01fe00d755ca83680ab01b27ddbaa9f4713b3832de18d714d07c7c14a4317 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 recv acaf941bcc0b17a09d151ed9cf70c23ea3f33a897ce99dbdd6ef5e354bc5514b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 send 641b2ed091cb6eddc1dfd51038f21ac0afaf8e6122440f40b709ab63c8c9d953 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 getSocketName 9ed5573f5c861837f24b973c84a456d5a47081cd000ebf71e2d1ce0995463468 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSocket d2dfa18c4c469383d96d14ff08646db8b345f1bad52d8772b9b59e2b5225315b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onBindSocket ae3e99d9ebed09010981a11eb788cb572cef63a4ca625d86f7196fde97527530 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onListen 5a6660028564f14fbe8bd68e0574ae31c4eb254064169438957864d62bf16868 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onAccept ea248480f5681c96d255c317a0783dc98acd028c8ec07d324ac2ae02a764374d -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onConnect 5ba2194bee14d9fad3c282147bebd0c03bc55b76c518bb684350c7901bc296c9 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onRecv d5f6bf951f02d8415354bbab1e720d1d87e3d39caf26dc6a0b9801cd03b42ed2 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSend 112d48a3a067379fcfaa56e894ffe0aaf02e3f3fc44bc2fe8472f0d49a47aeca -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onGetSocketName 99d9c9dff1637310ff2c9a001cd718a36cccc03a18d84a3294f2ab7874af1f76 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSocket_ 3c394ed1369781ee2254ded5956687c27971c495548b6278e5e62efd757370a6 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onBindSocket_ 7c91b3aabf7daffd7da4e525db14be852b1e3b874212d7da444467d6de0585ed -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onListen_ e9f2c57f616bed580f21e58bd01b327d4615680d6209574cfeb6a7da85758025 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onAccept_ e4b93ffb876803acb6700e33127436cc53f348efbd0d422ebca09a3371b1614e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onConnect_ e0baf23e45038091b6c6ccea70941dd3fdf96128f94a595ae0f320f095979025 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onRecv_ dc391a9c916186a16974abf368dd23d0bdb3fced7b5f3cffe8a655aa41f230ea -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSend_ c746325d45b9a88f5519a5399bc784bd7b3eb5ba53a1aa37f6030e64b0d109bd -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onGetSocketName_ 31d562c8271942cd7eadefdde5bac73abbcdbcbf63ffc4e9d83b68ce39929988 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocket_ e10c012b9860dd7629b768fcb75126eff626b2f80ea1a0095ee2efcaa68b304e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocketOnError 21d850f11f110d1bdb7d4cabd44913473f997337ded0df8b79e3f94034c0bdf0 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 listenSocket 8a248c717b8216370b40ddc355851282362dd2bc130df5adf5f8f0aa2a3d9dcc -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 withAccepted 7838f3c8b28ede95756dd9ca6283db1a3519bb95e0b19ab3bcfcabedabb1c900 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 withAccepted_ b33b77bfe9094c3938bbc8684d668da222d2b461cf020fe927f848c1bb4c26a7 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveOnce 2e39131bc2e390022e72a22235cde46ecf0a0b54a46618719ccaab660acbc306 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveForkingOnce 1194e71d76856c2a519ef62c0197d4399fa30ece80ff756eb1419f57197aa927 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveForever ac87d6be311b022bce24cbbb1f75b5e80db4f672c04d695afb800e81cd1dc95e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 connectTo 1c2bee16cdfaa4046a2f173d0210f26c0eb48f356dafa00a84cec4e34f553bcb +output 75aee7d8167931634b5bd5566aa0c06a45be0f5a3c5c16aa86d2b1a75d684a42 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local conversions 78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e ccaba9486c6d41f3ce3872b0e45477e65ab94e14cb3aef114fb6b7b0d7819811 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin conversions 78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db compare 25d460f10cf8893795a8a5eac6a951b40459ef90e35f6945f72c3f53868f94f5 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db equal 07e8af1f8ef6366cab90420cf1ec97a15b434ba4ee3966af235579a05935180e +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lt 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lte a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gt d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gte a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db inRange 62a3f72adbb9030425f0e63e43568c0eae069d7293763fab3a83123edef50aaa +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db minimum f515c4e49514b4631ca183a2a2dfcfce46d5ef8a8338a09496ce9a0ca2f6502c +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db maximum dfcf25622f229c0ae04441aae5884a38d0ba046ec512c8ac1fde56290657938b +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db zero 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db odd 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db even 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db successor 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db predecessor dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db add 066aebbf5d3b48d5e6c9c35b5c59e9cc5172f47d09899684475738eafb4fbe4a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db subtract a5a9212b766933b7c8f07b3f98f7d9d789d0ae45e1301042da712fefa3dd0a3a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db multiply 320a41f19797fbb9a05d6615b53be543b7acec4e3e535ad81c8f1e5f16db3de0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db divMod 068932c208504ea9a547086ba761d6975ee3173d495b80addc056999b7fc557f +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db quotient bdfe626a727affba89d4c11e489eca0086f4c42facfb4ee9f248e69a47abed8a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db remainder 80f8dd39e11423480ce2f5f5edde99104824bab147a88a2c331c8f643a0e6817 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db power 3b59e090c4ea2d7966c2bc7bf21c9ed67940c1c7a41f01f6e04db93a69ca54a1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db decimal 644d54084c9734a5b58ebc7d9bb973eea48890665db1617679db9bd1c8bd68b0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db ascii e0df024c0168229497859a5e770f7e36594d9d266c3933102d1fa1997d4b3225 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db digit eed46692dadce061198cf3cafd6c00bbc5561d9fb637706d16bde41552a1b9bd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db letter ddca67e3c882ecee1239775aecf0d31b8ce136f86e6a6eea70e5d8b5979b0b30 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lowerHex 3b752ef4b4b89ef102dc4c43110ee8522922dbaadf77495713fc465d9e718eb4 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db printable 9c88d7eaba58c0771da373821f7223c298bea7697647f03f6b1a4d922ff8a6e5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_environment.manifest b/test/fixtures/support-receipts/demos_interactionTrees_environment.manifest @@ -1,2 +1,2 @@ arboricx.module-manifest.v1 -export main arboricx.indexed-tree.v1 631315c7be8413dd7f8a0dc2f393a7b1e9db638c191e141eb211a62ff4284be4 arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 ed6d09b22d87bbbce475419f15adf977399d4cf939a8f38e5309df46b5799712 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_environment.receipt b/test/fixtures/support-receipts/demos_interactionTrees_environment.receipt @@ -1,234 +1,262 @@ arboricx.build-receipt.v1 -source abe18fb13b4fdafce7d6d7a011bf230b21d081ddbc7c9c842ede42a9b6db4594 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 4dcb43187b3cbd168fcb3ad5269be3e96257aa30773bca933e6cf37dfbdee066 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 73aa85f792fa244c5ef5f21cee568764b6b10974c606091b30c421cb333a44c9 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output e645ead315c7c3c16a0d4b43d27a89ebc0d54495a4ca69e82c3b583de224f43c +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bce8dbaff65ded636ca85f0a04f0cc7f5ed78e59c717990697239888dab5c1bc +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d pure dab5acd986e6ef2cfd473cdfda2291978bdd05d53d654568aee54a44035cb126 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bind b4edbddf65d1f40c6c6c069ddb37c1c763b47a1a57a303ac1ee00ae75594d2a0 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d request 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkedRequest 1c58f5c798102b7dbfe5ef63251298c3cdb35c7e47f2f24dc0d72db7581e9b51 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkWith 911a2190a2a0bf023ad23badeea4756df03a3f7f02139581b19bc0f2011011c6 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkRequest 583ccf6db95ddb1be9e6f5afc1ca98ae6b410945d3d1643084d896f1737c16a5 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d handle d17093c9654d57c134b6d0f00580c1710e552f34d7583b9a1e438eb073a22661 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d forward 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d label f4325f96264534084186bae09455f2a8060568d39e45d57692fa896ad4bf3138 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d fail ab8e873ce69d0305b9e50f7d0fd0c02503c09d352f4fe6f1a80b6f471724e7d8 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d liftIO 47be69da2dca13f8c6a3e3c9107cd843158b3bf576fbde3d156b1daf927dba64 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runPure 79d3a676f798e1691119cd442473d17a86e00bc26a56843f4715f70ba5886c37 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIO 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d collect 28b9fa70f3c2243be4e459f9bbc356893c2dcd2bcabdbd9106f09025cbd96546 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d view 989468627f11a7589c918e56fb01da459397726dda8c07c7808c30f1eb0f613f +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d diagnostic 0a05b217b6ff9aef546f4f6d8c4f699f6ad8fa5bb1a73fafdeebd60609f2a912 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d scopeV2 0df99b6d6d7b1f1559b83c2980e1e25c72a1c43b32b758c295a35ad23ce60f0a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d spawnV2 8043d72533c0d1b1ca655ea4ecd12bbafd5e89402b9cab9ef3071201a4fbf8b2 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIOV2 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_forkAwait.manifest b/test/fixtures/support-receipts/demos_interactionTrees_forkAwait.manifest @@ -1,3 +1,3 @@ arboricx.module-manifest.v1 -export worker arboricx.indexed-tree.v1 46dd7787041b986ff0c66d74f4112f94737621d0149ea3c5b4abd3de4f8f5e24 arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 f8a4dc8b16828749db29ddef5a8c19242c72eb5d26fccb46beda3bb3d7b06c43 arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 f28474b837c8e4d705226e351ac24202a4028f88fef7c280efb304e54128364e arboricx.abi.tree.v1 +export worker arboricx.indexed-tree.v1 2bded52536bb9ea6dc17c47ad6833f7beddc73f1820a12690f54928743050de0 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_forkAwait.receipt b/test/fixtures/support-receipts/demos_interactionTrees_forkAwait.receipt @@ -1,234 +1,242 @@ arboricx.build-receipt.v1 -source 4911d6c33e4c159fa6723fa9849681666724a9b7247ab982decf347dab5fb6e5 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 79c07186e9a3a36ce4c1de54b331c42dec6b95aa974156677c9336fd1c0849d7 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output d6b939af9c141227cf383f359ef4418702813e7426db43fc1dcd6a179ad5059b -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output 2512a36cfe8af8f4166ba25c9b88d5c398e1535483370fe3c3e1379786d5caae +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_getLineAsync.manifest b/test/fixtures/support-receipts/demos_interactionTrees_getLineAsync.manifest @@ -1,2 +1,2 @@ arboricx.module-manifest.v1 -export main arboricx.indexed-tree.v1 91602887664e501e9c40d743d5428d9e1b07b9f5d728fe496f99e752a91ce53d arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 b32e74cf400189ae918cb1524e49ecd3b80325f619cf57471125d370b91de74b arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_getLineAsync.receipt b/test/fixtures/support-receipts/demos_interactionTrees_getLineAsync.receipt @@ -1,234 +1,242 @@ arboricx.build-receipt.v1 -source 7eaf0dbf9f3ed9b6e1098c1ee0d5953668d835070976596b8fc20e40c7d859d9 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 2b971f5cfdfea12210f6ed1801a3660735e1da97d4d372cf751eb76879750991 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 3341c45590f6fc86ddff6720fb52fb4dc058a27e4f60d9e488a16fe1d5ada90d -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output 54f2765098bd2580e08d2b8390899cbe49e956fbf037e7e4697e867d3001e464 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_greet.manifest b/test/fixtures/support-receipts/demos_interactionTrees_greet.manifest @@ -1,2 +1,2 @@ arboricx.module-manifest.v1 -export main arboricx.indexed-tree.v1 642632adfca35dbc64a66b5d326a0d3eed52fd03dcf7762ef4c72a086214b405 arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 d31b0bcfc35346148e6cce5419b3413dbd150200443dbd2db34d1cf309d1f717 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_greet.receipt b/test/fixtures/support-receipts/demos_interactionTrees_greet.receipt @@ -1,234 +1,87 @@ arboricx.build-receipt.v1 -source ced74e1406629ffec2a9eeae58b87828f9507cae7d2f112a2bdd65ff74034f93 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 40783b2c9c5750583151dda9d04b435d3d0d8a63b6f33cccfa94fd8ed958c14a +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output cc8b75304621d4747a6ba5951d63726776b311b423cb3cf4190cd61be43ea706 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output bde116bd78a59e12c6add24a004d0941b82e89bba5d3282c570559d930dd0d17 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_httpServer.manifest b/test/fixtures/support-receipts/demos_interactionTrees_httpServer.manifest @@ -1,3 +1,3 @@ arboricx.module-manifest.v1 -export myRouter arboricx.indexed-tree.v1 8dc642621d184e620476847d54ac1132b1bdc154ac5050440968d5fcc074530e arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 3a061db72a1d7a92295d1a10c4b6fa3cdc12388e7c0ac6c1da68c5ea695fe6e2 arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 c236dd639158cf86d9c4c189299df78b09c7c0ac409ba7cf169387b0851de351 arboricx.abi.tree.v1 +export server arboricx.indexed-tree.v1 b2306778500052198ca45859a5d95a1c33585fa4ac3398cb528221936d1e789a arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_httpServer.receipt b/test/fixtures/support-receipts/demos_interactionTrees_httpServer.receipt @@ -1,343 +1,328 @@ arboricx.build-receipt.v1 -source 664cc315f466f9a8ea5b39e1b7e16febd78e1e7b950f86b0cecc0ec6d82fefa7 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 386257391872e7b73befc2bcc3634706acf4acf0ff791bc60161179434c9a813 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output e8809f0967a2f4a6bac9004c28e316072ea17d924e6e5996f73961121d8e4de0 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -local socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 4d83b01a38a451728c81488e3ad4ddff15bf982cad2d743694203583184410cf -local http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 f0afce4e54933c592e6987610423d4d192c7a4f22e7eac1a6250e9825e4a861e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 socket d0962df2eb672cddf52b8ddf4a76f643a76280b4905c97495efb4af859265516 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocket 4b5cf72937d3fc20f6b23496986227b44a1dcaa1d18a17d74da2a3dcb01d9b6b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 bindSocket 007c736fd19010f007eef45cc4d649b5de7b0f77d042a6c17e4eaea6ccfd007f -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 listen 960de6adb3cce7b002e915e47f405294bf3687d724faff2a6ca5f8926e7d7d93 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 accept d5af3a6c23868f6c35b58488dee362395d7e1d54b94915432f2c1f221b5e7be3 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 connect 5ae01fe00d755ca83680ab01b27ddbaa9f4713b3832de18d714d07c7c14a4317 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 recv acaf941bcc0b17a09d151ed9cf70c23ea3f33a897ce99dbdd6ef5e354bc5514b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 send 641b2ed091cb6eddc1dfd51038f21ac0afaf8e6122440f40b709ab63c8c9d953 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 getSocketName 9ed5573f5c861837f24b973c84a456d5a47081cd000ebf71e2d1ce0995463468 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSocket d2dfa18c4c469383d96d14ff08646db8b345f1bad52d8772b9b59e2b5225315b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onBindSocket ae3e99d9ebed09010981a11eb788cb572cef63a4ca625d86f7196fde97527530 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onListen 5a6660028564f14fbe8bd68e0574ae31c4eb254064169438957864d62bf16868 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onAccept ea248480f5681c96d255c317a0783dc98acd028c8ec07d324ac2ae02a764374d -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onConnect 5ba2194bee14d9fad3c282147bebd0c03bc55b76c518bb684350c7901bc296c9 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onRecv d5f6bf951f02d8415354bbab1e720d1d87e3d39caf26dc6a0b9801cd03b42ed2 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSend 112d48a3a067379fcfaa56e894ffe0aaf02e3f3fc44bc2fe8472f0d49a47aeca -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onGetSocketName 99d9c9dff1637310ff2c9a001cd718a36cccc03a18d84a3294f2ab7874af1f76 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSocket_ 3c394ed1369781ee2254ded5956687c27971c495548b6278e5e62efd757370a6 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onBindSocket_ 7c91b3aabf7daffd7da4e525db14be852b1e3b874212d7da444467d6de0585ed -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onListen_ e9f2c57f616bed580f21e58bd01b327d4615680d6209574cfeb6a7da85758025 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onAccept_ e4b93ffb876803acb6700e33127436cc53f348efbd0d422ebca09a3371b1614e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onConnect_ e0baf23e45038091b6c6ccea70941dd3fdf96128f94a595ae0f320f095979025 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onRecv_ dc391a9c916186a16974abf368dd23d0bdb3fced7b5f3cffe8a655aa41f230ea -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSend_ c746325d45b9a88f5519a5399bc784bd7b3eb5ba53a1aa37f6030e64b0d109bd -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onGetSocketName_ 31d562c8271942cd7eadefdde5bac73abbcdbcbf63ffc4e9d83b68ce39929988 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocket_ e10c012b9860dd7629b768fcb75126eff626b2f80ea1a0095ee2efcaa68b304e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocketOnError 21d850f11f110d1bdb7d4cabd44913473f997337ded0df8b79e3f94034c0bdf0 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 listenSocket 8a248c717b8216370b40ddc355851282362dd2bc130df5adf5f8f0aa2a3d9dcc -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 withAccepted 7838f3c8b28ede95756dd9ca6283db1a3519bb95e0b19ab3bcfcabedabb1c900 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 withAccepted_ b33b77bfe9094c3938bbc8684d668da222d2b461cf020fe927f848c1bb4c26a7 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveOnce 2e39131bc2e390022e72a22235cde46ecf0a0b54a46618719ccaab660acbc306 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveForkingOnce 1194e71d76856c2a519ef62c0197d4399fa30ece80ff756eb1419f57197aa927 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveForever ac87d6be311b022bce24cbbb1f75b5e80db4f672c04d695afb800e81cd1dc95e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 connectTo 1c2bee16cdfaa4046a2f173d0210f26c0eb48f356dafa00a84cec4e34f553bcb -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 maxHeaderBytes e62a642ffd71dff46c4c03641e08f13490469e84bbd0815403b577a0f4916f90 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 maxBodyBytes 40a6c6220b30ead48df9abc0cc7f1b6202197de18d97434061fc1d4ccd6d1292 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 crlfcrlf f77e4d0100d99afad2fa15decbeb38aae4ed8c3135d5ea9c2e5a44537194a871 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 statusPhrases 0bcd79d7b0f770431eee50a6be0f938b32458a41f7f57ba9b2edb56baf607e97 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 lookupStatusPhrase ad3e569281eedcff04104de5e3da2170f57046bdb8528857b92e9d4dcb78f97d -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 statusPhrase 67d1a009c420ebc91c9ba7533209c485bb9bbd4c7249f4ac48be6b5e598f809d -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 statusLine ce0da21e44a60d7d7c1b09c43142df1dcbfa38b5a6830ffcd3d0a7704cae67af -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 headerLine 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpAppendResponseHeader 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 buildResponse 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 response 6d37791d690e6d36d3ca8b5541391b6b629c1b90b5d3da2a4101610840b657b8 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 emptyResponse 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 okResponse 1d039ba7479cfd48d6a4ed6fb194f16697c63e4c34338343f12a0e35c6c6c6c8 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 textResponse 1d039ba7479cfd48d6a4ed6fb194f16697c63e4c34338343f12a0e35c6c6c6c8 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 jsonResponse efa5ea017979655879b4be69b5a08251035283f4678e5ffbd5e9fd933d7e590c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 htmlResponse 9a1fa84e4e9147107b3c2b36756638f9e847163fba25e502951eb64de1d6a5fc -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 createdResponse 8a3469d3a2cce957b1571d4f84881d13f41d1e58db82d8d5f33bd3baaf961235 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 notFoundResponse 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 badRequestResponse 674fe3821cd49c8a73c6f6f3abfe3004ca33b393de879969e3b768bf6abfbc6a -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 errorResponse a83633de4defc0b4a70603c6b5d0fdf57be6567db629ab9347a7cbd7fd13cb9f -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 headerEndState e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHeadersOnly 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 headersOnly f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 responseForMethod 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpRecvUntil 6dea4b3cdf235c3a453e51049cc4fcb3ee3a0216e0dbe0a6987b43a33355db5f -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 recvUntilMax aab00d1f8d9ec906fb402fbfa3ef5cb9ab301bec0d6b5d609c9fb4e17ccf430c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 recvUntil 3ab70212ad49e37e8c6a29e393a9e5790d3bda04b5b56fca1ced5932bca79a43 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 recvHeaders 6c9519ee8c6c45ef98745ad799df591dc46acbf8eaa6219659bf03de38fbfeff -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpLineError 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpScanLine 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpLineReply 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpBadRequest e11a0cdf150e1784e00dcebff8906c033c6695ebc3e7c5716b72715dde8695a6 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 readLineBytes 7bed187a6276169e2eac95cdbb4f2fc53f3625dc6b183b4b9e56e0c5957c34b6 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpNonSpace? f5f2f658784d8ded1ec828967e19324ffd4fd2438e5a6ebcad0a6c07f9feb908 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpThreeWordsReply 5a71c4b351610b15374829ba12e3f6a5f793760c5927d414c944d1060df60161 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 parseThreeWords 1f04df81381567601348b1442947ed8792f3dd0424eb9393b3c147f33493d94a -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 parseRequestLine ace457a20b9f1ee3e757a622f3ba03b191873e4d4835cc7ebdfd947332561769 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 toLowerAsciiByte 4a9d74221452385910ba4d4b87cd60d31efe31132b056f32777ebeec6b457d9e -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpTokenByte? d0c894a224efa6fb17498ee6791f4598a26e38419b51d099af9e80035e3cf85e -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpOws? 14e51851f694336c78a7a25b88289c6687b8e0a19af8b220c3a027d5270522c2 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpFieldByte? b7b24f9ed6ee56a602e896fde3aee4c07fde3dee849b608fa451128d5ce4d196 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpTrimOws 7ab13fefc873a02011c645a56f65ca119806b1f7ab740e6bf56d1fb02ec7067c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHeaderLineReply 962626d5659cce124775e1b8d98482db74433856dc2701767286121681223c56 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpParseHeaderLines 382f455876fd6d1fb704e3d96a1d33bb569211d3e6de1ccb604337266c0ba744 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 parseHeaders a0fe7e14eacc4024e5812aa0443a8345954ff7db342218db523258cee87aa39b -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpBodyTooLarge a35fe61177a0ecce71ff585252f6df5f81e9201ad24e1c238e034c5500ef4d60 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpLengthOverflow 4a4e6626a22d3ee2d2d435940b99e86e0589945c66b3cf185f4fa64ff32b3e76 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpLengthDigits 5c312192ada0032aadbe847e6c6164a786deb66a8ae0e0adc6af140e4791da6b -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 parseContentLengthValue 330f1d204f021be9370b7fb01e3f11703a860532ce46f6f276e0f45d4f59356c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpSelectLength 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 contentLength 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 bodyReadState 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 takeBodyBytes 6996dc93fdff652211977ed68ef5db211c3df7a1db86c958035db653c1b228a7 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpRecvBucket 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 recvChunkMax4096 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpReadBodyState 8d477bdab5f8db48905a5f6b8c19bc69654a0bad367d2fe1680303e7a28b53c7 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 readBodyMore 6d75303c7d3fdda892b2b276dbb96c01019b1c25b8153f695e6c799c03c40355 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 readBodyExact dda235ff519ff0ac146c9e170d18f218a80e829c8cc034d2e7825e6c23d1506a -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 readBody b5280a0a7555b439dd5ce42bf592a165da51f95398087ea43774d6b45510fddc -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 validMethod? ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 validVersion? 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 validTarget? 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 validateRequest 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 routerMethod c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 respondAndClose 467c1b022f83b6fd3fb3e9fb7ccc6cd78d15c2b9b8a936987088c7c46639aebf -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpRespond 4c15cdb6bd52d2a638c93cc3719374cf4eb91e53eac55c58195b1ac56bdbe540 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandleBody 55672ac8616e621c775081a59ad851ff50fcb247dd11d31cf773875fb7fa1c55 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandleHeaders d21b4ed9b21aa5af60b775ad96959bb15648a5b19171691d555f3c71f8f90d6b -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandleRequest 939d41afd142bc74e6a4ee1b0cbde8cfa5d06c87c29ebd9ce68aaf161d98e586 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandleReceived e40bd55458aa83659301d11b22764e03d38567791a23a472d33fb981974260e6 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandlerIO ba715371563139d668ec9ec2afc8bb27407d4a6795cd449dd87afa11a9e698f4 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpPureRouter 373f2de1bdb7cb038c91aa75737a3abfc95c2655ac4bc1d4a89b6739f73c0849 -pin http e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 httpHandler 777fe83b3bf5ebb3b36d9a10d202ce381e1c488beb6667f70dd9f58961e4352a +output 7bf87e78b8a173b29555509fdc24536674c1dac39b61fa5d089ae9958c7bad16 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local conversions 78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e ccaba9486c6d41f3ce3872b0e45477e65ab94e14cb3aef114fb6b7b0d7819811 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 92cd4388ee95bd4452c9f146077f97ed5528c475e421e1d032a537181a682f15 +local http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 85e6f4f5df9e72ca19d0d1ba874262b7cd277d1854a295790d707b184fc6f0c8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin conversions 78ce6db3557cd57f2c5799989d16a8f40104de5c4485646734a23093dc458a4e showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db compare 25d460f10cf8893795a8a5eac6a951b40459ef90e35f6945f72c3f53868f94f5 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db equal 07e8af1f8ef6366cab90420cf1ec97a15b434ba4ee3966af235579a05935180e +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lt 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lte a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gt d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gte a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db inRange 62a3f72adbb9030425f0e63e43568c0eae069d7293763fab3a83123edef50aaa +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db minimum f515c4e49514b4631ca183a2a2dfcfce46d5ef8a8338a09496ce9a0ca2f6502c +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db maximum dfcf25622f229c0ae04441aae5884a38d0ba046ec512c8ac1fde56290657938b +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db zero 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db odd 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db even 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db successor 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db predecessor dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db add 066aebbf5d3b48d5e6c9c35b5c59e9cc5172f47d09899684475738eafb4fbe4a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db subtract a5a9212b766933b7c8f07b3f98f7d9d789d0ae45e1301042da712fefa3dd0a3a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db multiply 320a41f19797fbb9a05d6615b53be543b7acec4e3e535ad81c8f1e5f16db3de0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db divMod 068932c208504ea9a547086ba761d6975ee3173d495b80addc056999b7fc557f +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db quotient bdfe626a727affba89d4c11e489eca0086f4c42facfb4ee9f248e69a47abed8a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db remainder 80f8dd39e11423480ce2f5f5edde99104824bab147a88a2c331c8f643a0e6817 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db power 3b59e090c4ea2d7966c2bc7bf21c9ed67940c1c7a41f01f6e04db93a69ca54a1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db decimal 644d54084c9734a5b58ebc7d9bb973eea48890665db1617679db9bd1c8bd68b0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db ascii e0df024c0168229497859a5e770f7e36594d9d266c3933102d1fa1997d4b3225 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db digit eed46692dadce061198cf3cafd6c00bbc5561d9fb637706d16bde41552a1b9bd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db letter ddca67e3c882ecee1239775aecf0d31b8ce136f86e6a6eea70e5d8b5979b0b30 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lowerHex 3b752ef4b4b89ef102dc4c43110ee8522922dbaadf77495713fc465d9e718eb4 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db printable 9c88d7eaba58c0771da373821f7223c298bea7697647f03f6b1a4d922ff8a6e5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 recvHeaders fae92c42be9ddf04847aa2f6e1fcc670841d313d09bc648328095e68f3a6c7fb +pin io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 readBody cebdd476321f81788fd529ed96f2bf4d52bd7ca14d05eacc050b76ea2e552c03 +pin io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 respond 534ffe1c7a933f21a35fefb17f6f7bd78d98c3fe45adf5b19edb68e661242952 +pin io_v2_http 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 handle dba8f168d9e14aefd8b4c10bdc08fa796fc706b3d9e492bd299382d0abdea3b8 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxHeaderBytes e62a642ffd71dff46c4c03641e08f13490469e84bbd0815403b577a0f4916f90 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxBodyBytes 40a6c6220b30ead48df9abc0cc7f1b6202197de18d97434061fc1d4ccd6d1292 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 crlfcrlf f77e4d0100d99afad2fa15decbeb38aae4ed8c3135d5ea9c2e5a44537194a871 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrases 0bcd79d7b0f770431eee50a6be0f938b32458a41f7f57ba9b2edb56baf607e97 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 lookupStatusPhrase ad3e569281eedcff04104de5e3da2170f57046bdb8528857b92e9d4dcb78f97d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrase 67d1a009c420ebc91c9ba7533209c485bb9bbd4c7249f4ac48be6b5e598f809d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusLine ce0da21e44a60d7d7c1b09c43142df1dcbfa38b5a6830ffcd3d0a7704cae67af +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerLine 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpAppendResponseHeader 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 buildResponse 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyLength 005020fe1d72f85901d6d1911c7865245972a2be15b070ed07415082b6a68ecc +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 response 3a029a2a767699d24f1cb4bc4346dd3ccce2940ef4395eeb09b1f8fe89d44613 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 emptyResponse 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 okResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 textResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 jsonResponse 2abfe36d0337304c8c78bcb87e2f132db6d6acf2385e10cd3e1a92336c447acb +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 htmlResponse 5aaa86ca299dc2ce72f64a5abfc4e07ca0e949bd751669ed8171d4995152725b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 createdResponse 22d380835f4efd58877c625e8de7f08dd5dc1b5b6b30fdf6553e533116166bff +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 notFoundResponse 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 badRequestResponse 1b533e6b41f50c40a6d0e61d2e5661ede925fd4ad473f016b47186f4e9522097 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 errorResponse 7b6e86847a366c23c27ad86f767947425bf04a987b5f48451f8d714e102c22b5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerEndState e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeadersOnly 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headersOnly f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 responseForMethod 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineError 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpScanLine 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineReply 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBadRequest e11a0cdf150e1784e00dcebff8906c033c6695ebc3e7c5716b72715dde8695a6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 readLineBytes 7bed187a6276169e2eac95cdbb4f2fc53f3625dc6b183b4b9e56e0c5957c34b6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpNonSpace? f5f2f658784d8ded1ec828967e19324ffd4fd2438e5a6ebcad0a6c07f9feb908 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpThreeWordsReply 5a71c4b351610b15374829ba12e3f6a5f793760c5927d414c944d1060df60161 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseThreeWords 1f04df81381567601348b1442947ed8792f3dd0424eb9393b3c147f33493d94a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseRequestLine ace457a20b9f1ee3e757a622f3ba03b191873e4d4835cc7ebdfd947332561769 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 toLowerAsciiByte 4a9d74221452385910ba4d4b87cd60d31efe31132b056f32777ebeec6b457d9e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTokenByte? d0c894a224efa6fb17498ee6791f4598a26e38419b51d099af9e80035e3cf85e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpOws? 14e51851f694336c78a7a25b88289c6687b8e0a19af8b220c3a027d5270522c2 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpFieldByte? b7b24f9ed6ee56a602e896fde3aee4c07fde3dee849b608fa451128d5ce4d196 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTrimOws 7ab13fefc873a02011c645a56f65ca119806b1f7ab740e6bf56d1fb02ec7067c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeaderLineReply 962626d5659cce124775e1b8d98482db74433856dc2701767286121681223c56 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpParseHeaderLines 382f455876fd6d1fb704e3d96a1d33bb569211d3e6de1ccb604337266c0ba744 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseHeaders a0fe7e14eacc4024e5812aa0443a8345954ff7db342218db523258cee87aa39b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyTooLarge a35fe61177a0ecce71ff585252f6df5f81e9201ad24e1c238e034c5500ef4d60 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthOverflow 4a4e6626a22d3ee2d2d435940b99e86e0589945c66b3cf185f4fa64ff32b3e76 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthDigits 5c312192ada0032aadbe847e6c6164a786deb66a8ae0e0adc6af140e4791da6b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseContentLengthValue 330f1d204f021be9370b7fb01e3f11703a860532ce46f6f276e0f45d4f59356c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpSelectLength 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 contentLength 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 bodyReadState 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 takeBodyBytes 303837e8c16c2b3e76f4076ceb28e22ead58735b4796d72ad185fa22c01aa97e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpRecvBucket 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 recvChunkMax4096 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validMethod? ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validVersion? 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validTarget? 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validateRequest 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 routerMethod c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c diff --git a/test/fixtures/support-receipts/demos_interactionTrees_safeRead.manifest b/test/fixtures/support-receipts/demos_interactionTrees_safeRead.manifest @@ -1,3 +1,4 @@ arboricx.module-manifest.v1 -export safeRead arboricx.indexed-tree.v1 ab7406b3df5f3a27b25fca452842a85ed5106150cb95f46a807ce90b7fafbac5 arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 4e487b6718f5ddd0077c156dd0ab8f7811c001c511c9b140fe4414365b747a13 arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 6e08afb8daf9c244f1e7acae851153ffa8f57efc9d199c6808ca4171be5562d3 arboricx.abi.tree.v1 +export run arboricx.indexed-tree.v1 fbc4e448c56fbb4cd0a98e9d8eedaab284111ea1241eec5e7105369ca5977dbc arboricx.abi.tree.v1 +export safeRead arboricx.indexed-tree.v1 3e8ce5906e931f968cafef0275e5ed7a630d7c74ab4e73cb83d65e97887a10fd arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_safeRead.receipt b/test/fixtures/support-receipts/demos_interactionTrees_safeRead.receipt @@ -1,234 +1,87 @@ arboricx.build-receipt.v1 -source e9435aaeafcafcdc5ad778a1360328490306ce44395713d318edaa82119a3d10 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source d52075fd243ce733529740023181faa44c7c3022a9f28ee7ea2aaa078d95f7b2 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 1bafa9ab78a08f434958517c79ef9e9d3518fcd4e91f4235b4a920bdc5cfc936 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output 107bb9e32aeabf317a50c555fd2c82fda73c54654ee95d4c185717c7d0a06673 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_shout.manifest b/test/fixtures/support-receipts/demos_interactionTrees_shout.manifest @@ -1,4 +1,4 @@ arboricx.module-manifest.v1 -export safeRead arboricx.indexed-tree.v1 17f229c25444e565e20fcda131ae1e4544178830a2a7a908e3766001761df484 arboricx.abi.tree.v1 -export shout arboricx.indexed-tree.v1 ab40de6e95f670ffb3f1f99623e563e470d0bf3b198ef71b5653ad4bca0aa8f9 arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 1502772e368aa3f8ea39be9a2bcc0dfea15fa7f4eb80968fc4002fe3e414a89b arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 d21b6e3f6849f93cf02a45ad5f730fab25ece271bc73f4086e93ff4eb0dbc05f arboricx.abi.tree.v1 +export run arboricx.indexed-tree.v1 4f74459e021aa32448a0aa1197bcce1e9c3f9d828c8da589a1f578ce1dd415a5 arboricx.abi.tree.v1 +export shout arboricx.indexed-tree.v1 d29adb62fac42951e8f8d436f2b3d73384d56f2fe7d8f23d9433895494807bad arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_shout.receipt b/test/fixtures/support-receipts/demos_interactionTrees_shout.receipt @@ -1,234 +1,242 @@ arboricx.build-receipt.v1 -source 2beaabce9c2d9c6ee299d8ccbd3be62499782b9c10b6439c1027c8da7452a166 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 060a440835999d443ef767ea5d4e9efe616a7186e1bb04947d6f9f80a21b0a4b +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output f45d98fc0f3b982734ee2ade6f3cf93975e3101756be38cf6ca2dec2f2fc0ace -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output bc75e4f1698d736428575fb190f188b185f2e668db10f62fe7886866c579da84 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_state.manifest b/test/fixtures/support-receipts/demos_interactionTrees_state.manifest @@ -1,2 +1,3 @@ arboricx.module-manifest.v1 -export main arboricx.indexed-tree.v1 602cb35b92f575d7341890e37f1d01d9e9ddbd6fbdf4ae569d20fd9e8b005831 arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 a774c245206e810403f55309f008c074b1a85f163bbb0c12836d5b2f40138915 arboricx.abi.tree.v1 +export runState arboricx.indexed-tree.v1 e23bb3c2c84c7bd8a30403b8add36fc137dae6946488956d534e66a2fe757ff4 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_state.receipt b/test/fixtures/support-receipts/demos_interactionTrees_state.receipt @@ -1,234 +1,242 @@ arboricx.build-receipt.v1 -source aa0d8294256048c5caaa9e079a79ad729d0d77b182fa93d86e390bae008189c0 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 61799bafcab20f90faea5562f2c25c89bdab164571ac6073e4c029c1f86e5b66 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 0a1a75ed6807a13528e087c23812c1b0b7f7cba916d7d697332e197d44beecde -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output 763bc6ea4cb0bcab7b9b92aed6947d9220a2823a6bd9885d3a013a98b92c86ca +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_writeThenRead.manifest b/test/fixtures/support-receipts/demos_interactionTrees_writeThenRead.manifest @@ -1,3 +1,4 @@ arboricx.module-manifest.v1 -export writeThenRead arboricx.indexed-tree.v1 cd43063b5a914353231ecd378a9554b267e7ff423b31281b2939f97b6bfee5d3 arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 00dd7327ad2219fe424570daf1237b0f5f8a1a7e0af0a8c80b7fda244decf702 arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 8a88d4bebf8f641186c9a717e766aa80e16799170d375dd623332f756354d3f2 arboricx.abi.tree.v1 +export run arboricx.indexed-tree.v1 55c09af5818dc5b075a7161527fcda8577fed0a608421a9405b54d085a74b2b0 arboricx.abi.tree.v1 +export writeThenRead arboricx.indexed-tree.v1 31339cc0f51ad4c81fe55181836a1d997e0d418c6d15a8924a7ba7c9b96ccf24 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_writeThenRead.receipt b/test/fixtures/support-receipts/demos_interactionTrees_writeThenRead.receipt @@ -1,234 +1,242 @@ arboricx.build-receipt.v1 -source 32496972f1c1b6b06eb6fb58b83e054d692ea9e5e4b601947c20c72c92a0751b -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 65c006672139d98c27b5022a8641c73b6d0c9c29dd42f6cfe8b4820928664807 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 79fe12a0c2f6761514069b31e611ae97e7ca69e0c6ae6bd2d2606e4971c9667b -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output 4ea3868438079b5153746f618b98c190a8f9be5bc0a0035298f65612380adb9c +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae diff --git a/test/fixtures/support-receipts/demos_interactionTrees_yield.manifest b/test/fixtures/support-receipts/demos_interactionTrees_yield.manifest @@ -1,3 +1,3 @@ arboricx.module-manifest.v1 -export chatter arboricx.indexed-tree.v1 fdeef32f4a75a874e23245e4c85a7b3860a13c632770970c8b44016bf94cb8f1 arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 8c399940c2364c174903ff5f195cbdda2c8905d7be98cea2e04d903001928546 arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 0c64549996e07ed2277687df2d44390b5277ec5d15c10f1c38348e8e39840ede arboricx.abi.tree.v1 +export chatter arboricx.indexed-tree.v1 b580ccece49c50102dd0e63f95fb73c2d9af2e615eda07f5c6e67ea99c437f8b arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_interactionTrees_yield.receipt b/test/fixtures/support-receipts/demos_interactionTrees_yield.receipt @@ -1,234 +1,270 @@ arboricx.build-receipt.v1 -source 17faf5560459e4c0ad2aa1724846d73614dd71b68198684b0613aba49866f0d2 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 50691f992e348fb47baccc119c3809f7e33fdcce5c801b23ed8635962f346655 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output f276979eefa248d6af204a721b8737c057eacc3a27d211286db3d92bd36441f7 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output a605f6ba37ea84fedea127c610453bec9dfabf045e0834eb63742e4ed9267f9d +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +local handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bce8dbaff65ded636ca85f0a04f0cc7f5ed78e59c717990697239888dab5c1bc +local handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 88c5038b061b27c46603f7441133d67c6e21451f249a6015233bd1a4dde31b0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d pure dab5acd986e6ef2cfd473cdfda2291978bdd05d53d654568aee54a44035cb126 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bind b4edbddf65d1f40c6c6c069ddb37c1c763b47a1a57a303ac1ee00ae75594d2a0 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d request 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkedRequest 1c58f5c798102b7dbfe5ef63251298c3cdb35c7e47f2f24dc0d72db7581e9b51 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkWith 911a2190a2a0bf023ad23badeea4756df03a3f7f02139581b19bc0f2011011c6 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkRequest 583ccf6db95ddb1be9e6f5afc1ca98ae6b410945d3d1643084d896f1737c16a5 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d handle d17093c9654d57c134b6d0f00580c1710e552f34d7583b9a1e438eb073a22661 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d forward 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d label f4325f96264534084186bae09455f2a8060568d39e45d57692fa896ad4bf3138 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d fail ab8e873ce69d0305b9e50f7d0fd0c02503c09d352f4fe6f1a80b6f471724e7d8 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d liftIO 47be69da2dca13f8c6a3e3c9107cd843158b3bf576fbde3d156b1daf927dba64 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runPure 79d3a676f798e1691119cd442473d17a86e00bc26a56843f4715f70ba5886c37 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIO 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d collect 28b9fa70f3c2243be4e459f9bbc356893c2dcd2bcabdbd9106f09025cbd96546 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d view 989468627f11a7589c918e56fb01da459397726dda8c07c7808c30f1eb0f613f +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d diagnostic 0a05b217b6ff9aef546f4f6d8c4f699f6ad8fa5bb1a73fafdeebd60609f2a912 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d scopeV2 0df99b6d6d7b1f1559b83c2980e1e25c72a1c43b32b758c295a35ad23ce60f0a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d spawnV2 8043d72533c0d1b1ca655ea4ecd12bbafd5e89402b9cab9ef3071201a4fbf8b2 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIOV2 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 emit a6a2f37fe7003899b0524b86764b82e30844089d77821df7b3224c25024c23bb +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 info 68623a558ecdeae85e94bbc33fc7d7df4c22facc3e1c0866b59cc6034228b4c3 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 warn 06eff0e9177ff92b11a22fafd1595926b632c9e1733883b029f25a6beba0c4f1 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 withContext 48b6e19d4cdebb789b69332bf7414fee3c188978fedf233a929e9b5a6bedb810 +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 withSink 6b5180a9838e9b0bdb808828e874385f058bcd256ce4817dfdcabe4cc33dae7a +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 capture 9ad5e65ce08131170cab6963b1fc52ba942c7b025371ab415ac7b721db84aa6f +pin handler_log 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 discard 49d4e552a7ef1e30eaf2bb3d7bd4447a5d8508e04af35c9509c6e936bc954cee diff --git a/test/fixtures/support-receipts/demos_levelOrderTraversal.receipt b/test/fixtures/support-receipts/demos_levelOrderTraversal.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source 1278072b994e7b4f50b42f32ae7c94b09c9bea23a1c4aaee12ec2d14f2387cec -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output a83874999378a9ff9b15ea99ed7f51c90a2dc48b0fd6b7d8980a29ffa088582c -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/demos_patternMatching.receipt b/test/fixtures/support-receipts/demos_patternMatching.receipt @@ -1,10 +1,10 @@ arboricx.build-receipt.v1 source 82199ce4c4ba4c014c2cd09bdcc0644732c2f6863c0eff2710f4156dcb50a1e5 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 6ce77cdea44f0bbb9ff4b8e59b07b48b79a2aff349f07b6e9c57a225a0ae1f79 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local patterns 703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e 7d1e465f4c0732d87e843a2c3f08085c81f060a97b651181c06fa4127e1cc4f5 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local patterns 703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e 27a82746684d606eeb9b90f69e89981ea6bd589a5ed130ab2f25e424b90ad9a0 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/demos_runArboricxBundle.manifest b/test/fixtures/support-receipts/demos_runArboricxBundle.manifest @@ -1,3 +1,4 @@ arboricx.module-manifest.v1 -export runBundle arboricx.indexed-tree.v1 df7c48e4f4ddbd7347f7d5cb6e6d5b38c389700060e799e93d9ce589fb001df1 arboricx.abi.tree.v1 -export main arboricx.indexed-tree.v1 af6bf7060b5cf356a8ad15229989320520b6567e4a2e57c90787f15bca48407f arboricx.abi.tree.v1 +export main arboricx.indexed-tree.v1 73d5c0ba66fef7fa675b803660416b329199da6e3083177c4721c8b94d5dcd53 arboricx.abi.tree.v1 +export run arboricx.indexed-tree.v1 959ce918430762b8bc51840471f5f9c1da8d61c5398ee5dc579542a8632ec77a arboricx.abi.tree.v1 +export runBundle arboricx.indexed-tree.v1 d329c8f128cc79c626863fde837659e1081c6d301fbecf6904003a4445f1b1c9 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/demos_runArboricxBundle.receipt b/test/fixtures/support-receipts/demos_runArboricxBundle.receipt @@ -1,238 +1,253 @@ arboricx.build-receipt.v1 -source dba78c003920b0e9d459c94cbf929b29c440ddb9f1d7bf79d917ca68279c5850 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source e014061b2107cb0d8d1d2366742c8f35640c9a7dd85b80fc78d4afc7813bf0cc +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 8f0b54be481f1a6d123748689e5a4ee26b98f4f415d396056e62cc5c644ee608 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -local arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 0c9361d459580541fa1962a9c43d747a3564378beb2266b52f765465518517ea -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output cf6c20277f27afe0677b012920a10bf32a12929b8cfaa310620cf33143cd4ce1 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 fda4bfec1f3837c0e51c458ccb092419842b7189f850e58861760f1fc21b8b8f +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +local arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 129af3b31050fe21ec49c20d730bda8e66c485974f642a62cd0e68e7b4229116 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 readBytes a7ed7716ca188b830f261d06119648a929bafb8d896293234339c94bbc357d8c +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 hashBytes 3ea7a52e3e4dfc2941b5332d7fbb0c2b66fb72d36954351c00b263bb55cca6bc +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 publishBytes 2906ae1e2de27fd8b1fe2c71f3bfeb81ea5467d622a4f47b009179135452ec95 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 readTree f8a6d56fe51dc20d86f616c26fda2999d948e2232fc11c0553c3161afe700a69 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 publishTree b196ac6adb2ebeb4f51d9b6fe678b6c94e986b1d07690caba73af6a8844212d9 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 parentPath 5f34aa8a2a12d814575223fd7f5b077a692c1d2272a7572aae8b21bb9334189f +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae pin arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 readArboricxDecoded b59eefef9ea509792ac66aac8f9e770af09d58370dc34f3ffa2fcf72d1a4b673 pin arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 readArboricxBundle 78e9ef86e33bfa86a8eee8395cbb4cba9359c7408623c9fae23a1ad10f9b0ae7 pin arboricx 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 readArboricxExecutableByName a90d75ca259e957c031492e93de8d365a7cac3f54470c09d75484bf6391fa55f diff --git a/test/fixtures/support-receipts/demos_size.receipt b/test/fixtures/support-receipts/demos_size.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source b8d20a3ef0666e85684b34079e438ffbb1c7a7444b9c3ae6472cefd4fd21dcfd -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output c628107a60003ede5cac28ee6f6f20e2fc74d01f699924733a1c54001ebee934 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/demos_toSource.receipt b/test/fixtures/support-receipts/demos_toSource.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source a1d060c00dae207ae61cdbfb15027a585cd650e748b83cf5b5c231f02fe5e1b7 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 31148e782eb67ea2265ee9e0fd2a186ef2d2c24c1c8f38f98c0f3e8df955cb69 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/handler_artifacts.manifest b/test/fixtures/support-receipts/handler_artifacts.manifest @@ -0,0 +1,9 @@ +arboricx.module-manifest.v1 +export load arboricx.indexed-tree.v1 a328711380ff8a816961cbb63745d9056234c1d3895abbc5553a01e26cf4f731 arboricx.abi.tree.v1 +export loadFrom arboricx.indexed-tree.v1 31e7a145e15592233983722218d1cc06d9e05503597c7bc79c0fd119b2418d46 arboricx.abi.tree.v1 +export publish arboricx.indexed-tree.v1 b31d4d11fbb7e86e3873f9173194941eec340748a3c3551ce77d4fbbeb429494 arboricx.abi.tree.v1 +export publishTo arboricx.indexed-tree.v1 bff1bb880d4b22d21e52a0b22f3cf3ee7da5840e4518208adae2cae094d7f0dd arboricx.abi.tree.v1 +export withFilesystemV2 arboricx.indexed-tree.v1 76e916cab925bb60e90111c8ab500e0df8fe9a30dee40f7149508665ffb782fa arboricx.abi.tree.v1 +export withFilesystemForV2 arboricx.indexed-tree.v1 9c0765155fc6a0493ca3c4a604a7d3c12c1d4d4897045f2e6d5d1234fe017799 arboricx.abi.tree.v1 +export prepareFilesystemV2 arboricx.indexed-tree.v1 ac24c7d1b00cb8a3e4b38cd67c9ca389514c84092b49a45d168c1c7ce7193b29 arboricx.abi.tree.v1 +export publishStreamV2 arboricx.indexed-tree.v1 c9a57e8267ad1a3644b50f663cce0144546c531376ee6d02f765acba450ad41a arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/handler_artifacts.receipt b/test/fixtures/support-receipts/handler_artifacts.receipt @@ -0,0 +1,292 @@ +arboricx.build-receipt.v1 +source ee1531f86d1faaef4ce7b1ca9151bd2e7b10c3fc0574d0db67ddfce6c012c4e3 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f +output fc4e3d25dae3fb8aa8acfc06f05d1660455d1624fd31b25e6562e8769fe2c2d8 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bce8dbaff65ded636ca85f0a04f0cc7f5ed78e59c717990697239888dab5c1bc +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 fda4bfec1f3837c0e51c458ccb092419842b7189f850e58861760f1fc21b8b8f +local io_v2_artifacts 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 f3d1847ca412cffd8ab9840906cf7e28bbc64a7b613d7d592be3de6717012b8b +local arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d 9ba1f4a3231a3290ca4c02d19759079452f8ad0f3b7a05c83ac970c69732f067 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d pure dab5acd986e6ef2cfd473cdfda2291978bdd05d53d654568aee54a44035cb126 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bind b4edbddf65d1f40c6c6c069ddb37c1c763b47a1a57a303ac1ee00ae75594d2a0 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d request 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkedRequest 1c58f5c798102b7dbfe5ef63251298c3cdb35c7e47f2f24dc0d72db7581e9b51 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkWith 911a2190a2a0bf023ad23badeea4756df03a3f7f02139581b19bc0f2011011c6 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkRequest 583ccf6db95ddb1be9e6f5afc1ca98ae6b410945d3d1643084d896f1737c16a5 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d handle d17093c9654d57c134b6d0f00580c1710e552f34d7583b9a1e438eb073a22661 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d forward 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d label f4325f96264534084186bae09455f2a8060568d39e45d57692fa896ad4bf3138 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d fail ab8e873ce69d0305b9e50f7d0fd0c02503c09d352f4fe6f1a80b6f471724e7d8 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d liftIO 47be69da2dca13f8c6a3e3c9107cd843158b3bf576fbde3d156b1daf927dba64 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runPure 79d3a676f798e1691119cd442473d17a86e00bc26a56843f4715f70ba5886c37 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIO 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d collect 28b9fa70f3c2243be4e459f9bbc356893c2dcd2bcabdbd9106f09025cbd96546 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d view 989468627f11a7589c918e56fb01da459397726dda8c07c7808c30f1eb0f613f +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d diagnostic 0a05b217b6ff9aef546f4f6d8c4f699f6ad8fa5bb1a73fafdeebd60609f2a912 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d scopeV2 0df99b6d6d7b1f1559b83c2980e1e25c72a1c43b32b758c295a35ad23ce60f0a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d spawnV2 8043d72533c0d1b1ca655ea4ecd12bbafd5e89402b9cab9ef3071201a4fbf8b2 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIOV2 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 readBytes a7ed7716ca188b830f261d06119648a929bafb8d896293234339c94bbc357d8c +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 hashBytes 3ea7a52e3e4dfc2941b5332d7fbb0c2b66fb72d36954351c00b263bb55cca6bc +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 publishBytes 2906ae1e2de27fd8b1fe2c71f3bfeb81ea5467d622a4f47b009179135452ec95 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 readTree f8a6d56fe51dc20d86f616c26fda2999d948e2232fc11c0553c3161afe700a69 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 publishTree b196ac6adb2ebeb4f51d9b6fe678b6c94e986b1d07690caba73af6a8844212d9 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 parentPath 5f34aa8a2a12d814575223fd7f5b077a692c1d2272a7572aae8b21bb9334189f +pin io_v2_artifacts 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 write 3958b3975aea86f8e939f2d59d1f674f98a10db0feb7d45ee075c105fa68394f +pin io_v2_artifacts 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 publish 3a922deae98e27749857ff024d79a321093627d6d36656ec5048725073d1fa5d +pin io_v2_artifacts 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 publishWith 5524cf19c7627fdad48f0a28b8c89646b468429bb12cb8db5f82636a86f07124 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d pathJoin 829f14f27a485a4b6c46b43e7738745a7de75055f29d17a185eca46eba15f7da +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d objectDir 8a7f9fc2cdb834eab42af178576aa9208658f3050dc6358a18647170ef34713c +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d hashShard 1f3d07a8d7cd99b856642914d152f7ebea5755263480690b2922b6514a5e587b +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleObjectPath d640ad60309411f5ec38b4909f9f5ec21059cfa5f7e3a16d6f56d36af62f7624 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleBit? 65884ff45b474eace191b13b20f765a5c5d1ecc40e537c80ce0497af1e5bc636 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHexByte? 9c57b3fe1c129ee921946078e79855a5a753f1d08fd7c6576fa1c7ecb02d127e +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashBytes ee0ee6b5285b7dbaf28282c4c90df8dafbe7b6d8e9bd6a8ce32669ecb5b3c77e +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashFuel8 2288aaff65180213c9f2a0320465102cd4b7e23d08c4ceca63f66362f05b1c1b +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashFuel64 befaa254fea1150958c6834ad270fc6b558eae5df5f671998f66819db17e91b4 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashScan 231d71dd3ca456c9119d220caed59625f1e9e55b49905e79b2e42275444edc62 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashValid? 65a921dc150476b2c6cc91719ee5f6856e84fb5f87930407b63c6f606ecdeded +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashC 9e1ea088465773f583d1d6cb59b4b5bf1e97975bc8acba92b2d688f98fcea754 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d stripPrefix c5567e6c98ffe51fd7d9502efcec133c5665602b692bf197fa7557a3ced86705 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashPrefix 04ef2d1fce822846b3aae9b70795fdebcdcc8be1a841f35b04a16ac23b8a73e1 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundlePath 8c0b567fd42473cb50c21667e711d0a1c2a703088e9f419326186488174a83c0 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d healthPath c62b994110ddef7b756d7778ae2c1c8f598685733ea33dcc7a8870c14813f244 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleContentType 193f98476a4848c8a543af754a2c98ab842032db4c0721a22e21a607975d112a +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d htmlLandingPage 13e9d1c402f9008f18b36172d573fc1585e6303633b739ba10a265d3239037dd +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleResponse 2c2508dc1a9907a5d3907a297e15449f674c1df649d948208405e71ac66f00f8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packagePrefix 4863ca9af271ee8d26de1c3670e7f7aadd47f9f4de8b2a838ead8903a3604425 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageAlphaNum? 6fc7db89f4e88912c897ef3f334cdd598fe1f62e14a61e7be183827f4c269cd8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageByte? 540d4151153d268fd54cb13c31f237a23f95e37c1b1adc2c6bff96d87f9546f8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageVersion c315d31ca807d567a2031b0d268989b971bbdc3b970cacc79b095d6114cd00fb +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageName cbd8d3ecf8cbc5d455d4a7cd07d099984a5064f7b1dbb822c4e1d631f361c2d8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageTarget 523ebe158e800b99ef8d34b5d81335c67c6dc9dece752fe7140a8c79ff9c49de diff --git a/test/fixtures/support-receipts/handler_log.manifest b/test/fixtures/support-receipts/handler_log.manifest @@ -0,0 +1,8 @@ +arboricx.module-manifest.v1 +export emit arboricx.indexed-tree.v1 a6a2f37fe7003899b0524b86764b82e30844089d77821df7b3224c25024c23bb arboricx.abi.tree.v1 +export info arboricx.indexed-tree.v1 68623a558ecdeae85e94bbc33fc7d7df4c22facc3e1c0866b59cc6034228b4c3 arboricx.abi.tree.v1 +export warn arboricx.indexed-tree.v1 06eff0e9177ff92b11a22fafd1595926b632c9e1733883b029f25a6beba0c4f1 arboricx.abi.tree.v1 +export withContext arboricx.indexed-tree.v1 48b6e19d4cdebb789b69332bf7414fee3c188978fedf233a929e9b5a6bedb810 arboricx.abi.tree.v1 +export withSink arboricx.indexed-tree.v1 6b5180a9838e9b0bdb808828e874385f058bcd256ce4817dfdcabe4cc33dae7a arboricx.abi.tree.v1 +export capture arboricx.indexed-tree.v1 9ad5e65ce08131170cab6963b1fc52ba942c7b025371ab415ac7b721db84aa6f arboricx.abi.tree.v1 +export discard arboricx.indexed-tree.v1 49d4e552a7ef1e30eaf2bb3d7bd4447a5d8508e04af35c9509c6e936bc954cee arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/handler_log.receipt b/test/fixtures/support-receipts/handler_log.receipt @@ -0,0 +1,180 @@ +arboricx.build-receipt.v1 +source 4f64f9f4175b18c4e97caa24249d3bbe1261a57c6215de53c5ed4efe7fc546ab +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f +output 8e266f85d668a1f414dada9e4cca4d19a737bb1e19bf2a1ef985017175708c31 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bce8dbaff65ded636ca85f0a04f0cc7f5ed78e59c717990697239888dab5c1bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d pure dab5acd986e6ef2cfd473cdfda2291978bdd05d53d654568aee54a44035cb126 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bind b4edbddf65d1f40c6c6c069ddb37c1c763b47a1a57a303ac1ee00ae75594d2a0 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d request 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkedRequest 1c58f5c798102b7dbfe5ef63251298c3cdb35c7e47f2f24dc0d72db7581e9b51 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkWith 911a2190a2a0bf023ad23badeea4756df03a3f7f02139581b19bc0f2011011c6 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkRequest 583ccf6db95ddb1be9e6f5afc1ca98ae6b410945d3d1643084d896f1737c16a5 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d handle d17093c9654d57c134b6d0f00580c1710e552f34d7583b9a1e438eb073a22661 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d forward 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d label f4325f96264534084186bae09455f2a8060568d39e45d57692fa896ad4bf3138 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d fail ab8e873ce69d0305b9e50f7d0fd0c02503c09d352f4fe6f1a80b6f471724e7d8 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d liftIO 47be69da2dca13f8c6a3e3c9107cd843158b3bf576fbde3d156b1daf927dba64 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runPure 79d3a676f798e1691119cd442473d17a86e00bc26a56843f4715f70ba5886c37 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIO 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d collect 28b9fa70f3c2243be4e459f9bbc356893c2dcd2bcabdbd9106f09025cbd96546 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d view 989468627f11a7589c918e56fb01da459397726dda8c07c7808c30f1eb0f613f +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d diagnostic 0a05b217b6ff9aef546f4f6d8c4f699f6ad8fa5bb1a73fafdeebd60609f2a912 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d scopeV2 0df99b6d6d7b1f1559b83c2980e1e25c72a1c43b32b758c295a35ad23ce60f0a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d spawnV2 8043d72533c0d1b1ca655ea4ecd12bbafd5e89402b9cab9ef3071201a4fbf8b2 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIOV2 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a diff --git a/test/fixtures/support-receipts/handlers.manifest b/test/fixtures/support-receipts/handlers.manifest @@ -0,0 +1,20 @@ +arboricx.module-manifest.v1 +export pure arboricx.indexed-tree.v1 dab5acd986e6ef2cfd473cdfda2291978bdd05d53d654568aee54a44035cb126 arboricx.abi.tree.v1 +export bind arboricx.indexed-tree.v1 b4edbddf65d1f40c6c6c069ddb37c1c763b47a1a57a303ac1ee00ae75594d2a0 arboricx.abi.tree.v1 +export request arboricx.indexed-tree.v1 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 arboricx.abi.tree.v1 +export checkedRequest arboricx.indexed-tree.v1 1c58f5c798102b7dbfe5ef63251298c3cdb35c7e47f2f24dc0d72db7581e9b51 arboricx.abi.tree.v1 +export checkWith arboricx.indexed-tree.v1 911a2190a2a0bf023ad23badeea4756df03a3f7f02139581b19bc0f2011011c6 arboricx.abi.tree.v1 +export checkRequest arboricx.indexed-tree.v1 583ccf6db95ddb1be9e6f5afc1ca98ae6b410945d3d1643084d896f1737c16a5 arboricx.abi.tree.v1 +export handle arboricx.indexed-tree.v1 d17093c9654d57c134b6d0f00580c1710e552f34d7583b9a1e438eb073a22661 arboricx.abi.tree.v1 +export forward arboricx.indexed-tree.v1 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 arboricx.abi.tree.v1 +export label arboricx.indexed-tree.v1 f4325f96264534084186bae09455f2a8060568d39e45d57692fa896ad4bf3138 arboricx.abi.tree.v1 +export fail arboricx.indexed-tree.v1 ab8e873ce69d0305b9e50f7d0fd0c02503c09d352f4fe6f1a80b6f471724e7d8 arboricx.abi.tree.v1 +export liftIO arboricx.indexed-tree.v1 47be69da2dca13f8c6a3e3c9107cd843158b3bf576fbde3d156b1daf927dba64 arboricx.abi.tree.v1 +export runPure arboricx.indexed-tree.v1 79d3a676f798e1691119cd442473d17a86e00bc26a56843f4715f70ba5886c37 arboricx.abi.tree.v1 +export runIO arboricx.indexed-tree.v1 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a arboricx.abi.tree.v1 +export collect arboricx.indexed-tree.v1 28b9fa70f3c2243be4e459f9bbc356893c2dcd2bcabdbd9106f09025cbd96546 arboricx.abi.tree.v1 +export view arboricx.indexed-tree.v1 989468627f11a7589c918e56fb01da459397726dda8c07c7808c30f1eb0f613f arboricx.abi.tree.v1 +export diagnostic arboricx.indexed-tree.v1 0a05b217b6ff9aef546f4f6d8c4f699f6ad8fa5bb1a73fafdeebd60609f2a912 arboricx.abi.tree.v1 +export scopeV2 arboricx.indexed-tree.v1 0df99b6d6d7b1f1559b83c2980e1e25c72a1c43b32b758c295a35ad23ce60f0a arboricx.abi.tree.v1 +export spawnV2 arboricx.indexed-tree.v1 8043d72533c0d1b1ca655ea4ecd12bbafd5e89402b9cab9ef3071201a4fbf8b2 arboricx.abi.tree.v1 +export runIOV2 arboricx.indexed-tree.v1 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/handlers.receipt b/test/fixtures/support-receipts/handlers.receipt @@ -0,0 +1,235 @@ +arboricx.build-receipt.v1 +source 73cfe94509cdbc3c234bf4dabfac1796f5c6cf92565fcdd5a673a865bb8cd65b +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f +output 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 diff --git a/test/fixtures/support-receipts/http.manifest b/test/fixtures/support-receipts/http.manifest @@ -9,24 +9,21 @@ export statusLine arboricx.indexed-tree.v1 ce0da21e44a60d7d7c1b09c43142df1dcbfa3 export headerLine arboricx.indexed-tree.v1 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 arboricx.abi.tree.v1 export httpAppendResponseHeader arboricx.indexed-tree.v1 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 arboricx.abi.tree.v1 export buildResponse arboricx.indexed-tree.v1 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 arboricx.abi.tree.v1 -export response arboricx.indexed-tree.v1 6d37791d690e6d36d3ca8b5541391b6b629c1b90b5d3da2a4101610840b657b8 arboricx.abi.tree.v1 +export httpBodyLength arboricx.indexed-tree.v1 005020fe1d72f85901d6d1911c7865245972a2be15b070ed07415082b6a68ecc arboricx.abi.tree.v1 +export response arboricx.indexed-tree.v1 3a029a2a767699d24f1cb4bc4346dd3ccce2940ef4395eeb09b1f8fe89d44613 arboricx.abi.tree.v1 export emptyResponse arboricx.indexed-tree.v1 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de arboricx.abi.tree.v1 -export okResponse arboricx.indexed-tree.v1 1d039ba7479cfd48d6a4ed6fb194f16697c63e4c34338343f12a0e35c6c6c6c8 arboricx.abi.tree.v1 -export textResponse arboricx.indexed-tree.v1 1d039ba7479cfd48d6a4ed6fb194f16697c63e4c34338343f12a0e35c6c6c6c8 arboricx.abi.tree.v1 -export jsonResponse arboricx.indexed-tree.v1 efa5ea017979655879b4be69b5a08251035283f4678e5ffbd5e9fd933d7e590c arboricx.abi.tree.v1 -export htmlResponse arboricx.indexed-tree.v1 9a1fa84e4e9147107b3c2b36756638f9e847163fba25e502951eb64de1d6a5fc arboricx.abi.tree.v1 -export createdResponse arboricx.indexed-tree.v1 8a3469d3a2cce957b1571d4f84881d13f41d1e58db82d8d5f33bd3baaf961235 arboricx.abi.tree.v1 +export okResponse arboricx.indexed-tree.v1 e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 arboricx.abi.tree.v1 +export textResponse arboricx.indexed-tree.v1 e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 arboricx.abi.tree.v1 +export jsonResponse arboricx.indexed-tree.v1 2abfe36d0337304c8c78bcb87e2f132db6d6acf2385e10cd3e1a92336c447acb arboricx.abi.tree.v1 +export htmlResponse arboricx.indexed-tree.v1 5aaa86ca299dc2ce72f64a5abfc4e07ca0e949bd751669ed8171d4995152725b arboricx.abi.tree.v1 +export createdResponse arboricx.indexed-tree.v1 22d380835f4efd58877c625e8de7f08dd5dc1b5b6b30fdf6553e533116166bff arboricx.abi.tree.v1 export notFoundResponse arboricx.indexed-tree.v1 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 arboricx.abi.tree.v1 -export badRequestResponse arboricx.indexed-tree.v1 674fe3821cd49c8a73c6f6f3abfe3004ca33b393de879969e3b768bf6abfbc6a arboricx.abi.tree.v1 -export errorResponse arboricx.indexed-tree.v1 a83633de4defc0b4a70603c6b5d0fdf57be6567db629ab9347a7cbd7fd13cb9f arboricx.abi.tree.v1 +export badRequestResponse arboricx.indexed-tree.v1 1b533e6b41f50c40a6d0e61d2e5661ede925fd4ad473f016b47186f4e9522097 arboricx.abi.tree.v1 +export errorResponse arboricx.indexed-tree.v1 7b6e86847a366c23c27ad86f767947425bf04a987b5f48451f8d714e102c22b5 arboricx.abi.tree.v1 export headerEndState arboricx.indexed-tree.v1 e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 arboricx.abi.tree.v1 export httpHeadersOnly arboricx.indexed-tree.v1 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 arboricx.abi.tree.v1 export headersOnly arboricx.indexed-tree.v1 f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 arboricx.abi.tree.v1 export responseForMethod arboricx.indexed-tree.v1 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 arboricx.abi.tree.v1 -export httpRecvUntil arboricx.indexed-tree.v1 6dea4b3cdf235c3a453e51049cc4fcb3ee3a0216e0dbe0a6987b43a33355db5f arboricx.abi.tree.v1 -export recvUntilMax arboricx.indexed-tree.v1 aab00d1f8d9ec906fb402fbfa3ef5cb9ab301bec0d6b5d609c9fb4e17ccf430c arboricx.abi.tree.v1 -export recvUntil arboricx.indexed-tree.v1 3ab70212ad49e37e8c6a29e393a9e5790d3bda04b5b56fca1ced5932bca79a43 arboricx.abi.tree.v1 -export recvHeaders arboricx.indexed-tree.v1 6c9519ee8c6c45ef98745ad799df591dc46acbf8eaa6219659bf03de38fbfeff arboricx.abi.tree.v1 export httpLineError arboricx.indexed-tree.v1 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e arboricx.abi.tree.v1 export httpScanLine arboricx.indexed-tree.v1 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 arboricx.abi.tree.v1 export httpLineReply arboricx.indexed-tree.v1 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a arboricx.abi.tree.v1 @@ -51,24 +48,11 @@ export parseContentLengthValue arboricx.indexed-tree.v1 330f1d204f021be9370b7fb0 export httpSelectLength arboricx.indexed-tree.v1 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca arboricx.abi.tree.v1 export contentLength arboricx.indexed-tree.v1 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 arboricx.abi.tree.v1 export bodyReadState arboricx.indexed-tree.v1 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 arboricx.abi.tree.v1 -export takeBodyBytes arboricx.indexed-tree.v1 6996dc93fdff652211977ed68ef5db211c3df7a1db86c958035db653c1b228a7 arboricx.abi.tree.v1 +export takeBodyBytes arboricx.indexed-tree.v1 303837e8c16c2b3e76f4076ceb28e22ead58735b4796d72ad185fa22c01aa97e arboricx.abi.tree.v1 export httpRecvBucket arboricx.indexed-tree.v1 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 arboricx.abi.tree.v1 export recvChunkMax4096 arboricx.indexed-tree.v1 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 arboricx.abi.tree.v1 -export httpReadBodyState arboricx.indexed-tree.v1 8d477bdab5f8db48905a5f6b8c19bc69654a0bad367d2fe1680303e7a28b53c7 arboricx.abi.tree.v1 -export readBodyMore arboricx.indexed-tree.v1 6d75303c7d3fdda892b2b276dbb96c01019b1c25b8153f695e6c799c03c40355 arboricx.abi.tree.v1 -export readBodyExact arboricx.indexed-tree.v1 dda235ff519ff0ac146c9e170d18f218a80e829c8cc034d2e7825e6c23d1506a arboricx.abi.tree.v1 -export readBody arboricx.indexed-tree.v1 b5280a0a7555b439dd5ce42bf592a165da51f95398087ea43774d6b45510fddc arboricx.abi.tree.v1 export validMethod? arboricx.indexed-tree.v1 ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 arboricx.abi.tree.v1 export validVersion? arboricx.indexed-tree.v1 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed arboricx.abi.tree.v1 export validTarget? arboricx.indexed-tree.v1 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa arboricx.abi.tree.v1 export validateRequest arboricx.indexed-tree.v1 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 arboricx.abi.tree.v1 export routerMethod arboricx.indexed-tree.v1 c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c arboricx.abi.tree.v1 -export respondAndClose arboricx.indexed-tree.v1 467c1b022f83b6fd3fb3e9fb7ccc6cd78d15c2b9b8a936987088c7c46639aebf arboricx.abi.tree.v1 -export httpRespond arboricx.indexed-tree.v1 4c15cdb6bd52d2a638c93cc3719374cf4eb91e53eac55c58195b1ac56bdbe540 arboricx.abi.tree.v1 -export httpHandleBody arboricx.indexed-tree.v1 55672ac8616e621c775081a59ad851ff50fcb247dd11d31cf773875fb7fa1c55 arboricx.abi.tree.v1 -export httpHandleHeaders arboricx.indexed-tree.v1 d21b4ed9b21aa5af60b775ad96959bb15648a5b19171691d555f3c71f8f90d6b arboricx.abi.tree.v1 -export httpHandleRequest arboricx.indexed-tree.v1 939d41afd142bc74e6a4ee1b0cbde8cfa5d06c87c29ebd9ce68aaf161d98e586 arboricx.abi.tree.v1 -export httpHandleReceived arboricx.indexed-tree.v1 e40bd55458aa83659301d11b22764e03d38567791a23a472d33fb981974260e6 arboricx.abi.tree.v1 -export httpHandlerIO arboricx.indexed-tree.v1 ba715371563139d668ec9ec2afc8bb27407d4a6795cd449dd87afa11a9e698f4 arboricx.abi.tree.v1 -export httpPureRouter arboricx.indexed-tree.v1 373f2de1bdb7cb038c91aa75737a3abfc95c2655ac4bc1d4a89b6739f73c0849 arboricx.abi.tree.v1 -export httpHandler arboricx.indexed-tree.v1 777fe83b3bf5ebb3b36d9a10d202ce381e1c488beb6667f70dd9f58961e4352a arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/http.receipt b/test/fixtures/support-receipts/http.receipt @@ -1,13 +1,11 @@ arboricx.build-receipt.v1 -source d93ca3d550c9d177d9968bd65b0aad0aa4a31f20b8274cfadcaae778f20f1cb4 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 8338f32bdf08b7743933809b2acce42899d0020e83c95cd0b4d25b0a8b5c07df +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output e6195e968697ca5799891bcd9096b568dceb717ea6e2b14adbdb8ca041a01691 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -local socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 4d83b01a38a451728c81488e3ad4ddff15bf982cad2d743694203583184410cf -local parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d 271e1e5144ed8b4eecc317cf9f8a952f07f83fb62a2966ce5e0437011c508763 -local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 3c2b355c055b99e9555b359ab75d92f95deb68ab17b893ecc79245b3f80d4b0f +output b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d dd9c680147cd7887a7a969d516bc895843c165abafc8f342e618659858ff6d21 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 @@ -172,103 +170,6 @@ pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 byt pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 socket d0962df2eb672cddf52b8ddf4a76f643a76280b4905c97495efb4af859265516 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocket 4b5cf72937d3fc20f6b23496986227b44a1dcaa1d18a17d74da2a3dcb01d9b6b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 bindSocket 007c736fd19010f007eef45cc4d649b5de7b0f77d042a6c17e4eaea6ccfd007f -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 listen 960de6adb3cce7b002e915e47f405294bf3687d724faff2a6ca5f8926e7d7d93 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 accept d5af3a6c23868f6c35b58488dee362395d7e1d54b94915432f2c1f221b5e7be3 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 connect 5ae01fe00d755ca83680ab01b27ddbaa9f4713b3832de18d714d07c7c14a4317 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 recv acaf941bcc0b17a09d151ed9cf70c23ea3f33a897ce99dbdd6ef5e354bc5514b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 send 641b2ed091cb6eddc1dfd51038f21ac0afaf8e6122440f40b709ab63c8c9d953 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 getSocketName 9ed5573f5c861837f24b973c84a456d5a47081cd000ebf71e2d1ce0995463468 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSocket d2dfa18c4c469383d96d14ff08646db8b345f1bad52d8772b9b59e2b5225315b -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onBindSocket ae3e99d9ebed09010981a11eb788cb572cef63a4ca625d86f7196fde97527530 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onListen 5a6660028564f14fbe8bd68e0574ae31c4eb254064169438957864d62bf16868 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onAccept ea248480f5681c96d255c317a0783dc98acd028c8ec07d324ac2ae02a764374d -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onConnect 5ba2194bee14d9fad3c282147bebd0c03bc55b76c518bb684350c7901bc296c9 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onRecv d5f6bf951f02d8415354bbab1e720d1d87e3d39caf26dc6a0b9801cd03b42ed2 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSend 112d48a3a067379fcfaa56e894ffe0aaf02e3f3fc44bc2fe8472f0d49a47aeca -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onGetSocketName 99d9c9dff1637310ff2c9a001cd718a36cccc03a18d84a3294f2ab7874af1f76 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSocket_ 3c394ed1369781ee2254ded5956687c27971c495548b6278e5e62efd757370a6 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onBindSocket_ 7c91b3aabf7daffd7da4e525db14be852b1e3b874212d7da444467d6de0585ed -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onListen_ e9f2c57f616bed580f21e58bd01b327d4615680d6209574cfeb6a7da85758025 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onAccept_ e4b93ffb876803acb6700e33127436cc53f348efbd0d422ebca09a3371b1614e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onConnect_ e0baf23e45038091b6c6ccea70941dd3fdf96128f94a595ae0f320f095979025 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onRecv_ dc391a9c916186a16974abf368dd23d0bdb3fced7b5f3cffe8a655aa41f230ea -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onSend_ c746325d45b9a88f5519a5399bc784bd7b3eb5ba53a1aa37f6030e64b0d109bd -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 onGetSocketName_ 31d562c8271942cd7eadefdde5bac73abbcdbcbf63ffc4e9d83b68ce39929988 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocket_ e10c012b9860dd7629b768fcb75126eff626b2f80ea1a0095ee2efcaa68b304e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 closeSocketOnError 21d850f11f110d1bdb7d4cabd44913473f997337ded0df8b79e3f94034c0bdf0 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 listenSocket 8a248c717b8216370b40ddc355851282362dd2bc130df5adf5f8f0aa2a3d9dcc -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 withAccepted 7838f3c8b28ede95756dd9ca6283db1a3519bb95e0b19ab3bcfcabedabb1c900 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 withAccepted_ b33b77bfe9094c3938bbc8684d668da222d2b461cf020fe927f848c1bb4c26a7 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveOnce 2e39131bc2e390022e72a22235cde46ecf0a0b54a46618719ccaab660acbc306 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveForkingOnce 1194e71d76856c2a519ef62c0197d4399fa30ece80ff756eb1419f57197aa927 -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 serveForever ac87d6be311b022bce24cbbb1f75b5e80db4f672c04d695afb800e81cd1dc95e -pin socket 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 connectTo 1c2bee16cdfaa4046a2f173d0210f26c0eb48f356dafa00a84cec4e34f553bcb pin parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d parserDiagnostic 788973e5607ecbf8e4ff728d1b902187e8b911f5706a3bd6bb71d84c0d0a5b8e pin parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d parserDiagnosticCode 3ec371c2c643a6d27efa4037d67ee1ece8bcd2fce30e914ed8f718c09f26dd8a pin parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d parserDiagnosticExpected 110f1681320cfb98353d697325a848cb3db6e01a4ae4f2ab74e51652dfa29cde diff --git a/test/fixtures/support-receipts/intensional.receipt b/test/fixtures/support-receipts/intensional.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 source 4b9470311bb8984bf311371c6b1c056ac971e073c60be5a433a5826780c31d38 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 7467e74f0c5b7dfad8d6a14d34171b2b0bcd7750423894e2ca920e0535fde7d1 -local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 2b3ccb358f6e262bbb6f9a28c17c9c96a8e3a07a162d4069a8226c9208be03f3 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/io.manifest b/test/fixtures/support-receipts/io.manifest @@ -0,0 +1,75 @@ +arboricx.module-manifest.v1 +export io arboricx.indexed-tree.v1 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc arboricx.abi.tree.v1 +export pure arboricx.indexed-tree.v1 c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 arboricx.abi.tree.v1 +export bind arboricx.indexed-tree.v1 f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 arboricx.abi.tree.v1 +export fail arboricx.indexed-tree.v1 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 arboricx.abi.tree.v1 +export scope arboricx.indexed-tree.v1 cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 arboricx.abi.tree.v1 +export currentScope arboricx.indexed-tree.v1 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 arboricx.abi.tree.v1 +export spawn arboricx.indexed-tree.v1 bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 arboricx.abi.tree.v1 +export join arboricx.indexed-tree.v1 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 arboricx.abi.tree.v1 +export discard arboricx.indexed-tree.v1 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 arboricx.abi.tree.v1 +export cancel arboricx.indexed-tree.v1 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f arboricx.abi.tree.v1 +export cancelled arboricx.indexed-tree.v1 f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 arboricx.abi.tree.v1 +export request arboricx.indexed-tree.v1 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f arboricx.abi.tree.v1 +export capabilities arboricx.indexed-tree.v1 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a arboricx.abi.tree.v1 +export inspect arboricx.indexed-tree.v1 d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 arboricx.abi.tree.v1 +export stdin arboricx.indexed-tree.v1 a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 arboricx.abi.tree.v1 +export stdout arboricx.indexed-tree.v1 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f arboricx.abi.tree.v1 +export stderr arboricx.indexed-tree.v1 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 arboricx.abi.tree.v1 +export openReader arboricx.indexed-tree.v1 a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 arboricx.abi.tree.v1 +export openWriter arboricx.indexed-tree.v1 c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a arboricx.abi.tree.v1 +export openAppender arboricx.indexed-tree.v1 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f arboricx.abi.tree.v1 +export openUpdate arboricx.indexed-tree.v1 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 arboricx.abi.tree.v1 +export seekFile arboricx.indexed-tree.v1 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 arboricx.abi.tree.v1 +export filePosition arboricx.indexed-tree.v1 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 arboricx.abi.tree.v1 +export syncFile arboricx.indexed-tree.v1 c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 arboricx.abi.tree.v1 +export syncDirectory arboricx.indexed-tree.v1 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 arboricx.abi.tree.v1 +export fileSize arboricx.indexed-tree.v1 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa arboricx.abi.tree.v1 +export resizeFile arboricx.indexed-tree.v1 b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 arboricx.abi.tree.v1 +export statPath arboricx.indexed-tree.v1 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 arboricx.abi.tree.v1 +export lstatPath arboricx.indexed-tree.v1 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 arboricx.abi.tree.v1 +export removeFile arboricx.indexed-tree.v1 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 arboricx.abi.tree.v1 +export renamePath arboricx.indexed-tree.v1 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 arboricx.abi.tree.v1 +export createDirectory arboricx.indexed-tree.v1 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 arboricx.abi.tree.v1 +export removeDirectory arboricx.indexed-tree.v1 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 arboricx.abi.tree.v1 +export openDirectory arboricx.indexed-tree.v1 c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc arboricx.abi.tree.v1 +export nextDirectory arboricx.indexed-tree.v1 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc arboricx.abi.tree.v1 +export ipv4 arboricx.indexed-tree.v1 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa arboricx.abi.tree.v1 +export ipv6 arboricx.indexed-tree.v1 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 arboricx.abi.tree.v1 +export resolveTCP arboricx.indexed-tree.v1 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead arboricx.abi.tree.v1 +export listenTCP arboricx.indexed-tree.v1 d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d arboricx.abi.tree.v1 +export connectTCP arboricx.indexed-tree.v1 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 arboricx.abi.tree.v1 +export acceptTCP arboricx.indexed-tree.v1 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca arboricx.abi.tree.v1 +export localAddress arboricx.indexed-tree.v1 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf arboricx.abi.tree.v1 +export peerAddress arboricx.indexed-tree.v1 b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 arboricx.abi.tree.v1 +export shutdownWrite arboricx.indexed-tree.v1 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 arboricx.abi.tree.v1 +export readIndexedTree arboricx.indexed-tree.v1 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f arboricx.abi.tree.v1 +export hashIndexedTree arboricx.indexed-tree.v1 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd arboricx.abi.tree.v1 +export writeIndexedTree arboricx.indexed-tree.v1 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 arboricx.abi.tree.v1 +export verifyIndexedTree arboricx.indexed-tree.v1 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 arboricx.abi.tree.v1 +export encodeUTF8 arboricx.indexed-tree.v1 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f arboricx.abi.tree.v1 +export decodeUTF8 arboricx.indexed-tree.v1 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 arboricx.abi.tree.v1 +export readSome arboricx.indexed-tree.v1 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 arboricx.abi.tree.v1 +export writeSome arboricx.indexed-tree.v1 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 arboricx.abi.tree.v1 +export close arboricx.indexed-tree.v1 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd arboricx.abi.tree.v1 +export openStage arboricx.indexed-tree.v1 c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 arboricx.abi.tree.v1 +export sealStage arboricx.indexed-tree.v1 dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b arboricx.abi.tree.v1 +export publishNew arboricx.indexed-tree.v1 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 arboricx.abi.tree.v1 +export publishReplace arboricx.indexed-tree.v1 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 arboricx.abi.tree.v1 +export openDigest arboricx.indexed-tree.v1 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 arboricx.abi.tree.v1 +export updateDigest arboricx.indexed-tree.v1 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 arboricx.abi.tree.v1 +export finishDigest arboricx.indexed-tree.v1 b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 arboricx.abi.tree.v1 +export onOk arboricx.indexed-tree.v1 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 arboricx.abi.tree.v1 +export ensureDirectory arboricx.indexed-tree.v1 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 arboricx.abi.tree.v1 +export connectCandidates arboricx.indexed-tree.v1 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea arboricx.abi.tree.v1 +export connectFirstTCP arboricx.indexed-tree.v1 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b arboricx.abi.tree.v1 +export require arboricx.indexed-tree.v1 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 arboricx.abi.tree.v1 +export dropWritten arboricx.indexed-tree.v1 f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 arboricx.abi.tree.v1 +export writeAll arboricx.indexed-tree.v1 ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be arboricx.abi.tree.v1 +export copyStream arboricx.indexed-tree.v1 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a arboricx.abi.tree.v1 +export monotonicNanoseconds arboricx.indexed-tree.v1 f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e arboricx.abi.tree.v1 +export sleepMilliseconds arboricx.indexed-tree.v1 f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 arboricx.abi.tree.v1 +export yield arboricx.indexed-tree.v1 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 arboricx.abi.tree.v1 +export spawnProcess arboricx.indexed-tree.v1 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 arboricx.abi.tree.v1 +export workingDirectory arboricx.indexed-tree.v1 ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 arboricx.abi.tree.v1 +export environmentVariable arboricx.indexed-tree.v1 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/io.receipt b/test/fixtures/support-receipts/io.receipt @@ -0,0 +1,80 @@ +arboricx.build-receipt.v1 +source c7c5c3133cf91206d8bdb13fd6972aed4bca3fae8b16308bafc6e25236c2cd0c +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f +output 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 diff --git a/test/fixtures/support-receipts/io_v2_artifacts.manifest b/test/fixtures/support-receipts/io_v2_artifacts.manifest @@ -0,0 +1,4 @@ +arboricx.module-manifest.v1 +export write arboricx.indexed-tree.v1 3958b3975aea86f8e939f2d59d1f674f98a10db0feb7d45ee075c105fa68394f arboricx.abi.tree.v1 +export publish arboricx.indexed-tree.v1 3a922deae98e27749857ff024d79a321093627d6d36656ec5048725073d1fa5d arboricx.abi.tree.v1 +export publishWith arboricx.indexed-tree.v1 5524cf19c7627fdad48f0a28b8c89646b468429bb12cb8db5f82636a86f07124 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/io_v2_artifacts.receipt b/test/fixtures/support-receipts/io_v2_artifacts.receipt @@ -0,0 +1,374 @@ +arboricx.build-receipt.v1 +source 811886e206ba2912893ebba28afe4a4085b8838fc5a097b165ffb56637c93524 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f +output 45af65caa639245e794d72e49510cbca63274fc885fa7b2e1afcffda3c5897b3 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 +local http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 85e6f4f5df9e72ca19d0d1ba874262b7cd277d1854a295790d707b184fc6f0c8 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 fda4bfec1f3837c0e51c458ccb092419842b7189f850e58861760f1fc21b8b8f +local handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bce8dbaff65ded636ca85f0a04f0cc7f5ed78e59c717990697239888dab5c1bc +local arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d 9ba1f4a3231a3290ca4c02d19759079452f8ad0f3b7a05c83ac970c69732f067 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db compare 25d460f10cf8893795a8a5eac6a951b40459ef90e35f6945f72c3f53868f94f5 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db equal 07e8af1f8ef6366cab90420cf1ec97a15b434ba4ee3966af235579a05935180e +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lt 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lte a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gt d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gte a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db inRange 62a3f72adbb9030425f0e63e43568c0eae069d7293763fab3a83123edef50aaa +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db minimum f515c4e49514b4631ca183a2a2dfcfce46d5ef8a8338a09496ce9a0ca2f6502c +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db maximum dfcf25622f229c0ae04441aae5884a38d0ba046ec512c8ac1fde56290657938b +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db zero 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db odd 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db even 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db successor 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db predecessor dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db add 066aebbf5d3b48d5e6c9c35b5c59e9cc5172f47d09899684475738eafb4fbe4a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db subtract a5a9212b766933b7c8f07b3f98f7d9d789d0ae45e1301042da712fefa3dd0a3a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db multiply 320a41f19797fbb9a05d6615b53be543b7acec4e3e535ad81c8f1e5f16db3de0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db divMod 068932c208504ea9a547086ba761d6975ee3173d495b80addc056999b7fc557f +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db quotient bdfe626a727affba89d4c11e489eca0086f4c42facfb4ee9f248e69a47abed8a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db remainder 80f8dd39e11423480ce2f5f5edde99104824bab147a88a2c331c8f643a0e6817 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db power 3b59e090c4ea2d7966c2bc7bf21c9ed67940c1c7a41f01f6e04db93a69ca54a1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db decimal 644d54084c9734a5b58ebc7d9bb973eea48890665db1617679db9bd1c8bd68b0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db ascii e0df024c0168229497859a5e770f7e36594d9d266c3933102d1fa1997d4b3225 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db digit eed46692dadce061198cf3cafd6c00bbc5561d9fb637706d16bde41552a1b9bd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db letter ddca67e3c882ecee1239775aecf0d31b8ce136f86e6a6eea70e5d8b5979b0b30 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lowerHex 3b752ef4b4b89ef102dc4c43110ee8522922dbaadf77495713fc465d9e718eb4 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db printable 9c88d7eaba58c0771da373821f7223c298bea7697647f03f6b1a4d922ff8a6e5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxHeaderBytes e62a642ffd71dff46c4c03641e08f13490469e84bbd0815403b577a0f4916f90 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxBodyBytes 40a6c6220b30ead48df9abc0cc7f1b6202197de18d97434061fc1d4ccd6d1292 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 crlfcrlf f77e4d0100d99afad2fa15decbeb38aae4ed8c3135d5ea9c2e5a44537194a871 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrases 0bcd79d7b0f770431eee50a6be0f938b32458a41f7f57ba9b2edb56baf607e97 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 lookupStatusPhrase ad3e569281eedcff04104de5e3da2170f57046bdb8528857b92e9d4dcb78f97d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrase 67d1a009c420ebc91c9ba7533209c485bb9bbd4c7249f4ac48be6b5e598f809d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusLine ce0da21e44a60d7d7c1b09c43142df1dcbfa38b5a6830ffcd3d0a7704cae67af +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerLine 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpAppendResponseHeader 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 buildResponse 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyLength 005020fe1d72f85901d6d1911c7865245972a2be15b070ed07415082b6a68ecc +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 response 3a029a2a767699d24f1cb4bc4346dd3ccce2940ef4395eeb09b1f8fe89d44613 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 emptyResponse 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 okResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 textResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 jsonResponse 2abfe36d0337304c8c78bcb87e2f132db6d6acf2385e10cd3e1a92336c447acb +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 htmlResponse 5aaa86ca299dc2ce72f64a5abfc4e07ca0e949bd751669ed8171d4995152725b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 createdResponse 22d380835f4efd58877c625e8de7f08dd5dc1b5b6b30fdf6553e533116166bff +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 notFoundResponse 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 badRequestResponse 1b533e6b41f50c40a6d0e61d2e5661ede925fd4ad473f016b47186f4e9522097 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 errorResponse 7b6e86847a366c23c27ad86f767947425bf04a987b5f48451f8d714e102c22b5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerEndState e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeadersOnly 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headersOnly f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 responseForMethod 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineError 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpScanLine 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineReply 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBadRequest e11a0cdf150e1784e00dcebff8906c033c6695ebc3e7c5716b72715dde8695a6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 readLineBytes 7bed187a6276169e2eac95cdbb4f2fc53f3625dc6b183b4b9e56e0c5957c34b6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpNonSpace? f5f2f658784d8ded1ec828967e19324ffd4fd2438e5a6ebcad0a6c07f9feb908 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpThreeWordsReply 5a71c4b351610b15374829ba12e3f6a5f793760c5927d414c944d1060df60161 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseThreeWords 1f04df81381567601348b1442947ed8792f3dd0424eb9393b3c147f33493d94a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseRequestLine ace457a20b9f1ee3e757a622f3ba03b191873e4d4835cc7ebdfd947332561769 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 toLowerAsciiByte 4a9d74221452385910ba4d4b87cd60d31efe31132b056f32777ebeec6b457d9e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTokenByte? d0c894a224efa6fb17498ee6791f4598a26e38419b51d099af9e80035e3cf85e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpOws? 14e51851f694336c78a7a25b88289c6687b8e0a19af8b220c3a027d5270522c2 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpFieldByte? b7b24f9ed6ee56a602e896fde3aee4c07fde3dee849b608fa451128d5ce4d196 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTrimOws 7ab13fefc873a02011c645a56f65ca119806b1f7ab740e6bf56d1fb02ec7067c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeaderLineReply 962626d5659cce124775e1b8d98482db74433856dc2701767286121681223c56 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpParseHeaderLines 382f455876fd6d1fb704e3d96a1d33bb569211d3e6de1ccb604337266c0ba744 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseHeaders a0fe7e14eacc4024e5812aa0443a8345954ff7db342218db523258cee87aa39b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyTooLarge a35fe61177a0ecce71ff585252f6df5f81e9201ad24e1c238e034c5500ef4d60 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthOverflow 4a4e6626a22d3ee2d2d435940b99e86e0589945c66b3cf185f4fa64ff32b3e76 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthDigits 5c312192ada0032aadbe847e6c6164a786deb66a8ae0e0adc6af140e4791da6b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseContentLengthValue 330f1d204f021be9370b7fb01e3f11703a860532ce46f6f276e0f45d4f59356c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpSelectLength 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 contentLength 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 bodyReadState 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 takeBodyBytes 303837e8c16c2b3e76f4076ceb28e22ead58735b4796d72ad185fa22c01aa97e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpRecvBucket 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 recvChunkMax4096 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validMethod? ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validVersion? 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validTarget? 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validateRequest 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 routerMethod c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 readBytes a7ed7716ca188b830f261d06119648a929bafb8d896293234339c94bbc357d8c +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 hashBytes 3ea7a52e3e4dfc2941b5332d7fbb0c2b66fb72d36954351c00b263bb55cca6bc +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 publishBytes 2906ae1e2de27fd8b1fe2c71f3bfeb81ea5467d622a4f47b009179135452ec95 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 readTree f8a6d56fe51dc20d86f616c26fda2999d948e2232fc11c0553c3161afe700a69 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 publishTree b196ac6adb2ebeb4f51d9b6fe678b6c94e986b1d07690caba73af6a8844212d9 +pin io_v2_storage e2c2fc2efa773eec6c4b0e1cd7e800137eaa2d3d2d698fea2b1ea1f07edb53c4 parentPath 5f34aa8a2a12d814575223fd7f5b077a692c1d2272a7572aae8b21bb9334189f +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d pure dab5acd986e6ef2cfd473cdfda2291978bdd05d53d654568aee54a44035cb126 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d bind b4edbddf65d1f40c6c6c069ddb37c1c763b47a1a57a303ac1ee00ae75594d2a0 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d request 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkedRequest 1c58f5c798102b7dbfe5ef63251298c3cdb35c7e47f2f24dc0d72db7581e9b51 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkWith 911a2190a2a0bf023ad23badeea4756df03a3f7f02139581b19bc0f2011011c6 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d checkRequest 583ccf6db95ddb1be9e6f5afc1ca98ae6b410945d3d1643084d896f1737c16a5 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d handle d17093c9654d57c134b6d0f00580c1710e552f34d7583b9a1e438eb073a22661 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d forward 5a31b868bac385567348c3e122b389eba7ef5ca043a8fc061d8499431a1f8ed3 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d label f4325f96264534084186bae09455f2a8060568d39e45d57692fa896ad4bf3138 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d fail ab8e873ce69d0305b9e50f7d0fd0c02503c09d352f4fe6f1a80b6f471724e7d8 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d liftIO 47be69da2dca13f8c6a3e3c9107cd843158b3bf576fbde3d156b1daf927dba64 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runPure 79d3a676f798e1691119cd442473d17a86e00bc26a56843f4715f70ba5886c37 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIO 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d collect 28b9fa70f3c2243be4e459f9bbc356893c2dcd2bcabdbd9106f09025cbd96546 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d view 989468627f11a7589c918e56fb01da459397726dda8c07c7808c30f1eb0f613f +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d diagnostic 0a05b217b6ff9aef546f4f6d8c4f699f6ad8fa5bb1a73fafdeebd60609f2a912 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d scopeV2 0df99b6d6d7b1f1559b83c2980e1e25c72a1c43b32b758c295a35ad23ce60f0a +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d spawnV2 8043d72533c0d1b1ca655ea4ecd12bbafd5e89402b9cab9ef3071201a4fbf8b2 +pin handlers 15b8d4a4f7aacbc639352316bfb62a6e275f773eb270f7d6a2369bf00f8b1a6d runIOV2 25cef5504930dc95db45759701879b55359202ed2b7534e222b32136873a636a +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d pathJoin 829f14f27a485a4b6c46b43e7738745a7de75055f29d17a185eca46eba15f7da +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d objectDir 8a7f9fc2cdb834eab42af178576aa9208658f3050dc6358a18647170ef34713c +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d hashShard 1f3d07a8d7cd99b856642914d152f7ebea5755263480690b2922b6514a5e587b +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleObjectPath d640ad60309411f5ec38b4909f9f5ec21059cfa5f7e3a16d6f56d36af62f7624 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleBit? 65884ff45b474eace191b13b20f765a5c5d1ecc40e537c80ce0497af1e5bc636 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHexByte? 9c57b3fe1c129ee921946078e79855a5a753f1d08fd7c6576fa1c7ecb02d127e +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashBytes ee0ee6b5285b7dbaf28282c4c90df8dafbe7b6d8e9bd6a8ce32669ecb5b3c77e +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashFuel8 2288aaff65180213c9f2a0320465102cd4b7e23d08c4ceca63f66362f05b1c1b +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashFuel64 befaa254fea1150958c6834ad270fc6b558eae5df5f671998f66819db17e91b4 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashScan 231d71dd3ca456c9119d220caed59625f1e9e55b49905e79b2e42275444edc62 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashValid? 65a921dc150476b2c6cc91719ee5f6856e84fb5f87930407b63c6f606ecdeded +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashC 9e1ea088465773f583d1d6cb59b4b5bf1e97975bc8acba92b2d688f98fcea754 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d stripPrefix c5567e6c98ffe51fd7d9502efcec133c5665602b692bf197fa7557a3ced86705 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleHashPrefix 04ef2d1fce822846b3aae9b70795fdebcdcc8be1a841f35b04a16ac23b8a73e1 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundlePath 8c0b567fd42473cb50c21667e711d0a1c2a703088e9f419326186488174a83c0 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d healthPath c62b994110ddef7b756d7778ae2c1c8f598685733ea33dcc7a8870c14813f244 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleContentType 193f98476a4848c8a543af754a2c98ab842032db4c0721a22e21a607975d112a +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d htmlLandingPage 13e9d1c402f9008f18b36172d573fc1585e6303633b739ba10a265d3239037dd +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d bundleResponse 2c2508dc1a9907a5d3907a297e15449f674c1df649d948208405e71ac66f00f8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packagePrefix 4863ca9af271ee8d26de1c3670e7f7aadd47f9f4de8b2a838ead8903a3604425 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageAlphaNum? 6fc7db89f4e88912c897ef3f334cdd598fe1f62e14a61e7be183827f4c269cd8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageByte? 540d4151153d268fd54cb13c31f237a23f95e37c1b1adc2c6bff96d87f9546f8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageVersion c315d31ca807d567a2031b0d268989b971bbdc3b970cacc79b095d6114cd00fb +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageName cbd8d3ecf8cbc5d455d4a7cd07d099984a5064f7b1dbb822c4e1d631f361c2d8 +pin arboricx_server a6876256adddf94e4631b6e14540df4db22b6ece63444ad963dc92174acee48d packageTarget 523ebe158e800b99ef8d34b5d81335c67c6dc9dece752fe7140a8c79ff9c49de diff --git a/test/fixtures/support-receipts/io_v2_http.manifest b/test/fixtures/support-receipts/io_v2_http.manifest @@ -0,0 +1,5 @@ +arboricx.module-manifest.v1 +export recvHeaders arboricx.indexed-tree.v1 fae92c42be9ddf04847aa2f6e1fcc670841d313d09bc648328095e68f3a6c7fb arboricx.abi.tree.v1 +export readBody arboricx.indexed-tree.v1 cebdd476321f81788fd529ed96f2bf4d52bd7ca14d05eacc050b76ea2e552c03 arboricx.abi.tree.v1 +export respond arboricx.indexed-tree.v1 534ffe1c7a933f21a35fefb17f6f7bd78d98c3fe45adf5b19edb68e661242952 arboricx.abi.tree.v1 +export handle arboricx.indexed-tree.v1 dba8f168d9e14aefd8b4c10bdc08fa796fc706b3d9e492bd299382d0abdea3b8 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/io_v2_http.receipt b/test/fixtures/support-receipts/io_v2_http.receipt @@ -0,0 +1,321 @@ +arboricx.build-receipt.v1 +source b881a086294ff264567d758dd2c1ef7a3470faa909ec588e9f7bb8c4430f7bfc +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d +options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f +output 87ae475180ea8613f74de41d1960e12cf1de175ceab47e3eaf67fdaf3d3b3b52 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 +local native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db 13512f671353c4cfd3e279e549900617f09a9653fa8e59ec928bbb2da5e4f663 +local http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 85e6f4f5df9e72ca19d0d1ba874262b7cd277d1854a295790d707b184fc6f0c8 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b +pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db compare 25d460f10cf8893795a8a5eac6a951b40459ef90e35f6945f72c3f53868f94f5 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db equal 07e8af1f8ef6366cab90420cf1ec97a15b434ba4ee3966af235579a05935180e +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lt 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lte a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gt d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db gte a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db inRange 62a3f72adbb9030425f0e63e43568c0eae069d7293763fab3a83123edef50aaa +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db minimum f515c4e49514b4631ca183a2a2dfcfce46d5ef8a8338a09496ce9a0ca2f6502c +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db maximum dfcf25622f229c0ae04441aae5884a38d0ba046ec512c8ac1fde56290657938b +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db zero 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db odd 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db even 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db successor 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db predecessor dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db add 066aebbf5d3b48d5e6c9c35b5c59e9cc5172f47d09899684475738eafb4fbe4a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db subtract a5a9212b766933b7c8f07b3f98f7d9d789d0ae45e1301042da712fefa3dd0a3a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db multiply 320a41f19797fbb9a05d6615b53be543b7acec4e3e535ad81c8f1e5f16db3de0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db divMod 068932c208504ea9a547086ba761d6975ee3173d495b80addc056999b7fc557f +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db quotient bdfe626a727affba89d4c11e489eca0086f4c42facfb4ee9f248e69a47abed8a +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db remainder 80f8dd39e11423480ce2f5f5edde99104824bab147a88a2c331c8f643a0e6817 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db power 3b59e090c4ea2d7966c2bc7bf21c9ed67940c1c7a41f01f6e04db93a69ca54a1 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db decimal 644d54084c9734a5b58ebc7d9bb973eea48890665db1617679db9bd1c8bd68b0 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db ascii e0df024c0168229497859a5e770f7e36594d9d266c3933102d1fa1997d4b3225 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db digit eed46692dadce061198cf3cafd6c00bbc5561d9fb637706d16bde41552a1b9bd +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db letter ddca67e3c882ecee1239775aecf0d31b8ce136f86e6a6eea70e5d8b5979b0b30 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db lowerHex 3b752ef4b4b89ef102dc4c43110ee8522922dbaadf77495713fc465d9e718eb4 +pin native_math 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db printable 9c88d7eaba58c0771da373821f7223c298bea7697647f03f6b1a4d922ff8a6e5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxHeaderBytes e62a642ffd71dff46c4c03641e08f13490469e84bbd0815403b577a0f4916f90 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 maxBodyBytes 40a6c6220b30ead48df9abc0cc7f1b6202197de18d97434061fc1d4ccd6d1292 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 crlfcrlf f77e4d0100d99afad2fa15decbeb38aae4ed8c3135d5ea9c2e5a44537194a871 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrases 0bcd79d7b0f770431eee50a6be0f938b32458a41f7f57ba9b2edb56baf607e97 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 lookupStatusPhrase ad3e569281eedcff04104de5e3da2170f57046bdb8528857b92e9d4dcb78f97d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusPhrase 67d1a009c420ebc91c9ba7533209c485bb9bbd4c7249f4ac48be6b5e598f809d +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 statusLine ce0da21e44a60d7d7c1b09c43142df1dcbfa38b5a6830ffcd3d0a7704cae67af +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerLine 28ee66c4b88c5682d273b428bd7ab51efe64cac4a96919505285950959ed9c95 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpAppendResponseHeader 4d2724f0ee2315f39f1ac8df97ea05d424c3b82289e98fe58bf71834dc80da42 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 buildResponse 02b922ffa4a6a77f42f170cdd0a5aee755578e3e3d5cb9194e8490db95414164 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyLength 005020fe1d72f85901d6d1911c7865245972a2be15b070ed07415082b6a68ecc +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 response 3a029a2a767699d24f1cb4bc4346dd3ccce2940ef4395eeb09b1f8fe89d44613 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 emptyResponse 7b8c925f97085d86b8c7969833f5a745199e5c2f130075703a95bf0e025515de +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 okResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 textResponse e5e7efac115832f9471b1bf59e50da9204067705f795529014426d49da5423d5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 jsonResponse 2abfe36d0337304c8c78bcb87e2f132db6d6acf2385e10cd3e1a92336c447acb +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 htmlResponse 5aaa86ca299dc2ce72f64a5abfc4e07ca0e949bd751669ed8171d4995152725b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 createdResponse 22d380835f4efd58877c625e8de7f08dd5dc1b5b6b30fdf6553e533116166bff +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 notFoundResponse 300f7915c5a75ef4aab7d63a9d113c150f105c384a7fab4c97256ed9e3297b01 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 badRequestResponse 1b533e6b41f50c40a6d0e61d2e5661ede925fd4ad473f016b47186f4e9522097 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 errorResponse 7b6e86847a366c23c27ad86f767947425bf04a987b5f48451f8d714e102c22b5 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headerEndState e1e02d1438ca133c1697de32c065c05d09d7a823849bd000037da452863a0c20 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeadersOnly 193bd05c248100ebed177d832e568c9af9c507ceab7295d4b995c8153c911d00 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 headersOnly f402f9472bc5d854e7ecd2756e0bc85270ad4234e2097ea76491bc8f2e1011a8 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 responseForMethod 521547de19a98f9dea8773a476e6059530ca18870fe7425cfdf0de761d7e5993 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineError 90e26ce5a50e4d236f686346b56094d409a17ba6cf6f2baf5146e56881b66f8e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpScanLine 2c507d029481714365ed2c4d687496b5147026744242093d4a26bee4721ba987 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLineReply 98594579a1412136c4d02ef416de74e17598db6ce960c83776f6b4adea1b119a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBadRequest e11a0cdf150e1784e00dcebff8906c033c6695ebc3e7c5716b72715dde8695a6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 readLineBytes 7bed187a6276169e2eac95cdbb4f2fc53f3625dc6b183b4b9e56e0c5957c34b6 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpNonSpace? f5f2f658784d8ded1ec828967e19324ffd4fd2438e5a6ebcad0a6c07f9feb908 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpThreeWordsReply 5a71c4b351610b15374829ba12e3f6a5f793760c5927d414c944d1060df60161 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseThreeWords 1f04df81381567601348b1442947ed8792f3dd0424eb9393b3c147f33493d94a +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseRequestLine ace457a20b9f1ee3e757a622f3ba03b191873e4d4835cc7ebdfd947332561769 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 toLowerAsciiByte 4a9d74221452385910ba4d4b87cd60d31efe31132b056f32777ebeec6b457d9e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTokenByte? d0c894a224efa6fb17498ee6791f4598a26e38419b51d099af9e80035e3cf85e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpOws? 14e51851f694336c78a7a25b88289c6687b8e0a19af8b220c3a027d5270522c2 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpFieldByte? b7b24f9ed6ee56a602e896fde3aee4c07fde3dee849b608fa451128d5ce4d196 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpTrimOws 7ab13fefc873a02011c645a56f65ca119806b1f7ab740e6bf56d1fb02ec7067c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpHeaderLineReply 962626d5659cce124775e1b8d98482db74433856dc2701767286121681223c56 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpParseHeaderLines 382f455876fd6d1fb704e3d96a1d33bb569211d3e6de1ccb604337266c0ba744 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseHeaders a0fe7e14eacc4024e5812aa0443a8345954ff7db342218db523258cee87aa39b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpBodyTooLarge a35fe61177a0ecce71ff585252f6df5f81e9201ad24e1c238e034c5500ef4d60 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthOverflow 4a4e6626a22d3ee2d2d435940b99e86e0589945c66b3cf185f4fa64ff32b3e76 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpLengthDigits 5c312192ada0032aadbe847e6c6164a786deb66a8ae0e0adc6af140e4791da6b +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 parseContentLengthValue 330f1d204f021be9370b7fb01e3f11703a860532ce46f6f276e0f45d4f59356c +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpSelectLength 5363b4a62c9c002174dc6cd4e48d992841625abf5ca63c37ff001f8de78a3bca +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 contentLength 89dcc8d87f4ea0cec7418f09635b52f2ba03e6cb1b82856886459ceed26fe3f1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 bodyReadState 4b1f2b11e90b11bcfc522deeae834f7dda74256eb49351ca425110831156a186 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 takeBodyBytes 303837e8c16c2b3e76f4076ceb28e22ead58735b4796d72ad185fa22c01aa97e +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 httpRecvBucket 9fdd070fea60b98fbb92b00448c9e4a8424cb77add7477e3ae71b2ef9718d287 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 recvChunkMax4096 dc09a3b54866abf7581ccfc49b92ac2f09c9517f057dbb71f8a4ca05216954c9 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validMethod? ecc48603200f943a4a7074c2c15a282fdadb449732a103b10901acaba94d92c1 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validVersion? 8359ad52fbbbbf38745c2cce361e536718fa0fff7209111d5e15ec868792eaed +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validTarget? 8a047486b225fa0680ff70541ef68bf08ff2c0c4256f1743d76e176d2d9cd2aa +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 validateRequest 067fea6682633eafb0fdfd1df2a0eab21a21abe539954e3a4e3852029e290d69 +pin http b33334423eddc3c01098d0c0ad303da37044892cb0b6e6bad22af677ccf3aff6 routerMethod c329d3eb4fb13567f328de847b80a65d2a0ef0152095260f2fa72fc75f4cf41c +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 diff --git a/test/fixtures/support-receipts/patterns.receipt b/test/fixtures/support-receipts/patterns.receipt @@ -1,9 +1,9 @@ arboricx.build-receipt.v1 -source d61f2de7b3dce93c9b8914fb980224437d0ea4207787caab9098041cf5bfe896 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 64ba09f05b561ed1effe383dfad832e354f306a40675f478c7aecfaefd6d78d5 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 703db8cd6c5679861257d1cc38c17be200241ade8db3812e8c387e9274aaa56e -local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 2b3ccb358f6e262bbb6f9a28c17c9c96a8e3a07a162d4069a8226c9208be03f3 +local base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 1d53769b79c45c26c1c4db463e3eeaa907c40faa067996366e2d0d5cad9348d9 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin base bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/socket.manifest b/test/fixtures/support-receipts/socket.manifest @@ -1,35 +1,14 @@ arboricx.module-manifest.v1 -export socket arboricx.indexed-tree.v1 d0962df2eb672cddf52b8ddf4a76f643a76280b4905c97495efb4af859265516 arboricx.abi.tree.v1 -export closeSocket arboricx.indexed-tree.v1 4b5cf72937d3fc20f6b23496986227b44a1dcaa1d18a17d74da2a3dcb01d9b6b arboricx.abi.tree.v1 -export bindSocket arboricx.indexed-tree.v1 007c736fd19010f007eef45cc4d649b5de7b0f77d042a6c17e4eaea6ccfd007f arboricx.abi.tree.v1 -export listen arboricx.indexed-tree.v1 960de6adb3cce7b002e915e47f405294bf3687d724faff2a6ca5f8926e7d7d93 arboricx.abi.tree.v1 -export accept arboricx.indexed-tree.v1 d5af3a6c23868f6c35b58488dee362395d7e1d54b94915432f2c1f221b5e7be3 arboricx.abi.tree.v1 -export connect arboricx.indexed-tree.v1 5ae01fe00d755ca83680ab01b27ddbaa9f4713b3832de18d714d07c7c14a4317 arboricx.abi.tree.v1 -export recv arboricx.indexed-tree.v1 acaf941bcc0b17a09d151ed9cf70c23ea3f33a897ce99dbdd6ef5e354bc5514b arboricx.abi.tree.v1 -export send arboricx.indexed-tree.v1 641b2ed091cb6eddc1dfd51038f21ac0afaf8e6122440f40b709ab63c8c9d953 arboricx.abi.tree.v1 -export getSocketName arboricx.indexed-tree.v1 9ed5573f5c861837f24b973c84a456d5a47081cd000ebf71e2d1ce0995463468 arboricx.abi.tree.v1 -export onSocket arboricx.indexed-tree.v1 d2dfa18c4c469383d96d14ff08646db8b345f1bad52d8772b9b59e2b5225315b arboricx.abi.tree.v1 -export onBindSocket arboricx.indexed-tree.v1 ae3e99d9ebed09010981a11eb788cb572cef63a4ca625d86f7196fde97527530 arboricx.abi.tree.v1 -export onListen arboricx.indexed-tree.v1 5a6660028564f14fbe8bd68e0574ae31c4eb254064169438957864d62bf16868 arboricx.abi.tree.v1 -export onAccept arboricx.indexed-tree.v1 ea248480f5681c96d255c317a0783dc98acd028c8ec07d324ac2ae02a764374d arboricx.abi.tree.v1 -export onConnect arboricx.indexed-tree.v1 5ba2194bee14d9fad3c282147bebd0c03bc55b76c518bb684350c7901bc296c9 arboricx.abi.tree.v1 -export onRecv arboricx.indexed-tree.v1 d5f6bf951f02d8415354bbab1e720d1d87e3d39caf26dc6a0b9801cd03b42ed2 arboricx.abi.tree.v1 -export onSend arboricx.indexed-tree.v1 112d48a3a067379fcfaa56e894ffe0aaf02e3f3fc44bc2fe8472f0d49a47aeca arboricx.abi.tree.v1 -export onGetSocketName arboricx.indexed-tree.v1 99d9c9dff1637310ff2c9a001cd718a36cccc03a18d84a3294f2ab7874af1f76 arboricx.abi.tree.v1 -export onSocket_ arboricx.indexed-tree.v1 3c394ed1369781ee2254ded5956687c27971c495548b6278e5e62efd757370a6 arboricx.abi.tree.v1 -export onBindSocket_ arboricx.indexed-tree.v1 7c91b3aabf7daffd7da4e525db14be852b1e3b874212d7da444467d6de0585ed arboricx.abi.tree.v1 -export onListen_ arboricx.indexed-tree.v1 e9f2c57f616bed580f21e58bd01b327d4615680d6209574cfeb6a7da85758025 arboricx.abi.tree.v1 -export onAccept_ arboricx.indexed-tree.v1 e4b93ffb876803acb6700e33127436cc53f348efbd0d422ebca09a3371b1614e arboricx.abi.tree.v1 -export onConnect_ arboricx.indexed-tree.v1 e0baf23e45038091b6c6ccea70941dd3fdf96128f94a595ae0f320f095979025 arboricx.abi.tree.v1 -export onRecv_ arboricx.indexed-tree.v1 dc391a9c916186a16974abf368dd23d0bdb3fced7b5f3cffe8a655aa41f230ea arboricx.abi.tree.v1 -export onSend_ arboricx.indexed-tree.v1 c746325d45b9a88f5519a5399bc784bd7b3eb5ba53a1aa37f6030e64b0d109bd arboricx.abi.tree.v1 -export onGetSocketName_ arboricx.indexed-tree.v1 31d562c8271942cd7eadefdde5bac73abbcdbcbf63ffc4e9d83b68ce39929988 arboricx.abi.tree.v1 -export closeSocket_ arboricx.indexed-tree.v1 e10c012b9860dd7629b768fcb75126eff626b2f80ea1a0095ee2efcaa68b304e arboricx.abi.tree.v1 -export closeSocketOnError arboricx.indexed-tree.v1 21d850f11f110d1bdb7d4cabd44913473f997337ded0df8b79e3f94034c0bdf0 arboricx.abi.tree.v1 -export listenSocket arboricx.indexed-tree.v1 8a248c717b8216370b40ddc355851282362dd2bc130df5adf5f8f0aa2a3d9dcc arboricx.abi.tree.v1 -export withAccepted arboricx.indexed-tree.v1 7838f3c8b28ede95756dd9ca6283db1a3519bb95e0b19ab3bcfcabedabb1c900 arboricx.abi.tree.v1 -export withAccepted_ arboricx.indexed-tree.v1 b33b77bfe9094c3938bbc8684d668da222d2b461cf020fe927f848c1bb4c26a7 arboricx.abi.tree.v1 -export serveOnce arboricx.indexed-tree.v1 2e39131bc2e390022e72a22235cde46ecf0a0b54a46618719ccaab660acbc306 arboricx.abi.tree.v1 -export serveForkingOnce arboricx.indexed-tree.v1 1194e71d76856c2a519ef62c0197d4399fa30ece80ff756eb1419f57197aa927 arboricx.abi.tree.v1 -export serveForever arboricx.indexed-tree.v1 ac87d6be311b022bce24cbbb1f75b5e80db4f672c04d695afb800e81cd1dc95e arboricx.abi.tree.v1 -export connectTo arboricx.indexed-tree.v1 1c2bee16cdfaa4046a2f173d0210f26c0eb48f356dafa00a84cec4e34f553bcb arboricx.abi.tree.v1 +export ipv4 arboricx.indexed-tree.v1 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa arboricx.abi.tree.v1 +export ipv6 arboricx.indexed-tree.v1 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 arboricx.abi.tree.v1 +export resolveTCP arboricx.indexed-tree.v1 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead arboricx.abi.tree.v1 +export listenTCP arboricx.indexed-tree.v1 d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d arboricx.abi.tree.v1 +export connectTCP arboricx.indexed-tree.v1 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 arboricx.abi.tree.v1 +export acceptTCP arboricx.indexed-tree.v1 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca arboricx.abi.tree.v1 +export localAddress arboricx.indexed-tree.v1 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf arboricx.abi.tree.v1 +export peerAddress arboricx.indexed-tree.v1 b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 arboricx.abi.tree.v1 +export shutdownWrite arboricx.indexed-tree.v1 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 arboricx.abi.tree.v1 +export readSome arboricx.indexed-tree.v1 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 arboricx.abi.tree.v1 +export writeSome arboricx.indexed-tree.v1 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 arboricx.abi.tree.v1 +export writeAll arboricx.indexed-tree.v1 ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be arboricx.abi.tree.v1 +export close arboricx.indexed-tree.v1 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/socket.receipt b/test/fixtures/support-receipts/socket.receipt @@ -1,234 +1,80 @@ arboricx.build-receipt.v1 -source 5014c5c41aa63d3b8a52e6cd89600b435505d93325f730b532c9eabaffd58421 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source b842a39f410e28c89c843ac8f1d3a8db7afa2e11a5ce12bf5c29193a6fb6b3c6 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 49fc19156894c8f5b5a7124760eb8db2ac504404dcda77ae0e3a0dea39983d44 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 id 15f13aeab5531e917cb30154730ad17db4f423c0b92784be7fa1817cb4be2287 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 const c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pair 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 if 56fd7fec37e6713635947456e96f6dc61836bb46849ee931ec3c857c6903e7bc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 y 6acb841649e0e188532ad5cf9d6fe75231980a319252cfa87951018ef683a915 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 compose d7c1fcd7ffdb874370f9a04b7f85daa04789db51e1a169db71bcdf3cc1d92844 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 triage 3b89c8b2218cd7c9969398c48c0becc6671ab0ab5c456dcf575109b2bae0d28d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 test 606b5a4981f6012fb36c83ccad2f44f6d8f43c5fe1cb007427aa9ec17e0d6e7e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchBool 5e75e2eba9d9f1465e75b35d5f2c974e02da8609c384965533123cf159c8afa1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lAnd 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lOr fda8282b06b0ad4a2989987184cdd2b1e3d81a9355d431c467606f0431e52392 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchPair f6190e6a6483238cde28df9e47e3e9a0679cc1416e8e21cc7537a147dcad9f9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fst 8e0725282a61d8f9b877514b91d73dc35986fe428eb643023ab94d3c080410f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snd 5485573a58b48584b5587240f43f834ac1a3938cd2305e11a3912a16f6ad664f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 not? 314affa8a7250aa5cb4cf00e35449676ae3a18cacd6478026c3e01131fe74b21 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 and? 48b77e121652f7372961bcff04a0a1eaeb6ba2b524b5df9e9b236045783e9104 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 or? 5e0f47dad5ddf5868a3f49bd764af78eabbbb5ea3cf11c102c859accab2b844b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 xor? f4395e0930bc2fe8175de5dc7086dc09bc0e6bc31bb6d69c160a0f726cdf4c4c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 equal? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 succ 92368bcbd17e125e9320d4b6b6be1ecc192f195f73602c6587b886d0134b60d1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ok fa6c79b65290f4a9df00d6597094d9bed71250d54eb58c2fb339427680ad965b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 err d0fb36bc3d029d4f307101c457452b2fada5e71835dbb5277b48b057afd180c1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchResult 7a8f2434be8829fb8828f3f025e7144191031d471cebddb6d2dc8e5efcde8e7a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsOk c95b951a6b2651c742f1b61b2c7ee423dfc44134b39bff184d7d5f98b522336b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultIsErr 41c525d70056bae0a40f54608f072b56ecd93057a5d6d560c67ebd68f7fa49ef -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nothing 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 just 6740c23886cedc8d876db8c2f3a4e8c2e647bc4b0806552f9b47c1fb2fa5d4e4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchMaybe 0e50413f7b90700b0f52fd794e87f99938eff9f7bbda61ee1382d3f29bb2a5df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe 5558ad99e8503199405c47a7742433cec680d7f83ccf203be03e5c8491349bcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeMap 0e934bb03bcad87fdb9cbd7a3fe10e6b9813170523661d3cc0fb779c237089df -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeBind fd8db402e6b4565646ea18b5cf99d99b860de9e2c68ef4736c113226a16e1dce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybeOr 379313856ec98dee847b54d994cb8a5a439457e9521d2469ca553b0fbe883dcf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 maybe? 7d04abf2f877095c0f7c1a23968687b611965a9c810eea9715754567d7905f2b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyBool 1aea8441c7da5880c46c0cda4e67f5fe26031168affa6ed41a3e959f780921fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyList d1e600fe518c4f2ff1814c6cac186ed8ce6674ded700755129c3df0c685cb89d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyMaybe 0450f8cc043deb7651af752148d3490300f78e124555cc33e0e409fca32ba000 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lazyResult ca3675c3587d3ac79b7b1067912388aa5353c7a2b7fe5258cc5535914a0b49f5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 ifLazy 50cf419adcba80f9ca2baf6fe82195845e6dc3f06ffa28743d006d47f3820273 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andLazy? f782153bf621d20287c255f8bfdeacf126973875983e461d05ea9c3e0968639b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred_ dadc9a9becafd4bb255bc9bd00616c49a488237b4e16ae5919ae46f590525667 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero_? 1a44df56829bf449628a829ebf2d0a02ef86babe5c2f016e0ae58ef490a0c584 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte_? a418c1e5f9d7009ced4b56f78b7362dd19737561f8569cc75a61fc2af0bec51d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte_? a673430b4e67fc6e29e3ae0311fcc849cc6cc86de8012ef2a279854283001e54 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt_? 2d03ab42e2a4c9cd6f7ba21c381816f69ad0d9a68603de268e40649d9de3d680 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt_? d61ee5a7024a93abad63c481e07dbe5c397185091d495280521aaccc7cc4ffbb -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 even? 1708a9a702cf609e842f230d620ff0b52502b07e6209a5124475007d23bb1333 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 odd? 597f51ff9347748cc2a6ad0a824e18506d3db33151258898f8ad5c77e4aa35dd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapResult 175ce29ff0cdf8a18f1a40ba4e5555153bfe7c6681acf267b4eff617fcb542b2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindResult 3d7c08240f2b7614d86e0e58b758b21f10ca1291d1a2c32128ecba8ca50499bf -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultOr e0eadaf43ed5b408817338534b30d5adbdf027b54597b9198717ebc1d5c4972b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 resultMapErr 090a773457482c6d03b9d56d9a660da1d868992875b6336f0aebb5a20d009a0b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 matchList 732ece1c0328060122cefceb8ecfd6afd3890ecc04554c87750887ddb2bef5b4 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 emptyList? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 append af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lExist? c55b80e7759ab0057ccdc676542098b9fdbe58ab7cd7bee326b41c9d5931e8d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 map 082c832efccd01a00168483bacc71219c005b7f532886e467d1f93a128277045 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 filter 2ea4a2cc9ccba98902dfe74c2e1fa2b5fec4fa498764bcb86b3b01e4fdb8667b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldl 87493caa0e52e0804336420e35e3ea46e4a218670c2281e9eb4f5eec2662fb9f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 foldr 60bbb01b68c96a16c87080eb42ee2ea9f5ed0d2a9c8062484ae5a39de90abb46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 reverse 294aced922e4cb2c7227030e71525ee239d8ed903a6119854f0fdf81f30392bd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 snoc 0b2f28544fa3f4254b3125cc9952da1310347ea9e4c6a09425f9784fe373a8f6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 count d8c6cd695fb4009edd23fb89ae5ca53c243e0183e7bab8e1a390d84fa30de888 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 all? c46b6fdec24bce9449dbf5bb3882416c5e239b36a287729da036b9106b37e206 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 any? ff534aec0900bb624bb2030fae41599417276fe55856c0e773974731c0b76c3b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intersect c189099dc322035613393dda0057e726e45c25b96819b6d16c089011fb007623 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nth 1a5dad6fef788e6ed689ef24db90154c9d430e00b0ea99dab9042f49244eb9a1 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 headMaybe f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lastMaybe 0f75b672c7bf1a24a7f80f1875960b0c9c974f64a45f0f374772073ec9ba5a2d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nthMaybe a0c61edd95a2cc32f1c7f1a8e0a09f15f472331776cab2d4cf7181250435f987 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 take 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 drop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 concatMap f0d8f0479fbeebae6dcb7109829fb7bef8e7e248449d4411a442b825c4d99d31 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 find fa462cf1cf2580b1f98ef7cfedacb258235629551f46d941c38f8ce72b4d6784 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 partition 8417ca2bbe923600c8c2906bcbd693062c1aacc2f41c657e3de8411ebb7e519d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strEmpty? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 startsWith? 8154aaec33323a1911f200bd6f18f3f700d248145a770678f420041ebb7458b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 endsWith? d3f3f02ecfb3efddaa83cd7a5c1730a54d36aebeb23a1dee4c869330c59c7c14 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contains? 4b3d77ee01b1740995b5ac3508c2444dfac980225631885f5506876f3d01bea7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 takeWhile 04a2117886debee771a2c8344ea1174fe618b41160728c84bf3e74040021f95f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 dropWhile 255564d18a37081098d43a3e24ce73c30a02d67deb269aeb42213fced8438edd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 spaceByte? 4c3943f0808305e9165947bc4ce126bf61e3a2f65cd94aea0475bfabc069704d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tabByte? f8942b545e756e54a7a3aa393cd6232a1143516ef46dce62fad08d27b045b16a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trimByte? 3a1c12dbaa34c6f3c7e13a48a7a046043c841797ff974133bd3bdea8350b103f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 trim 3f23fcadd5d84bddd3eb54b8756fafcef77023ce5a10684c507eaaeb871f46c6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 intercalate b1d8123f5bcc86cdc0818f00d187b0b29d8668f3183f8e5f793914cce66e2638 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 joinSuffix e807557c402969756b74b403213ece91c331c35fd53827a253e70676ace8868d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 splitOnByte 702d2ba65cbd69194ca367e506e432c3017d92b91511f5e1088703020cb7e7ce -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lines 3844c06c6e848fca0d46d4caf14f44bd4acbcc4df09c482b24a86203053951d9 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unlines 6b9789f51d26de29e035cea374fe83964fb239dbf21e7857765355b8a9b7327d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 words 3dc82e5d664a329d45c7bff3386c07a0f57129492ed9b8e508dce91d47b2bf87 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 unwords ef3ed37b582f3ca7bc5777b74db051fff36a6c5b189591554ec8ca73c1b7385c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 zipWith ad688f802432c721b66429d5283e04f069e1cda24765d3ed8e59ff7b9fbbaac3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractOk c4c69ce83e64c58241c5bbbc64b40dbbf6b5bb12b98e3658e50d59440c63f8ec -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 contractErr f2129dc0488f6fdb7a8058d1fbddcb156e4e4a7e605df259ac3479835e8ad4d2 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 defaultKernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 kernel 2a17cf6f10c57c0f2e0f59dee7e51344594828ca40e557d5ee36e1710efed76e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 skipKernel 5a8b867eeefe887b4c4dca6b40e3702bda3665351d0f31ab52f195ec414a3dd7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 withContract 1274c9607e6eb9aa00b4b42dd3417d91df32965516ad2b92cd3e0bf25470a4fc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 check 827969ea5ab9f6a2ab6a933210a5ef5b50d070dfb985fa39d6794ea9c740f543 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkContract b986eecc4c4cc286b1e3b6b0e189516d40a3ce8b47da2a93947b410f8bb9dd46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 anyC c0b097165a685c247bdbcb2af86e4a3fd944af2de922d0e50ad3d68dfeafe91e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 neverC dc33f335150d76666dad1ffb42069e755c7349335c74e084560f8ab8d7df8312 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 guardC 739640012722834b0924d0b59113650f3c7141beee210319e925ec7280c4ec9a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isNat? 3a182e143ae7e85a110ecb55cac644c431cf0cce55a47be3adf1e95511c18371 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nat? 514b65256d4fee60b2da10376b4e342a3d81531ad2dd3c8fe835ac6dbcb2451e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pred bc4a23ca65baf447a94846d4f0e6695c44a58fb9efd7829b86928bb9a1403339 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 add 0d37bef7fa64465b14df915cc6623aea5318f470f7589f812006a15483997dcd -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sub 45a0213bb312523bc1d50248a519c1272b7079935aa4ec23fd602a271d572dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mul 8591573db2e8c65db0ef4ca41fdf8d11960e9558582347b1d32fb07deb390490 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pow e3e69ef1b27695609fa88ceca4fe26c7e638044707d2f4dfbfedff103a2efef5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 min 4d4209d0f31f39891f31836b8d14e1ddb6b438920c32454c8cc79940f474541a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 max 7a13356de2d4a64e87b9c7a25f87e74c340c917cf5beb348a6a6e274cdc07e72 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonZero? 12ffae613b6629b94eb2016e9bb3af8b37a169fd770a21b0da5ed10e73bf5434 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 div 643ba7b3b56a10eb65bc0c160310e2176315943c893c3efc33bc0698540f2989 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mod c2f3de03c1391dfc1b29361ccb3bbeafbead3e014bd3ddc92aa421ca088cf1fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bool? 36258eeb3bd1c18cd9f37cb6bb6ac47a7db7dde814924ef958c18a3e4896ae6e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 isZero? cf23e7ad9a127b346042759b69903a5fd2bc303b86135ea82c4cc0bf85b064b8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lte? 3b59f80c7090f1b09b44af18101bd0b1b1026633e6a2b0b47190a2e34d5d2d46 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gte? 84b0a8aa0082bd2f8e58e23a7977be2f255ca454e09ec248cf92be701a6b9c4b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 lt? 4d5a4946b5360b6eb643e2c7da8b4a89d4fa4a52d1dc3ca8801e22b2a2942b4f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 gt? f46061605057938810eec0be9756811d5c1bc1a782ef928a3ffe0a3615e25a89 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 andC 8aa9aee5f5e2f08021b211d29132efb043963cfb0487f5c8c69103370e979e5b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 orC f56acbbf14935e6df671dad2b245e63483300f3d3c849c3c6dde941907f02468 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 notC afb1ce3407b598f896bfdddbc467c324344c2332ce4fd9ba1602c5c851c9e22a -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 mapC a860f2ba4b79dbe956669c35990c66901569429bb28b9ae2e05626f9b927992d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindC d44f97555a129a972b179c076650012146ebb0e5d59dfe5434abc36db653ccbc -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 listOf 30a2ae02fefbaea305c0cc0c3fc27bcf990646a84fdc4b9ef5b9baeb4910795b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 length 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 strLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 sum df430514e1597443431fdc1d05c57121adb31535a51918d2edd2db837fd79059 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 product 16c840a4a96bb12b09965228e7ae88a3e637de6c61141fbdda1c547859f5ef94 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 nonEmptyListOf a29da91362b02c103a110ee0d13fb68e3f7098fd0f8e8acc087d5c22847efd3e -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 head c61f49e4694b60e4aa2f6b4e8ea5d9c755757457c2fc44d0b9df20a99f35266f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 tail 22f8facc5634fbd3c17bb25a81d877775a2ac2ede712a69fa55ee3bd28b0e17d -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 last effd2a893e07adcb08f6edf9ff96831fdf6e627e21a055bc0dab0a78c4a73b2c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pairOf 629aeb46d96462cdc2b1ac48812f9927ca19a1462e9a0ad25b4ee2f61c8258fa -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 divMod 6b74a1f790d3e9ce7d1c54dd82a4fc792a2d52e86883dad0e5d152c7e5f4b65f -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fnContract 16eb50982faf0053febf8efaec1c0fdfaab2b255c149bee87e4e72002079d929 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fn2 2ccaacba76d909179969a143fe8175309db4ac415ff5c3a4bbebbcde72ca3dc6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureE c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindE f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 exceptE ef43908cd91977ac2086ffed30745bbc5d7a4bc5f6e36fcf4ec74237c4346b8c -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 pureM c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bindM f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 checkM 6d08e8c9c83672d1e932759aaa2a759b6b5a52f9d18c22a841c75e2fa4cfe2e5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 liftM 5a6509acc0b3de69063e9eac30282e318756f3d654d1912f5743d0cd08609466 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 runM 9ea77563202903b50dd175e9c49b35f9f57bbc2b51e94c3dcf83a63b2fe7088b -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 handleM 5c1898c35defbbd28fb734362e84cc58758478e9b2167b576226f1d2e13a2017 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesNil? 1535dec8df207e8884288469857d96d658b6f31ae36723de6297cd73aab9cfb5 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesHead f361620ac2ef0c1b51d3937b13936ada3f13290fefdc7afea94303e351ea5c11 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTail e7847aa2818295d37d9127731bbd621adb01050ec4715eaa7ec6a7969959a5b6 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesLength 09bdc981bfcd9a6670eed0316ad74e6d1e481e9e00cc3d9611531d18f20d80d3 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesAppend af7e2569d0d16378cddc57f9f5ba894fb0b8c4d82edf2e9555c21e61c8c96ec8 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesTake 44190ff3c4dfd15c925c49c69b47ac2748da883bace8892a6de4b0d719e28e82 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesDrop 6cebcd1d65a7d48f7ca668efbbfe8441d2009b2dc746f6906a1318b0b18a7b51 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed -pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed +output f43fa8a321b5cd25954941270ea7f56d68115c77fac007f4e899dbc221f2cd38 +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 diff --git a/test/fixtures/support-receipts/source_build_stable.manifest b/test/fixtures/support-receipts/source_build_stable.manifest @@ -1,2 +1,2 @@ arboricx.module-manifest.v1 -export sourceBuildGraph arboricx.indexed-tree.v1 00bc713f4838c3fce1aeaa1430bbe369a3b7c836cfb3450e502b80bf7fd7834a arboricx.abi.tree.v1 +export sourceBuildGraph arboricx.indexed-tree.v1 25059e94944b3ebda24f5c27c3d71bf1041d069abf739438398a044e11e164c8 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/source_build_stable.receipt b/test/fixtures/support-receipts/source_build_stable.receipt @@ -1,43 +1,43 @@ arboricx.build-receipt.v1 source 240f25295a1fa23fe29faacb204a4bb5b0bda5b9b6d64185ee8135374e1c10c5 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 899ebd78231147cbf6b72a71298a72c7047d6fb1d8033fac88898e178613e6a7 -local source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f e53af4a0a1a75ab555200d3fb3a9a00bb9acec43dc56e7288850966217f55779 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildSourcePath 7c3b8e132ff64d64b3257af737c513d021735e65490cf4167ec40ba60342ea34 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildContains 6b3ea74330ba5d21a32d5c591ad048bf61d0b5a129708fdc682259d10bb0a6b6 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildStage 15f16e238d3739b2924dd1e63e5d14542fb9afa309305866dfb63d24d73ab8f7 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGetManifest ec54d33149037c1ee5b828136d5c8456a0a754b198ad65a83f1bebfd25aad5ba -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildDone 69dbd1fa6b605b097f901e5a7a1f0d635f1f2386bca540f3633e89172791c0af -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildResolveRefs 51d53749e0c966e579ee0e22797ce656d2893bb72e1204b215afb371c2f16bfb -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGroupsFromLocals 8bb2ae51fd5488b48ef7479cf67854fa0487f9248a165e7c48c0d089922be5fc -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGroupManifest 57e2ffb3c76d71634cb3d58eec63086ac16799efe09e0eb3feb1eddb9ae1672e -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildLocalRows 77c7b92c45ce1d6251d7ebad4aa4a1f9e63dc3e9485ca8767392051061d33002 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildPinRows 1db3ed55bae74a35e81c5fe49c9eb4484b19368ab2a10514ab5600b873bc1ddf -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildAllPinRows 60edda295b215a557fd1df19bdc6cdec7ab19247e1c64c83f74342c34e39a66d -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildReceiptBytes 87ec2cc88ab699e8612364d28a4e18044dbbe8bdacc8ca76b1bc8da65b702265 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildPutReceipt 85b80d7ab4a2dd9af37a454a38281bfe0d5c383f327869c8a881c2affbafd685 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheKeyBytes 1649175a9bfcfa9074fc33b530a903652a113ab02f56e8e5def47b6247c87c1d -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCachePath 28de4286fd15ea12f0d4cbd166ba820285a86d1e87437f5bb786be2670443cb0 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheBytes 6cd52a8119d36859d4ce7c3968f07acedbd4dcb2b9e7f442b14a1c40924af7aa -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheDecode 873f3727911f46e849c9a9b4e9d40ce08a2490f9f19532856593f277e5efe0d5 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCachePublish d099ca775c62b4700477591a10f353d7d85db3829096a94e3184e241bd16990d -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildVerifyGroups 78919237f25a30807b662d354331cacf2392098a1f9e95ce2f92c34021543765 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheVerifyGroups b57a60a21f3173f3220bc7c25c018dd13e11081d8742259be509b4e19b7ec782 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheUse d102d37cc02504675f9b83f7a539e8e17e94564be36e8144232a91a687f8c56e -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCompile 9b86dbb47cbf2b95ef46cf0c4871dfafc9ad03f734f1ff266536f4b842adcd09 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCached 0aa88bb8efe3fcd1342246a6be8f717c8fec11b4536db49f07db2183ef8a5191 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildFinish 0953f34d5fb746f321d1412e8f38e2ee9fb037748a2a01e0d641c460e65bb689 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildReadTriPath 15aafd0e79fef6b63f98b08c66a7d24667d0ddbe285f32e7fe6879d1bcaf07a7 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildReadTri 8e5ac6c6028142d8042704c069e3ff360422b375f5843b60e0c78ea2888024a2 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCompileTriWith 6e04a2bbc7a97edb74b1986beea78efe23967639e95612db4b7e9f9f7e5b8dde -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCompileTri a47435f075a42d79cbd840cf3b355c7a3b9540f41f3b92fff39facbfc44f9ffa -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildStableTriProfile e2d1624808dbf487d3818fa0daabcc5e6371e713936045a5f30e3f00582fac28 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildTriProfile 49802768ca04c964cde1888dbffb25a77dc9ef95422e7303ef375ac38003b74b -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildDependencies c7c11ac297d87d5e581a0f3bfa467605a074b0b6aace4958e22a21649097f182 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildWalk d1bad40a26b5bc02f8c79f913214c6f1d40175d617ec8e416bb59a9c078a2df4 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraphUnitProfile c0cbe47cdc6a85c948f10c161990c0d8cd4df155ea3b105a8dc0533a07c58c96 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraphProfile 5822e5a19adf1c714270f8db56638fed9c3318e6674102c69d62faf6a5b64f0b -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraph da85f1448bb0f644a6e471ba262cead255718def5621f065c5483cb193bcad5e -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraphBundle ebcf92ac4fcc32688a02818a3d57fed0e380f3f324350fc85593ac8d8d351f63 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraphStable 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +output d4c7521ff4bfea552124779a06ae3616e0dbbb3f00f711903b6f0f85902268df +local source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 d9b1bbcf51ed48b8a9c918fac4e9044430b90be9ef3d64f7fc75005c876c6fd8 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildSourcePath 7c3b8e132ff64d64b3257af737c513d021735e65490cf4167ec40ba60342ea34 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildContains 6b3ea74330ba5d21a32d5c591ad048bf61d0b5a129708fdc682259d10bb0a6b6 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildStage 15f16e238d3739b2924dd1e63e5d14542fb9afa309305866dfb63d24d73ab8f7 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGetManifest bbc73832dbe39e583b5166d8ff53d1b97f2bb99f61f7693b9ca47c554e83bdee +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildDone 69dbd1fa6b605b097f901e5a7a1f0d635f1f2386bca540f3633e89172791c0af +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildResolveRefs 00c75ff6c72c67fd50afabec5096087df52c2ed0e5be2075e33d7bc85e3e60af +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGroupsFromLocals 2c6445d384f8f43981be5cb07b0e3fa41c930f71ee8479317497708b67224a0d +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGroupManifest 57e2ffb3c76d71634cb3d58eec63086ac16799efe09e0eb3feb1eddb9ae1672e +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildLocalRows 77c7b92c45ce1d6251d7ebad4aa4a1f9e63dc3e9485ca8767392051061d33002 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildPinRows 1db3ed55bae74a35e81c5fe49c9eb4484b19368ab2a10514ab5600b873bc1ddf +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildAllPinRows 60edda295b215a557fd1df19bdc6cdec7ab19247e1c64c83f74342c34e39a66d +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildReceiptBytes 87ec2cc88ab699e8612364d28a4e18044dbbe8bdacc8ca76b1bc8da65b702265 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildPutReceipt 5c3d1f286c9d1f64636565b77557450bd4f8d00ee8aae17fd7c661cbd8f3e2e2 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheKeyBytes 1649175a9bfcfa9074fc33b530a903652a113ab02f56e8e5def47b6247c87c1d +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCachePath 28de4286fd15ea12f0d4cbd166ba820285a86d1e87437f5bb786be2670443cb0 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheBytes 6cd52a8119d36859d4ce7c3968f07acedbd4dcb2b9e7f442b14a1c40924af7aa +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheDecode 873f3727911f46e849c9a9b4e9d40ce08a2490f9f19532856593f277e5efe0d5 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCachePublish 929be959e359d1eb05a6b68d1f5b4a91fa3c0d4071d1adbba3248ce02f51d14d +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildVerifyGroups 60128d7cbcd2db0c97eb3f563f4a2cdd55d627e6170f0410067f092f636f4cf5 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheVerifyGroups 129dfeea2bdd4ea380bf49f7ea5ec278fc890b61b1e197ea1bfc9e35ba26e6a3 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheUse 67d7ce87388fffbdcfcea5803a8d85d11ab24ff35b491d13eb3429f41d25c435 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCompile 48bf3231d26beaded1fb6ea9cafede80a0aed59e20bf90bceb9e5f6127a70e40 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCached 7025c9aed370e5a1e40252d5130c96d987619e7e50443ba66127b397fe8f0673 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildFinish 7e87a77b24d10e179abaf33ef6a1ee55059e6518d8474f45e4599874315b6cc7 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildReadTriPath 569535418ae7b3f5dbc6f1202fdb3b23b4f2ab23ec117e01d154181377d598ee +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildReadTri 0bdf706f0df43a35965a6f54c63a4a76afb32d83db2a78b13b1067267709eba9 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCompileTriWith a64b687e4a61cd61ef48fdda0cad4924af97df1bd3e5c7205fa197b420ecc134 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCompileTri 579018e6eaf89b2942d208aad6a70c3bf777460d3aad87e29cd566d55a9add4f +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildStableTriProfile d607dae90d25471e54c53f68ad3775cfc0d0c27a02fad7e5863b98cbbd20745f +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildTriProfile 0d8a88553d8286040e984ca884881eab7865ab48db9d1a9f9ecd180a80152cf2 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildDependencies c7c11ac297d87d5e581a0f3bfa467605a074b0b6aace4958e22a21649097f182 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildWalk 02b68954ac35af7b60a291249f3f79c8c32bca4fec5ff348a5a44de7b667cfdd +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraphUnitProfile 1e5ff22147a66005214be6ca97f757645e78429d5c8326256af0cb55a68b3da5 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraphProfile 3b7a9f1d2a674485c62405e0ae2fc03a64027a48e1aa49c541ec98a186fab44c +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraph 3ff2e4ba1941518b3a330ef1b54167e09a210df0d93f3ad903e2af7df26b839c +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraphBundle f52b82056f14ff85ab0d33cea3313923366d340a34e85c320757b6a108327275 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraphStable 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d diff --git a/test/fixtures/support-receipts/source_export_cache.manifest b/test/fixtures/support-receipts/source_export_cache.manifest @@ -1,7 +1,8 @@ arboricx.module-manifest.v1 -export sourceExportCacheOne arboricx.indexed-tree.v1 48090ec3746b7bb54e6e85419f34aaaf7fa635e068e41cea0b6d227af519d1de arboricx.abi.tree.v1 -export sourceExportCacheAll arboricx.indexed-tree.v1 1110ecff489c2473953e7b8256b6de6b4a981ed4f3c21e6d3ce7b77e8a13a2c0 arboricx.abi.tree.v1 -export sourceExportCacheSelect arboricx.indexed-tree.v1 4c7c41c500bfd71b070dab0824426ba8a69c4ddfe38327e711b9c054c9b55f7c arboricx.abi.tree.v1 -export sourceExportCacheBuild arboricx.indexed-tree.v1 26036248a3ca987241019f77b24bce21d31b9594063b77053ea05bee693dcd3a arboricx.abi.tree.v1 -export sourceExportCacheCompile arboricx.indexed-tree.v1 bc5e91bacc9d1d7136229eb591b475ef17e07e86ea08d03a761ef9da29bcfe51 arboricx.abi.tree.v1 -export sourceExportCacheProfile arboricx.indexed-tree.v1 fb6b7876ecb3aa01267355f0ba62b4a20be576bf30bcb6d5e42682f736e82255 arboricx.abi.tree.v1 +export sourceExportCacheLog arboricx.indexed-tree.v1 d8ce209aba5103f933933025c449225adec5a769735427bf6f55fcd6eea3bce0 arboricx.abi.tree.v1 +export sourceExportCacheOne arboricx.indexed-tree.v1 3d91d01556b9ac8b00ad919a348082efac42e40da09c6f2f93fa7d47287fca54 arboricx.abi.tree.v1 +export sourceExportCacheAll arboricx.indexed-tree.v1 4b00044956d5105087809d6cc1e23f24593a5b2f25784c16a78c1d50166e0bf9 arboricx.abi.tree.v1 +export sourceExportCacheSelect arboricx.indexed-tree.v1 7ed8dba8f9d84268d095834491a807a79d5fcb81d234fc1295759e5576fe8c4c arboricx.abi.tree.v1 +export sourceExportCacheBuild arboricx.indexed-tree.v1 b34675bc5b67f9092092b91ebe7cc85a99d7f03bc0f9fb91f12d6f4ff5f69586 arboricx.abi.tree.v1 +export sourceExportCacheCompile arboricx.indexed-tree.v1 99c93cd8e911046e471b32e071aeb948480309a2c43d11fe9fe78df1e2af4573 arboricx.abi.tree.v1 +export sourceExportCacheProfile arboricx.indexed-tree.v1 8deebc65ddc0874b74c1047f74af7ac42859cf8423002e52cf6b00e0518269f6 arboricx.abi.tree.v1 diff --git a/test/fixtures/support-receipts/source_export_cache.receipt b/test/fixtures/support-receipts/source_export_cache.receipt @@ -1,16 +1,18 @@ arboricx.build-receipt.v1 -source 378ca283a0ef856e6fde25e88ebd321b89eb348ef52232a5895f4240be0b5369 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +source 5445a4f505c572889455435960a144663e224fa40a9c1cce1d9a96a8e3678fef +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f -output 7f29e52b9154b0ba6410836b055f8628f76bc51aa572efa2ef691f37ecaec077 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 a88eccbc20c6c03eaf19d2b7ebf10cfd8963a24cde4bd64e16dd1df4664b7be5 -local source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf a3d3726263cd93f4983382f73114455721a03adf499c12a84ee5711473f4e382 -local source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 556ff9c43221c27b76583d41d3015f64f446a3cf58c507eccc01363964bb5f04 -local source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e 654250856a4ad10acb61a4d2de5c78fbdac6eeff5509c984d088d776783862c8 -local source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f e53af4a0a1a75ab555200d3fb3a9a00bb9acec43dc56e7288850966217f55779 -local source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 91f3faa6fafd792dffefedd83caa74b5b3b0af1618a8648aa0e75eacf650db45 -local source_resolve ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe 048bb0fbeeb4817c58c790cc7bcb1a318d66a21939933e140aec025152ddf624 +output adb94daf05edaab172bf385632665cf03122ff71a288199243062f0a6c72b573 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 c67085a3ebce163ee262e5499b72dd699fdd60b59c6a487371e9d25ddc2009bd +local io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b f59f21df6d1118b754af9b50ce74109aee5e5915add6e43420e71f6e1f22a060 +local io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df 61458b530f4d7a112b0d3ec896afb28570966d6fcd58a660714f3a365f5af81d +local source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f 752850ddfde6ed010e1c71d41401f1ddcb65074c036ee573015847cd290226f6 +local source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e 95ffa9f70cd68cdf14461d9830dadf8b70c80130d899d9fa09b03bc4d457b256 +local source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad c74d16b2363b56aba07cf783024bed71a76f1982001cdbd95ba2a53a76455654 +local source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 d9b1bbcf51ed48b8a9c918fac4e9044430b90be9ef3d64f7fc75005c876c6fd8 +local source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 3f3ac8b79dce2b1f94495deabe74fd9263263c3f6c955ca48f6c771c4b00202d +local source_resolve ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe 3f3bc11e7133fd96404bf87417f2de3f8bb4ced30ec296d402e9c3a825719a4c pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 @@ -175,221 +177,262 @@ pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 byt pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesSplitAt 80ce2ea1592bba157f3743317b141184c7a230e9777731501daf7c778b09f6b7 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 bytesEq? 2beb6faf5804bb74130d24e431756623c25b838278f6f13a3f326b2f0dee1eed pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 showNumber 86d5ac9b3f3bb31505178e1210d130174f88b47dce3736731ca15f0b482d3787 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 version 1190521ada412b467553aaf0cc540667821741d8bde7c0343b46d309e7f83ab1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 io ae80031948c3a6ab9fa4f4962e25bdaba7e8be06816f816375e7b6f7304b9a89 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStr 1cc1a851418cfbe855b4a0f0c8d23c4380280c2c4008eb96df2fdbc4e47b9722 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 getLine 03813fbad2501ec62e14805b2e1f994bfeebf3682e54316e7f2db643b84fb735 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readSourceFile 165a232c5b6e5b0408cbdcc632b8e5099a88d23642557a4bb040f21f5b4f4fda -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 encodeSourceText a1512d86ebf4b97888c0fc0735afa3d88707bd40effaa4821d1b7418ef9face4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFile 95466126174ef4eeb94ff7b0f3a48c7f01ea9ee8f05a95021ee0ac629b4a4333 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFile cc1e7e113fa126fdca8a2b8548c76cb5cd483a1d90f4cedd6e162faa18a4e718 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putBytes b254a61ddc3baa7ac678e123d52633145869801dd0bae84aa6903283ce5c72e9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytes 6d06955236b0354457412176c5e17b034aa5be2670bc5eec5acec2c4a081be9b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 listDirectory f91c1d33b0025c6f5f553ad8cabc7df357297c6e5203eb163cd4210c3a11a154 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 renameFile c80579e837f4233a8e08ad21d9c3e5f9fb1131fffa59ee2d44b133a533033ab3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 createDirectory 512e71f670a0e07f6a02ace6ebdba6b3f21a834bec13caf6d728a137792e5e1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 deleteFile f43cb3dc29d5d7cb2a6a0d13e3eb8b4f618f7eff4dd7c6af63194d1941b88043 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fileExists 6a168b3e7b798b77605a6f48dea8d2094ca67b609393a7451b01de29f31d0a73 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sha256Hex 492519ab08692be28339380e167f48cac112f0ca2d67e667a81e17e1338d29f4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 currentTime 918f2871e8e54704654a971f671a1a8004d92727ca6a569638a410d4f0901f77 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeBytesIfAbsent 9d90fc15170706682baddbbc3bcedd6da2c1c93fa68fddd0f718ec644d71923b -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 atomicWriteBytes 8b0d739ae174b00d8cab12fd9bccfc5c81894b4ad5ce2f6b9c5d0d6a07bf7d1c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readTreeTerm 579d1baadd8d70db149e64e1fe60394810b065faaae629cf0284a763d1409401 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashTreeTerm 4840ff06f6b8693c1c2d2b00d33a5242a78377fb3f24b0778e39df1da7d61ab6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeTreeTermIfAbsent ec0c3521fe05d920314dd856ab7f7d7cff6ed5061bc1e3af2d57325cbe7bcd11 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readIndexedTree b1170961680dcc470a363f9586660b3cec60097dfed5adafda0b8a96f078a8a3 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 hashIndexedTree 9fcc9817c9cc17efaaa82a4c05e3744ee4873a4da2fbc0c8675747b3ba0ad72a -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeIndexedTreeIfAbsent 2be24f8ba24ed0d5c034479a0c7d3f63110abb8bcb510013e5deb101e9de55a1 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 ask 18645c886eabf6ab350ad414f72d223cf65daf4a2f8b8d597f6fa57f4afda9ef -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 local a8f7e7ef2f83d324183cf77c410e31d82c64c69e2ea038756264a78b97f5d5bf -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 get 4030d35421cd5f2dfda61b0a549f78c79ea3f9dbdba2a50e62922c239da3a017 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 put 324747eaa564006bb95c98801fa9000258c46df581f777a441bacfbbe152fa83 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 fork 17a42b032d440795427a4340fa178c14e8f882a2e7771f74c4e0b6838afeb64e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 await c1cfdf1a82d8120eb2d4cc94790837665b19a5170dd447e3be7c560de4f39275 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 yield 6fe0725c466e39af107f66fb58a46264993ee5fbfd39731dbbffdbf463e7dcb9 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 sleep 7f4b057e5f3e50a273cc0f7a6daedf0552f612f8f2126e7dccca589737b6e151 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 when 8b09665faf7f4a2e6a6683da8e6ea99a3b2f0bd0285b29c49a54bac50c6416b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 unless 1214c8ebddff285c93ec32f6035457c181e8efaf9ff21a87184341b4ec5a8917 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 forever 9225a2989a4e551f1a15c6c4c68cf61101815616a258078c1c6656bca64f3f21 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onOk_ 8ec7dee19f4219cc92451487a3249d130ad90299f308f3d46a25f9357cd9f262 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 mapErrIO dbd2678cc5485ef5cec81acab554c1f734da820272fd30b07f534a6956ef1c34 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 print 0ae206a0fe52729df25878e0534636578348195614f749021645f0434b83855c -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 putStrLn 1c986df43f9f9b2cce41b169e73b8a60e1f3e8b7d03e8cd752f31b7c8e19d933 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onReadFile c3f5bad81c7f3db6c6fa4c2e2c558df1c8e43e2f609a1ccff33ddc8572c0d3bc -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onWriteFile 32494a5327b403f9b55717e54fa21603d860a99d236651758bde214bb2fcae4e -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onListDirectory 2dd66c92bf58305d3dff467ef3109d9adc22622afa3519792e6dda27d2ca3c90 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onRenameFile ec3e72aed9e64a1d4d8d2cd47cf5f32a7a481eabe163ac71c67faded3df84d2f -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCreateDirectory 7aa27dca8fee18ed334431739605045bd88cb69e65d7e07a5f31049bdf9706d4 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onDeleteFile eeb6a0b9ea9cd85a995b191535070254aa1e44b037dc1bee983fdf06895de6b0 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onFileExists ca4d417a41d15350d71b791bc872b1592e09bc5e1811471e46f6f6b059f420f7 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onSha256Hex 5c22d07c558d182e6bb018f0644b14e8ea52237a2cd1ce7901ca6e1cf392a695 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 onCurrentTime f9c21321158cd04c47ea9499934a4d8503bda98d024a3b53ce0aeeaef41fe9ba -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 readFileOrPrintError 6ba54ae23d5e162c4fa4c5544d89bd5e75e44f377ce5668aeb3c7dc718d6c0e8 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 writeFileOrPrintError e6c5ac46529ac5b2b80bf140620f4bd4bd667dc77cb152049a2d670acbf65786 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 copyFile b2ff6f1f81ba11449313ef182c4db435288d2d4f59f04bc3b71c9306e8efbd50 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 finally e9b1ca5beba639f940b993b90fa572a086fdc8a2ecfbd5545da0444af0f46187 -pin io 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 bracket ddb1dc6337536666345f7968beda7615d966fa57368d4dfab7906111599bc9ed -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectTreeDomain 3bfeabcc1070dae1f0e5de1a914419533cc1500fabfb486e94865f6b0f180d13 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectFlatTreeDomain a6807a649f443075b0c9f97734b09e3ffbbc15358c80238a164011de37800479 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectManifestDomain 057c682f1268cf4bebb47db5d76107c80d45f3e53e31d129513197183af26f0b -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectTreeAbi 62784f6200b34da8ae25550ed866379bbebd03e9e529020617afbe85383803af -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectTreeKind 3bfeabcc1070dae1f0e5de1a914419533cc1500fabfb486e94865f6b0f180d13 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectEncodeOnto 437f00cda34ab38f47c7f286104693ba0895335afff1d8a7db86d0e61738fa48 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectEncode 6b23808217a87cf1ecda5ebdc722d4777db692ad9d20a142e0489529be1f22c5 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectDecode 4e0b6e95d950bfd3a8a028ca04647b9960595ca970201a3d2b94da509459a5af -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectDecodeComplete 458d45088903b9147ee6d83b54155a027334ced6f917cc127ef0073564db7d5a -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectShard e1166be9cc8f1929b65844c5327e467b70ac47c4fdb53fa3bd55ec5c79dda328 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectPath 53dac6d64a8f37732e972e4f34c6cd14c070ec63d2ce4a9c7d4f4b6acf6a6a59 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectDigest 0e7eb2f42eb3e0cac7681d7f1119d5b938fcb93574cbbba7cc8462fe331ab04b -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectHashHex 163012b579c6dc8099d87c1377c485f1e88030d585be654ee6af5afea433fcf3 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectHashValid 186d2b689605a1adbd195da486f347fac38441cca31de3f0a81221d702649e67 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectVerify 1ccdf5bfb1320dd93403b77df44497c0145b111a984dcbccd33e93e7d4ff20bb -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectGet 1332773b14bab931768db94647ecd12756a7dac1132bde4c460105ee4606072b -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectStoreAt 323dbdc3de6161fa1377f50dd4c436b6ba85485f920976c21c07261b49acc364 -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectStore a9d7e92635a1112769392bbaabb6a1f7275fc4d2fbb917106356453f6ea32f3b -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectPutTree 3b4de09659d6a81e6dc1f1b0e296db873460ca6bee496cfe9b9b0efe5131196d -pin source_object dc29e3d043b9dc8685ed05ede1c226b3f14f47c63365b623214cf709a275b3bf sourceObjectGetTree 9ee7835b6a1d2e8aa00f69cf388d9412809557096e6a2a00bd66c1a44c3709c7 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleHeader 014dbf8e671474ae3d199918887682b380a16c26c311146e08aae3026938fe42 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleEscapeByte 1b6d5b402502059107f4a1b46ca036f6804d309a98a473b72dd7906d6bd5eae9 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleEscape 60f4b264d5c1c5171f44a325127361db9e7e1f36507675464d7b88ac3e656a71 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleUnescapeCode 65d9d4574d61975ced2696b5d38b65568a5e47aaf56028b99dda19ef62f76297 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleUnescape 3acfbdfaa86ced865a3e164c4922cd5554ef70593de654fd416e3a7ce8243bb1 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleRow 996220a8ab7cf15e5c6e004e0912f027446daad01325c62c88a8457389f7c7df -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleEncodeRows f6bbad852ec1a14b80929a651ba0343c14b5dc6d6fed5f024bdaff8dfb32f04a -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleEncode 5eb453bfd8aee435062cdaccb64cb0cac7c3c56c99c2da9bd64dac31d343fd0f -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExportChar? 7c5664b8d8e95e42cc079b6e6cefb2bb23af4e09a31a9d42c12b4534b9ebc4d7 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExportNameRest b81a1cbcb247c0bd60197bec62c23cea8a7e16190b9d66eec37d6b56337b815a -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExportNameValid f97e5907e840f15823e647df99e02cf3a216aea4cec437e34c5af384c3e57c47 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleAliasByte? 3de4eb817cdc55e776c70e14345cb58365b31dca55cc6a32d8f34ca86e895dff -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleAliasTail aae14807564168269c224ba9f17a21fa19dcddde7784ea432e60089f63bee7f0 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleAliasValid e8077f24f23ab799389aecc79dac8dd0c53654d996668a282dd3c2a94be95295 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleDigest? 0b59c83ee327830d8e8e6dce33f58f2f558221fbe3f7226c643a4754f16f91a3 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleRowParse 7df610a570b86a755e5f3fd0a208bc6689248f17101d718f3825dcc70484a77e -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleRows 132b631e39f9f2350de9be0af90d38b83d4e2a09cab663b4cf28f5a2a9ee96aa -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleDecode d3b85232602be8aa4e099b17522581a8c203b24da0c8d651e77b0d3161a49559 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleEntriesValid f91b876b5c7927f1b776a47da60a009801d3e73d8df75cbc2d9b1f51b3bc4207 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModulePutManifest a543a6545513850a7ec94c3f877d4da8617d3ceab1cdd900abe2522652fd9f3c -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleGetManifest 01d4ef86fdac1ffe774c15cc00c16740a525909e6a3fb551a00cab517983983b -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleAliasPath b19a469ec2aca677d0853a139fb48b9150d74b0436882b3c3e5cb0a7387ace8d -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleAliasBytes eefe3e19e91d41292cbf3ecb91d2ef607343652160b6f07a1a73b9acc35d56b5 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleParseAliasHash aa3d5f6814de00047cd0207b4808beba291ff61665c2488e53947988f36397b7 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleParseAlias fdf6b087c1156ec4d28b8ba479056749088fddd84b9232f11ec254d04c48fcf1 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleResolveAlias 862926c0c7e6cde7d71d046796eac3e24ae0fb8471c23183a9b12ef92fcfc360 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleResolveRef aa6ebd257de32f1a92db859dcc7ba9075a7cc09214cfde771cff16a8b25402d0 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModulePinAlias 83f9ed9e20c41f83b3505f6e40b94a9d8625e95f56e433882d3f8f2448eebafa -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleGetRefManifest c89f0a565cd5f037a21febb56148bce73dc9f6bd0d897fe93b95a178c1c5fd8a -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleLoadSelected a9dc5a24892a0465d9a30e1d4293a8d7d7a28e4b30cd7db4681b862d5b70d1fa -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModulePublishPending d86a1d0530bb8a45f3a994ae578d4f8b807d4e48c056b9bf3898e7729ea48624 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModulePublishNames e2427c3f98f07cec0dd96ce89f1d82015a5293fcfa4b389c1a7d43caa49dba75 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModulePublish 8e21299f9c498d2ce39d4ea4ffd4c28b9814facb8d5968d1c7be678e97c1289f -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderScanLine e2b6fe9954b3d524fcd40e7d871ca44f650028e0eaab4a8659420ecce7a669b3 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderLine bc7197671810e39abb5151e6d605ddda0d2a149341acea79f4e141e4c72c730a -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderComment? 1a3ae5d2b063fd7ecb076a90f088c0287eca96f3bf4acc7a68e1e9f7f9e067ee -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderMask 86f358d3d18821cfb75700cc80abfbe6006b7178b882ff39cf03558d899505f4 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderError a3d4d8ec05b2eccc9e3560b3c2817e253fea0074accfbd6235bba5cf7a29939f -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderContains 6b3ea74330ba5d21a32d5c591ad048bf61d0b5a129708fdc682259d10bb0a6b6 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderRefValid 4365d749c8d6aa7cc88688a372adb21f0689c5531f0f416aaedf44760b94d857 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderEdge fa475d2d72f8a83c859d2370705248c2b232e6a5c549a7164bf9ffecae0540e5 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderNext 508718a30533482617ea73403adff34aaf954d059545c881926bee1433c97076 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderSourceShape 951ad4613377b799f7f0d1b7dc8633911cd5a89d93592c5fa9bbf4e798dcd8a6 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleUseShape 044c0a31f041c4a674f6202549023b864f9940e06409ef775e322d317b0ef2ad -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExportShape 9553e216b8a5bf4d2f24ce4e815bb6fbd9c1f50f5bf9d1faa8428d72cc3ce72e -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceHeaderScan e4e90d7ee205f3fa558b0518bed6a04d3668e3fbbdf3b2755a17045e12e7d8d7 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleHeaderStageWith de9b91a1cc879944d6e61606a0d7c747541e51d396df02f434d962d449164bad -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleHeaderStage e707f762d16e1f3080d224a1897cb146db13270dc285b50e48b9bfacbbe255e2 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleStage 35ac82349bb5f2f2a06cc3d3aa39fecd2cbe93556723f7be628825368a1f364e -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleQualified 82f06d7cb7b3d86af08ea6254728d8ce4000d96d618390ba65067d558be2bb7a -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExtendEntries e4d4227249e6917fd7e8c91d553e9fbc354c57f154189962f8343daf7b609ae5 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExtendOne 5978437522db827a54382dbffce6b1a73edd31effac23d27643bd069ddcd905c -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExtendPending b6c56684b7b2ea8efcf6b4cd72cb59ff714bad3100e99e2030c95edd2a2843dc -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExtendUsesWith 3043d74527beff937f803bc7136eb3e334b2c44ed97abe838844b7b5404ada7f -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExtendUses af0f1699057a92eb1ac220f579f96dd5a8d6a6aa65bf72276af80f05342efc65 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExportEntries 7d8f026e9c3c0dfd22457c5ba867eb35d66e65115ce194656cfca68d1412ccc3 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleExportAll 67b368b52a88fdd1356e9c37d589bdbabb5735d5e6971b525d92e71d8bbe6437 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleAddPending f9cfd11830d5e08b676b2628917cbb58537226383aedc2f017b96a0c70070dce -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleAddEntries 666341a21fb44a36ba5244c8547ce799e4253ec99d8daddc53bcc8796e9bcfa9 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleSelectPending 14802675adb54f451e30a1d3099ad72ee2158eef41856a0982e7e1515eb3d70e -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleSelectWith a8a9d3d8f75d0f7e7e89bff984c761593933ea6ea81950d971d4fcdcdf63d90f -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleSelect df9ad3118c533f447f049a141579df49da47c5dcf4099c6bdeb2ab17325e8d40 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleBuildResolvedWith ab9f0a1d3b7590a42b39e01c54dea35e08ed0890a591d81279c65fbb434db154 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleBuildStagedWith 27fc2cd0df365403763b2a413c2a18065832586da8517afdade5eca5e1ff3b30 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleBuildWith 35bd83c4f4392bed514a34629157f2f04fc5b76fa87612240586b13471e97e66 -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleBuild 124f4be25eb799d46f7d93abc6375640e607eda94233b600e303a7ab0d52e95a -pin source_module 4241d44daa8875d2c479fc02e854ee62e5b03ce45395f026d0430a80e912ab74 sourceModuleBuildStaged 728efd0fed6e6906f2bfb1eb42811ac8b5a415b239a00113b5a9f913099651ca -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockHeader 3c3714f6df9346378fa0b99e7478b862dc8c2b3ad97c5f5726988ac8339493fb -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockPath 3758c80b1bc11689ea2b8eb9418eccd80ef59cd86b9208282d033a487607bec4 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockRow e86b8b2dc49723447fb4d22024b2a29b04bc4261b4b88ea878d957faa0289b56 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockRows 4138adab05f325b243ade3838df83037a0c4ce1c675f7b1cc4514f456f476f84 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockGroups 5bffe166498aec38d0a8751d818c0b9a3dc01f5eb8c744fc8137544a3c4ea3e3 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockEncode 25d5f0b1ed1069e9fb4fbf88866474d590456d620b6234642bfabc80cb0bcf67 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockParseRow f1bfbd1696197ec6f6af58bfe43d7d15dff50e4652988a8e63edc30f720d182e -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockParseRows 945ee92e420c0e8934d41fed2e65f51cffdeb6f1fa8fceba4f432c89ff4025c8 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockDecode 7f6ca476f92ed74e4c87a2f3bc3f32eb9692ec9a9ae3527b37a361338e820cd9 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockDistinct 50459be98404ff23ea075bd73b97a90b0a6e3812c35a4bdac4892318deeefd81 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockSelectRows d6845e4fe52c5db24e843ce31c31baaacbbd34f1a50fe970e17ebba165c6fcb7 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockRowsFor cfb79d96bd8b1e102c22b9b3ec31efeaec6d85417546c84ba4434ea3d335a332 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockCheckEntries cddb28578724c4af416ac4bde2e51aba19083d1d915d283b4d017bd551a91901 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockCheckTrees d6503e78de654347057a4283cbac1a20660d58dcd195973027f603571e269a78 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockVerifyRefs d2d955ca87bc06be5c0480921d839eb6ef8baa40f84b60c5f4f27c167081b8dd -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockVerify 49878d5ebebeaae464809c7bf0f0b229b8e0ce04e08ec0b9bd6f3b6212ea4d67 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockCollect ff09380ffbe4a0766e65d9b5df80acf5e9da3c1af18ea0cda169c6f40f6c82ec -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockUseRefs bfe4b79531e7e4bd11a9eecbdf411caf180788070b8a4a1f12998c77da29c4aa -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockExportRefs 0aefa7ebcc6555fb1978118be6bdb593bd3dceda2ab9ac7fbaae8f8acf786191 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockSourceRefs b4a78253305414629d9ab5b06fba4fe248f507acd6ababc1a3e3626757cf6f71 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockUpdate 10920701156a1410ddeee49204301a33b203566afcc3dd7ca7879084a863887b -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockRewriteUses 4ec3f266233239245acb3479216ad6fe1c4a453be9a425cd531221a99f2de457 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockRewriteExports 418139a80c01c59954423f6c24dc4e23fccad9e50172312d38e174a4b3fc2f3c -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockRewriteSource 6b63a759c98c72a50252478b77f645dd7bba7916ba1e724e266281489fd6f946 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockRewriteStaged 8dec8746da9f2f79b2a2e5fd7f649e083495b9ae767d0899ccb325f88657b3db -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockGroupRefs 1263838e1498db005b1b18d142b6d5e461adcb1ee6adbb632dc3c58dff11c976 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockUniqueRefs 2f17c23d49529d804b8a23123f2d686f6b5d3dbd031f93c939c58e28d2f4bdd3 -pin source_lock eb0f1347b5ff7773d074212c4f01276398a8dcd9e01fbd61643a5ce0cdb7ec6e sourceLockBuild 47e83ad4fe3daa8bf3e9b56f721575a70007041b463636becefe6eb7bf30d6d5 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildSourcePath 7c3b8e132ff64d64b3257af737c513d021735e65490cf4167ec40ba60342ea34 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildContains 6b3ea74330ba5d21a32d5c591ad048bf61d0b5a129708fdc682259d10bb0a6b6 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildStage 15f16e238d3739b2924dd1e63e5d14542fb9afa309305866dfb63d24d73ab8f7 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGetManifest ec54d33149037c1ee5b828136d5c8456a0a754b198ad65a83f1bebfd25aad5ba -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildDone 69dbd1fa6b605b097f901e5a7a1f0d635f1f2386bca540f3633e89172791c0af -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildResolveRefs 51d53749e0c966e579ee0e22797ce656d2893bb72e1204b215afb371c2f16bfb -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGroupsFromLocals 8bb2ae51fd5488b48ef7479cf67854fa0487f9248a165e7c48c0d089922be5fc -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGroupManifest 57e2ffb3c76d71634cb3d58eec63086ac16799efe09e0eb3feb1eddb9ae1672e -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildLocalRows 77c7b92c45ce1d6251d7ebad4aa4a1f9e63dc3e9485ca8767392051061d33002 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildPinRows 1db3ed55bae74a35e81c5fe49c9eb4484b19368ab2a10514ab5600b873bc1ddf -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildAllPinRows 60edda295b215a557fd1df19bdc6cdec7ab19247e1c64c83f74342c34e39a66d -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildReceiptBytes 87ec2cc88ab699e8612364d28a4e18044dbbe8bdacc8ca76b1bc8da65b702265 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildPutReceipt 85b80d7ab4a2dd9af37a454a38281bfe0d5c383f327869c8a881c2affbafd685 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheKeyBytes 1649175a9bfcfa9074fc33b530a903652a113ab02f56e8e5def47b6247c87c1d -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCachePath 28de4286fd15ea12f0d4cbd166ba820285a86d1e87437f5bb786be2670443cb0 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheBytes 6cd52a8119d36859d4ce7c3968f07acedbd4dcb2b9e7f442b14a1c40924af7aa -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheDecode 873f3727911f46e849c9a9b4e9d40ce08a2490f9f19532856593f277e5efe0d5 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCachePublish d099ca775c62b4700477591a10f353d7d85db3829096a94e3184e241bd16990d -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildVerifyGroups 78919237f25a30807b662d354331cacf2392098a1f9e95ce2f92c34021543765 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheVerifyGroups b57a60a21f3173f3220bc7c25c018dd13e11081d8742259be509b4e19b7ec782 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCacheUse d102d37cc02504675f9b83f7a539e8e17e94564be36e8144232a91a687f8c56e -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCompile 9b86dbb47cbf2b95ef46cf0c4871dfafc9ad03f734f1ff266536f4b842adcd09 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCached 0aa88bb8efe3fcd1342246a6be8f717c8fec11b4536db49f07db2183ef8a5191 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildFinish 0953f34d5fb746f321d1412e8f38e2ee9fb037748a2a01e0d641c460e65bb689 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildReadTriPath 15aafd0e79fef6b63f98b08c66a7d24667d0ddbe285f32e7fe6879d1bcaf07a7 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildReadTri 8e5ac6c6028142d8042704c069e3ff360422b375f5843b60e0c78ea2888024a2 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCompileTriWith 6e04a2bbc7a97edb74b1986beea78efe23967639e95612db4b7e9f9f7e5b8dde -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildCompileTri a47435f075a42d79cbd840cf3b355c7a3b9540f41f3b92fff39facbfc44f9ffa -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildStableTriProfile e2d1624808dbf487d3818fa0daabcc5e6371e713936045a5f30e3f00582fac28 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildTriProfile 49802768ca04c964cde1888dbffb25a77dc9ef95422e7303ef375ac38003b74b -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildDependencies c7c11ac297d87d5e581a0f3bfa467605a074b0b6aace4958e22a21649097f182 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildWalk d1bad40a26b5bc02f8c79f913214c6f1d40175d617ec8e416bb59a9c078a2df4 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraphUnitProfile c0cbe47cdc6a85c948f10c161990c0d8cd4df155ea3b105a8dc0533a07c58c96 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraphProfile 5822e5a19adf1c714270f8db56638fed9c3318e6674102c69d62faf6a5b64f0b -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraph da85f1448bb0f644a6e471ba262cead255718def5621f065c5483cb193bcad5e -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraphBundle ebcf92ac4fcc32688a02818a3d57fed0e380f3f324350fc85593ac8d8d351f63 -pin source_build 5a1c79afb0609925c1748a45f720119d35bf6ff44ea2cf1b074971f6a222d14f sourceBuildGraphStable 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 byteLimit 700a5f2c9726b8907aefa114c6ed0dc85da33dc76ea58df35f2ff421408007f9 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 version 68e06c01f7444d85875f2ac7e2dced23ae13ac023f33878e42d836cce85f67ee +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 abortIO 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 thenIO 71ee0f0ae39ef2c344be0faa770118999e3a5d478f24f94b5fdc83dc9a4109df +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 mapIO 7cd3a58148e356e7c2448ddb1c1777c6b862c98cdae5dfde9ad1b57d8479f759 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 void 5618673bf37c0cb560d6dd2201cd61664dbe61a0424f30f63bc3767a158efdc6 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 onOk bc0f319df8d2a5a383c78b7492ff96a0525a6d7bdd816172e7fc5382c0ad60f6 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 onOk_ e11fa89be56acbfbc11cd66800ff07a516b362dda65b9ad2cd242ec685d81486 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 onResult cb66f10bd92df9c3d81252d7f6e503de4030cfb77b739f7a7cb7b605cdda9d31 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 onResult_ 1e3378d2ec14361da6599592547e5dec510ec8124982d874229aed3a0c291e27 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 readFile b1376dc30a642a22f9978fd35f8b067691875ca7205e740a1dc8ca9673d200a8 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 sha256Hex 44d0184bdc381a97fc2f0474c288bfd914980a3216f8b4a4b2a68b4f916f99fa +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 readSourceFile ea100db85457df924c3fcab437aac79109b4f7868cb1a58ec2038983feb143a1 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 encodeSourceText 8bb70dacf3d98c2459915216bd0fc41a6598515e5e16643b5e775f60ac76fda0 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 createDirectory 129cdf84da1769a1afdf6bd1f6df8c16e301b484186e73f06a67e7f41c4cb7f3 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 fileExists 6593afa8836c1937e2efb54aff549b1368311ca6cddbdbddb8e0a38eebc7040c +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 writeBytesIfAbsent df509fd6fd14efe94794a127783a37a679cb7b85b2f3d89d78d9202b859abaa3 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 atomicWriteBytes fee2b7c3306b6405d63004037dc88e39394981db551442ae48b44aa61cf60af3 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 hashIndexedTree ae3279cbe113048307436ac083e9f70c2d1345f45f3ad9a8f89e696f2055cef4 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 writeIndexedTreeIfAbsent 2c5850406a9cdffe8a906a0a5a9db0c18894a1bcf252e765ec1b87b59f653017 +pin compiler_io 5875aaba9430fb7b0679adcd1c6d952cae8b631e8d0b829be9581b868aaf5f90 readIndexedTree efc9f94e244b1f71fe917829726dcaa4630091ad9a977cdf1852882e12316b70 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b io 4c420a8145c6b9e58c2c5e260c67c5740bce708ab0c388a88bb732d10df9d5fc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b pure c77cf428a6fda9f6d681a1dd832f02118c43fd945fc71542ce78eaadc623b763 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b bind f5690c082a04bedc787f9d485458c06dff2238d24a0fe925d287d343d8c51b91 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fail 22449e4dfdeb4954c906ee00a4d6800a9cc0d8f6abce03126c6cc337833ad430 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b scope cdacc7588989c64178eb096987a1cba598e7b7a18b464fcb248aef91a220a389 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b currentScope 48796d86b83e516e89f19f0b47c4c5972990be15a3ef26c68719862bba8fbdc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawn bda3f66c01b6c1a322b4a5b6fbca09edc590b88a11132df69d8b5d429f007b72 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b join 40bce75ad3dc0021586c624a2b8d926df47ebbc021068cf381562c5a344586d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b discard 8826a6e9d9b409968502f93762532eb0c06cf150b87e6daf67ee28f95040eaf4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancel 1c84141b6aae1a2819ceeae693c2854847c91d2c446a4bf12a2c1e37ee8a701f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b cancelled f7c51a22822fc40706997fb2ebdb877df593ac4056aa51319df2a36d140776d4 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b request 3abfe5aab260581613a3d8106e323c74aa8142a7d0f4799213a37e3ea1b6c04f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b capabilities 365ad350151a9152911227086b5fd2e9017b371b7a7fca0e99e2f1bc5e21009a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b inspect d304682145bcc0000e937fbbe41621f4d8df7d9e8f42b0035b17167c28369b51 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdin a30698f83a9f5815c65945ae39b3912cc4264e1e79c51cee57744b6ce3f10757 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stdout 7820819dc7400d5fffeeb87e91c5e4ecb25b46775bc9800baa64b18f4d615c9f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b stderr 5c35a56643977de83a8a8e10a196b3fae231cd1a982abcb0c9492c3e9579a8d5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openReader a41d9e1938dd0a85e98897b7d57faff55cf96ba11661a9cb60c5985c5dc93fc6 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openWriter c33d2f16d8a052744f19e8072318ebc1081fbfaea98d5eac51a4e5431d899c0a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openAppender 5bbc96d76fa8f38cd21cd844592859c9efc7a598a539b6a5457a0cf9fac4f91f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openUpdate 477aea8722acfbf3d6ef4b700cd3f6f724bad1ac4a5e6400c39bc3d84df45bd0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b seekFile 859ac514c00bb6852c100ca42bc2ed515332ebd405e387baed7342f93b5c94d7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b filePosition 90cc28cb46cbb8f79e3e0a6ac138f244b8b5167157d1ee720c6713f96513fff5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncFile c29a636f6f7f35070ad12042519aed619091e393c1a0e4d61a38385ef2c31206 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b syncDirectory 6491c1c4ab72034746a799de85e31eadca9445fe12c0be9355a20ebf0ec5a608 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b fileSize 0186572ea8f1be7817406d15f910eb13c9f925af6dafb54fc808c19f980b56aa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resizeFile b1b5fcdb573ac0460bc8ff60c577a3d85a582e6aa661a642bb49e06f01dda9a3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b statPath 513f52a19b3cf872eea18eea956fe0cecb3995d07b6e134853ead065d5fbb647 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b lstatPath 68d123bb079cc232e3a8a99673771a6df1a83badf83326452c76417648a6ecd7 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeFile 18da7334b79baccc33a9538d4750c8d8b62f0f8db1cdc4910b3ce64bbb327459 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b renamePath 0619553d5cca562f93856afd2f1e7c409f696a07b6a00043bb27e167791a9344 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b createDirectory 8136017269eb4fa462c2d86e381779491de06283bf6cbf00c740da67fe986868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b removeDirectory 3cd02315dac629f0e918bea587f5b03b386d2f1070abb7d5be01b3b1632e6171 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDirectory c5edc8e8855356dc62f2aacdf1afd878f32a0662069fe450b27538199b71eadc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b nextDirectory 25e9e78a913aec06d25395d8de4b61ed561fb786166e6efbb37a22a569968edc +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv4 3667096b342a98374200ccd9a40f1d36e84cb1f39f4c740375ba29600bbd0caa +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ipv6 2b89fcafd18bb2528c95f56a6101aeea729b3b088a2d53dcffd79e462643a868 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b resolveTCP 36209d56b0a33a7966294ed8f3e2c6b7154efe08e77f7ebb3ca9952421746ead +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b listenTCP d77b1f3c7f716bdf9aa12d8983654025662170e16727e96c03077b43d7a1ea7d +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectTCP 5e8ac6f30813d03f75c71b9db8c4f2f899f932cca50a29a39a701f3bc8457909 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b acceptTCP 7851d6db408e6c1404c35a4e0d7402db5dbfc2c87d935069d603ef64f5a39eca +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b localAddress 6b6e0b431b0706db937a39c60b94cbe007ea5b6aaaecf85bdd5134f6fffeb7cf +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b peerAddress b5cff0c1c86cf8f5734b66dbd6d5de1e385c857f569d90f5bf244dfc5b6211d2 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b shutdownWrite 39ffad7fe77ce956ad53a90deb8b63e449389624f84b4a9e7847b3129b225373 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readIndexedTree 675a2f777ead07497bcbe43b50cb7a21d45f563dea9c65487e7ab39992b4733f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b hashIndexedTree 55e3e229a651db71a984f84285fa03104184b27a87f58c1b3074a0b286c254dd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeIndexedTree 3b58dad3510d46c72b12389ead4dad02a6691f138f770ce7d399805e49b79299 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b verifyIndexedTree 83d527f1d28349884095523f9e588dd3436fbdb02f6d72f4e973de2766a6fe47 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b encodeUTF8 225c42665eb739d1768052c9d636f5e2849b31e3050b41ad1c27fae3cf75d08f +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b decodeUTF8 9f18a760f70f206d2645bf7c0b712a90d418a67007998320637e8d919cb01342 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b readSome 4ad391e3e89417ceef715817ffa5582d1dd7519c04e20f0854a9dbc3135e0155 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeSome 757a88089d6ad1b7cd7c7eb9943003e894a0f6bd2b58fa1ba5467b4b2cd6ad42 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b close 870a47e4809a0589faa2cfa927c98b7d854264972f4e037a98f10f6aea58c0fd +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openStage c7e69517d9953f27587ddd34db3d2c4cbb7dba9e55b14ebded559bf70cb180e0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sealStage dff49c31ab9fbc0777a47785facb5f22ce3966e64e14114c460a2784d1f5f49b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishNew 0c3b13ceb889ae4b6b590692f4ff0b78a08aaa20906e04a574162afad2c56497 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b publishReplace 52fdb07be8d8be524571cdd4e111c2a79da1bb5ffee1221e2194baffe6e81cd3 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b openDigest 209c4d7ad24deb2796014bf101f74f983d84bf07b24cd9390e0076a9a011a052 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b updateDigest 18efd0d1e27239e35be941e2160c78b8b5a240c950a4a7f2c267f694deff58b0 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b finishDigest b5dd1cba21d949cd88a92c9807a6cedd4b81ba4c2df41eca4936cc61c40a1957 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b onOk 4ff0cdb55ebefb4145684ee870807093b5512a07ae053ab22dc896aeefaa7161 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b ensureDirectory 0a6077a21965d52676f824c35869459d79c1b973bc5b7e7f05a8a5f2078aa4d1 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectCandidates 23653bbed0fd3786dafe51703efa6bff7141655fa9368b2d7b2c387f32e2b5ea +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b connectFirstTCP 89b154e8b31849f05fc0f1f4e478e53394c1fb0ddcce2b8ea301c16a5b13d64b +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b require 773366218d5de142e18472cb59ced5ba07c452f0e6a7482e75387d459fc69272 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b dropWritten f4cc81f6e8fb8c0c8ed6342bb651a022b6f1465bd9f142a6a8e7df0d126bfe92 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b writeAll ca7a2ab201feb2c0eb7a78f67b80d0ce6bf1cafb71f5191ac81bdaa459dd27be +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b copyStream 14aa486c4f5023077b44a1dbd55679f28e8d239686d9580a2304ab758f98275a +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b monotonicNanoseconds f7780fe869308ac7dd647a049926492b47057719c6ca8c58152b4b655b151a0e +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b sleepMilliseconds f7f5080df8d1afdb724b3ff776730b40df2e61b04d626cef1e10e516bbf1d354 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b yield 3aed4eaa184bea81de330339d737a14cceddd50286879b3f9658a9488e6910c5 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b spawnProcess 58bc6e3cfae96f93853a2e34a838276aac89aa43014c97d618f60ce4b01d2c67 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b workingDirectory ac83516cd538e2f469b7b26346f87b5a25a42015561c40dd881885ce7b0d2c90 +pin io_v2 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b environmentVariable 9d94a461b9eb18c173b0181898c94f6946eb91d85fdaa15fccc649437da271d1 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readSource d8b43d13ca95600e0bbe25669b8ef7e71e4c70f2cb02f3d8b04aa081fc380308 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readText 3237c7c27678459a6fcbc46905e0ff6d4ebcfd29d302113547206756de013fed +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df encodeText 8f02e9ed4cb8298203339440aee06b9361f28384159fde6b872901baf6521379 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df writeText 40ae5ecda363784d95ebf75369cffbd29285e67835ad8a1b97edd664d8b8e65a +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df lineCursor 6876bc7f273abba02af9cfab878ee2e914b2943ed88e92616caf203b35bdfd40 +pin io_v2_text 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df readLine b65089f33a261cd6dc389b5b3cfb32fce02f724ddd17b6a8c4c85a6e4fcf53ae +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectTreeDomain 3bfeabcc1070dae1f0e5de1a914419533cc1500fabfb486e94865f6b0f180d13 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectFlatTreeDomain a6807a649f443075b0c9f97734b09e3ffbbc15358c80238a164011de37800479 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectManifestDomain 057c682f1268cf4bebb47db5d76107c80d45f3e53e31d129513197183af26f0b +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectTreeAbi 62784f6200b34da8ae25550ed866379bbebd03e9e529020617afbe85383803af +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectTreeKind 3bfeabcc1070dae1f0e5de1a914419533cc1500fabfb486e94865f6b0f180d13 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectEncodeOnto 437f00cda34ab38f47c7f286104693ba0895335afff1d8a7db86d0e61738fa48 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectEncode 6b23808217a87cf1ecda5ebdc722d4777db692ad9d20a142e0489529be1f22c5 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectDecode 4e0b6e95d950bfd3a8a028ca04647b9960595ca970201a3d2b94da509459a5af +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectDecodeComplete 458d45088903b9147ee6d83b54155a027334ced6f917cc127ef0073564db7d5a +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectShard e1166be9cc8f1929b65844c5327e467b70ac47c4fdb53fa3bd55ec5c79dda328 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectPath 53dac6d64a8f37732e972e4f34c6cd14c070ec63d2ce4a9c7d4f4b6acf6a6a59 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectDigest 21847ea49a6c8e16eb60d4af429ec837b64338289d3cab8d250ea691996c7e37 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectHashHex 163012b579c6dc8099d87c1377c485f1e88030d585be654ee6af5afea433fcf3 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectHashValid 186d2b689605a1adbd195da486f347fac38441cca31de3f0a81221d702649e67 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectVerify 1a34226da13f085810af33e11723def494b27e0cf6979ce694cdae451aa82ed3 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectGet b050c5eaec92362fe694b47c0a1503d3721d6364d34380a70f9169f9508ce17d +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectStoreAt 322f0431f39f040f2bd40ce35d7c29698188bf5e3662bf4e40da32005a76a128 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectStore e0e9af9acf08ab1a2c5abda4c761092af6a98c93eaa67e13739da7c49c34c61a +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectPutTree 52e5d360b9b255d20dccafe4f95ab078d4819db16ebcd7148daf3a966123a0d6 +pin source_object c0a03bcd8a9669fc313fe233fb7e6f31ba644900ec98e0784d92bb541a700c4f sourceObjectGetTree 91ef22e4ea1588b9fb579e9117353dafb22e764b460e94cf4fb826b37434620d +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleHeader 014dbf8e671474ae3d199918887682b380a16c26c311146e08aae3026938fe42 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleEscapeByte 1b6d5b402502059107f4a1b46ca036f6804d309a98a473b72dd7906d6bd5eae9 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleEscape 60f4b264d5c1c5171f44a325127361db9e7e1f36507675464d7b88ac3e656a71 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleUnescapeCode 65d9d4574d61975ced2696b5d38b65568a5e47aaf56028b99dda19ef62f76297 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleUnescape 3acfbdfaa86ced865a3e164c4922cd5554ef70593de654fd416e3a7ce8243bb1 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleRow 996220a8ab7cf15e5c6e004e0912f027446daad01325c62c88a8457389f7c7df +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleEncodeRows f6bbad852ec1a14b80929a651ba0343c14b5dc6d6fed5f024bdaff8dfb32f04a +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleEncode 5eb453bfd8aee435062cdaccb64cb0cac7c3c56c99c2da9bd64dac31d343fd0f +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExportChar? 7c5664b8d8e95e42cc079b6e6cefb2bb23af4e09a31a9d42c12b4534b9ebc4d7 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExportNameRest b81a1cbcb247c0bd60197bec62c23cea8a7e16190b9d66eec37d6b56337b815a +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExportNameValid f97e5907e840f15823e647df99e02cf3a216aea4cec437e34c5af384c3e57c47 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleAliasByte? 3de4eb817cdc55e776c70e14345cb58365b31dca55cc6a32d8f34ca86e895dff +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleAliasTail aae14807564168269c224ba9f17a21fa19dcddde7784ea432e60089f63bee7f0 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleAliasValid e8077f24f23ab799389aecc79dac8dd0c53654d996668a282dd3c2a94be95295 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleDigest? 0b59c83ee327830d8e8e6dce33f58f2f558221fbe3f7226c643a4754f16f91a3 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleRowParse 7df610a570b86a755e5f3fd0a208bc6689248f17101d718f3825dcc70484a77e +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleRows 132b631e39f9f2350de9be0af90d38b83d4e2a09cab663b4cf28f5a2a9ee96aa +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleDecode d3b85232602be8aa4e099b17522581a8c203b24da0c8d651e77b0d3161a49559 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleEntriesValid f91b876b5c7927f1b776a47da60a009801d3e73d8df75cbc2d9b1f51b3bc4207 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModulePutManifest 603fcbebc646b73a0c60a7c342eded1be3e82727f0bdbccf8bbe1202529c66e7 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleGetManifest 24352fbd9cec1b7a5edf78c699584eba4a7c8dc36f90a39b409dd78fcb96b1d6 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleAliasPath b19a469ec2aca677d0853a139fb48b9150d74b0436882b3c3e5cb0a7387ace8d +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleAliasBytes eefe3e19e91d41292cbf3ecb91d2ef607343652160b6f07a1a73b9acc35d56b5 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleParseAliasHash aa3d5f6814de00047cd0207b4808beba291ff61665c2488e53947988f36397b7 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleParseAlias fdf6b087c1156ec4d28b8ba479056749088fddd84b9232f11ec254d04c48fcf1 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleResolveAlias 079436cb494c897dc1535df6b6ad7d45b45988299d2cd8186e235fc77967071d +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleResolveRef fddc349f41efbd177044bad7644354646d26b963cd4459d874ebe7f519dc8a19 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModulePinAlias 379338541ca5a57b3e655d95fc3816439ca75bba4ba894731028506b0c49b582 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleGetRefManifest 85d2f452d2481585ad673e9bb65c58e08194d22eb516337ffa7f69d6119712aa +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleLoadSelected 8a5308cd696188cdc06ffd95c4514983938195aee5138c103294c82acb2cd1d6 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModulePublishPending fa98c0f669781fb8e24022acf58752b2aca100c22f552b66564a127457ae09ea +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModulePublishNames 68e3f70023f13c7c627799b240528dd5800b52a65b0337ea4ff613e933262b5f +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModulePublish 13a379dd2fa5341fa0f1e6ea5024c6d8aa45cfd6cd55fcdf3a014fe816b3131d +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderScanLine e2b6fe9954b3d524fcd40e7d871ca44f650028e0eaab4a8659420ecce7a669b3 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderLine bc7197671810e39abb5151e6d605ddda0d2a149341acea79f4e141e4c72c730a +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderComment? 1a3ae5d2b063fd7ecb076a90f088c0287eca96f3bf4acc7a68e1e9f7f9e067ee +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderMask 86f358d3d18821cfb75700cc80abfbe6006b7178b882ff39cf03558d899505f4 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderError a3d4d8ec05b2eccc9e3560b3c2817e253fea0074accfbd6235bba5cf7a29939f +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderContains 6b3ea74330ba5d21a32d5c591ad048bf61d0b5a129708fdc682259d10bb0a6b6 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderRefValid 4365d749c8d6aa7cc88688a372adb21f0689c5531f0f416aaedf44760b94d857 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderEdge fa475d2d72f8a83c859d2370705248c2b232e6a5c549a7164bf9ffecae0540e5 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderNext 508718a30533482617ea73403adff34aaf954d059545c881926bee1433c97076 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderSourceShape 951ad4613377b799f7f0d1b7dc8633911cd5a89d93592c5fa9bbf4e798dcd8a6 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleUseShape 044c0a31f041c4a674f6202549023b864f9940e06409ef775e322d317b0ef2ad +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExportShape 9553e216b8a5bf4d2f24ce4e815bb6fbd9c1f50f5bf9d1faa8428d72cc3ce72e +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceHeaderScan e4e90d7ee205f3fa558b0518bed6a04d3668e3fbbdf3b2755a17045e12e7d8d7 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleHeaderStageWith de9b91a1cc879944d6e61606a0d7c747541e51d396df02f434d962d449164bad +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleHeaderStage e707f762d16e1f3080d224a1897cb146db13270dc285b50e48b9bfacbbe255e2 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleStage 35ac82349bb5f2f2a06cc3d3aa39fecd2cbe93556723f7be628825368a1f364e +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleQualified 82f06d7cb7b3d86af08ea6254728d8ce4000d96d618390ba65067d558be2bb7a +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExtendEntries ce47813b79a38f30cc77ae72790d12ee751a21e626084e4d3c942663732974ab +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExtendOne 323600d5cbec2610b0a45773a225f578621fcf3a929081fd486ddd74c5c0c34b +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExtendPending 911b068a7a019fb19bcd6981765eef03272b288f11027918acb41887386ed745 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExtendUsesWith 70ab55558ebb7dc5b0689e02b515d221232d9189b3e144f7da3b904ae223d52b +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExtendUses 855152434a3c558daa3b7a4b359470a647f83d003190e4cd7eb58504ac1e6400 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExportEntries 41a812c804c9bb2415fc6a0b51ee6031d11e1dfae1d35b2cc528ae2b8d1fa3a7 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleExportAll 70bb9f9de4d5812c3e8f198735d7418f8b843c1cd33341368319be2b857c72ed +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleAddPending 2d9aded4d1645512e8e4c05f3eee888e9e38a7d54cc00e22b0de9c2cf47f648c +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleAddEntries 245747bc50caf3dc2eec98b5cdc6d40685f1679a9b00f5a7fdc16981b7bacc15 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleSelectPending 998a8345478576f0728c5c820e0cf7d7275d911d5cbc4b331a10520432dde11c +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleSelectWith bef5db09c5c6952572ac28addc85c30d43193551484fff46f551e3bad4cdd6c4 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleSelect fb4a9cc81befe5f59a25349e0e8f07aba461ceda02070ca276f939ddb3a8594f +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleBuildResolvedWith 7b7fc11469014762ff0917ce2342c18d30702b4a0b1f6505084ed4ae46dc4c35 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleBuildStagedWith 2f7134bb3f1338d7c29b604af30cbff73009b49c10833ab46092f0d007ac15b7 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleBuildWith 5e742798fc234918ab40f4fc1682a6bf165cacd0eab95551898e7001c584be9f +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleBuild f6ef27cd557bac09efb42e696755d8ec590c49321f176bc06ed1ac84bf2a1b27 +pin source_module c47b5fce4d5fcd4d2090918928c71a7f8136a92a0eea53a8a008e8a9e77e595e sourceModuleBuildStaged 2f10ade02ee74a582f74f32965a5202573b921257d59fb3c895cde706b5ccfb7 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockHeader 3c3714f6df9346378fa0b99e7478b862dc8c2b3ad97c5f5726988ac8339493fb +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockPath 3758c80b1bc11689ea2b8eb9418eccd80ef59cd86b9208282d033a487607bec4 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockRow e86b8b2dc49723447fb4d22024b2a29b04bc4261b4b88ea878d957faa0289b56 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockRows 4138adab05f325b243ade3838df83037a0c4ce1c675f7b1cc4514f456f476f84 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockGroups 5bffe166498aec38d0a8751d818c0b9a3dc01f5eb8c744fc8137544a3c4ea3e3 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockEncode 25d5f0b1ed1069e9fb4fbf88866474d590456d620b6234642bfabc80cb0bcf67 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockParseRow f1bfbd1696197ec6f6af58bfe43d7d15dff50e4652988a8e63edc30f720d182e +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockParseRows 945ee92e420c0e8934d41fed2e65f51cffdeb6f1fa8fceba4f432c89ff4025c8 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockDecode 7f6ca476f92ed74e4c87a2f3bc3f32eb9692ec9a9ae3527b37a361338e820cd9 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockDistinct 50459be98404ff23ea075bd73b97a90b0a6e3812c35a4bdac4892318deeefd81 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockSelectRows d6845e4fe52c5db24e843ce31c31baaacbbd34f1a50fe970e17ebba165c6fcb7 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockRowsFor cfb79d96bd8b1e102c22b9b3ec31efeaec6d85417546c84ba4434ea3d335a332 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockCheckEntries 6a3dd203ce2ea90e527a04692588371ae50ffe776f4d0b6060a766b2bed5349d +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockCheckTrees 232a605ed346bcc0b3b0bcb45d5168067bc3e9ff72be36f3605f7c1fad39b3ce +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockVerifyRefs f83dbe22d1ec57e426b28a92290a51877f7623c305df896b15873b993f3d089f +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockVerify e94787b5b965ae06f354b4f8f16c20251f4478622a705a8504893782bef6005c +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockCollect f0fb6a98213a3b237f0410c571a79e6a03e2f97d17d843820764aabafd0cc984 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockUseRefs bfe4b79531e7e4bd11a9eecbdf411caf180788070b8a4a1f12998c77da29c4aa +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockExportRefs 0aefa7ebcc6555fb1978118be6bdb593bd3dceda2ab9ac7fbaae8f8acf786191 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockSourceRefs b4a78253305414629d9ab5b06fba4fe248f507acd6ababc1a3e3626757cf6f71 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockUpdate f6676cd4cfe0a125711d0b144eac43e453d58db89b923506417727465ca049d6 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockRewriteUses 4ec3f266233239245acb3479216ad6fe1c4a453be9a425cd531221a99f2de457 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockRewriteExports 418139a80c01c59954423f6c24dc4e23fccad9e50172312d38e174a4b3fc2f3c +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockRewriteSource 6b63a759c98c72a50252478b77f645dd7bba7916ba1e724e266281489fd6f946 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockRewriteStaged 8dec8746da9f2f79b2a2e5fd7f649e083495b9ae767d0899ccb325f88657b3db +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockGroupRefs 1263838e1498db005b1b18d142b6d5e461adcb1ee6adbb632dc3c58dff11c976 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockUniqueRefs 2f17c23d49529d804b8a23123f2d686f6b5d3dbd031f93c939c58e28d2f4bdd3 +pin source_lock 4681404d7a2ab19ef74f794060c31a203bb5bd599fd4acb4e1dd6d30542629ad sourceLockBuild a4059d04c503871e653c7a3611cc65861e6f8500452232222e3148e20426ad7a +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildSourcePath 7c3b8e132ff64d64b3257af737c513d021735e65490cf4167ec40ba60342ea34 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildContains 6b3ea74330ba5d21a32d5c591ad048bf61d0b5a129708fdc682259d10bb0a6b6 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildStage 15f16e238d3739b2924dd1e63e5d14542fb9afa309305866dfb63d24d73ab8f7 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGetManifest bbc73832dbe39e583b5166d8ff53d1b97f2bb99f61f7693b9ca47c554e83bdee +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildDone 69dbd1fa6b605b097f901e5a7a1f0d635f1f2386bca540f3633e89172791c0af +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildResolveRefs 00c75ff6c72c67fd50afabec5096087df52c2ed0e5be2075e33d7bc85e3e60af +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGroupsFromLocals 2c6445d384f8f43981be5cb07b0e3fa41c930f71ee8479317497708b67224a0d +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGroupManifest 57e2ffb3c76d71634cb3d58eec63086ac16799efe09e0eb3feb1eddb9ae1672e +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildLocalRows 77c7b92c45ce1d6251d7ebad4aa4a1f9e63dc3e9485ca8767392051061d33002 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildPinRows 1db3ed55bae74a35e81c5fe49c9eb4484b19368ab2a10514ab5600b873bc1ddf +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildAllPinRows 60edda295b215a557fd1df19bdc6cdec7ab19247e1c64c83f74342c34e39a66d +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildReceiptBytes 87ec2cc88ab699e8612364d28a4e18044dbbe8bdacc8ca76b1bc8da65b702265 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildPutReceipt 5c3d1f286c9d1f64636565b77557450bd4f8d00ee8aae17fd7c661cbd8f3e2e2 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheKeyBytes 1649175a9bfcfa9074fc33b530a903652a113ab02f56e8e5def47b6247c87c1d +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCachePath 28de4286fd15ea12f0d4cbd166ba820285a86d1e87437f5bb786be2670443cb0 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheBytes 6cd52a8119d36859d4ce7c3968f07acedbd4dcb2b9e7f442b14a1c40924af7aa +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheDecode 873f3727911f46e849c9a9b4e9d40ce08a2490f9f19532856593f277e5efe0d5 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCachePublish 929be959e359d1eb05a6b68d1f5b4a91fa3c0d4071d1adbba3248ce02f51d14d +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildVerifyGroups 60128d7cbcd2db0c97eb3f563f4a2cdd55d627e6170f0410067f092f636f4cf5 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheVerifyGroups 129dfeea2bdd4ea380bf49f7ea5ec278fc890b61b1e197ea1bfc9e35ba26e6a3 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCacheUse 67d7ce87388fffbdcfcea5803a8d85d11ab24ff35b491d13eb3429f41d25c435 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCompile 48bf3231d26beaded1fb6ea9cafede80a0aed59e20bf90bceb9e5f6127a70e40 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCached 7025c9aed370e5a1e40252d5130c96d987619e7e50443ba66127b397fe8f0673 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildFinish 7e87a77b24d10e179abaf33ef6a1ee55059e6518d8474f45e4599874315b6cc7 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildReadTriPath 569535418ae7b3f5dbc6f1202fdb3b23b4f2ab23ec117e01d154181377d598ee +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildReadTri 0bdf706f0df43a35965a6f54c63a4a76afb32d83db2a78b13b1067267709eba9 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCompileTriWith a64b687e4a61cd61ef48fdda0cad4924af97df1bd3e5c7205fa197b420ecc134 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildCompileTri 579018e6eaf89b2942d208aad6a70c3bf777460d3aad87e29cd566d55a9add4f +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildStableTriProfile d607dae90d25471e54c53f68ad3775cfc0d0c27a02fad7e5863b98cbbd20745f +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildTriProfile 0d8a88553d8286040e984ca884881eab7865ab48db9d1a9f9ecd180a80152cf2 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildDependencies c7c11ac297d87d5e581a0f3bfa467605a074b0b6aace4958e22a21649097f182 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildWalk 02b68954ac35af7b60a291249f3f79c8c32bca4fec5ff348a5a44de7b667cfdd +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraphUnitProfile 1e5ff22147a66005214be6ca97f757645e78429d5c8326256af0cb55a68b3da5 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraphProfile 3b7a9f1d2a674485c62405e0ae2fc03a64027a48e1aa49c541ec98a186fab44c +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraph 3ff2e4ba1941518b3a330ef1b54167e09a210df0d93f3ad903e2af7df26b839c +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraphBundle f52b82056f14ff85ab0d33cea3313923366d340a34e85c320757b6a108327275 +pin source_build f7317c40767f7513c35e90c88325778170c9bed3fb8159c729aae5f06b948f74 sourceBuildGraphStable 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d pin source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 sourceCompileDefinition 2419e4f91a6b79fcde05a6b479bf26c270d331c16fccd0104cf1bba38cdbe35b pin source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 sourceCompileOrderedUsing 1c33484341a092e5a11946c39982989b9b4a9024994b07f56df31796aa6d711a pin source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 sourceCompileOrderedWith 6e1c008cb41b8e92bde306f177e830b795cd474b04b8601be8c5b9756255739c diff --git a/test/fixtures/support-receipts/source_staged_conditional_experiment.receipt b/test/fixtures/support-receipts/source_staged_conditional_experiment.receipt @@ -1,20 +1,20 @@ arboricx.build-receipt.v1 source 989783bbdb71be5447803503f60925672ff29fad15804da31c8f7f83b8c402ad -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d 271e1e5144ed8b4eecc317cf9f8a952f07f83fb62a2966ce5e0437011c508763 -local source_core 83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2 d0396c92042ff414851f64a8f42edc4ce54d117c9a6a63f6a0d0a37c25214b69 -local source_ast c6579f89e47a16694f1c08fe9825b183d8bc272e23cd744eb954c106b689c32f 4f2f2d6b88cb8b7dd0db5ca27f87d3c31b35912d62c9b99285b997449eb6aeb4 -local source_pattern d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123 521ec559ce57747ad0349c6fbfa2d3ce3c29c42c9bb1d33177eb38cdcd4642b2 -local source_compile fb41d71410ad8fb9c0da35a5ab0a6b782b9814bd7629737b710b7404648939cd 38ea36490ad3d1a7e91d3c8979c438e663535575d66f7c98cda86e7c7f8fe416 -local source_resolve ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe 048bb0fbeeb4817c58c790cc7bcb1a318d66a21939933e140aec025152ddf624 -local source_program 447aa7d321344146772a2693e83b2ab87a0058e281c42780e1bb5b24e0ea995d b2b91bef58302ec2cebdee8bdf3374976b2c588a67a72e2e4a1cd478c495af8f -local source_order 1d55b1616e892be989d2c987099c84bef61686fc67f6a3eb5b1a373626b0ca5e f6e404200e96dee8b0d8ef190721900e199ca44f78f88b199e8b34f370ce150d -local source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 91f3faa6fafd792dffefedd83caa74b5b3b0af1618a8648aa0e75eacf650db45 -local source_staging_experiment db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 0d2b58ab1f75f21368a5d5cabdfb3466e5d521bac9b18aa6c6ac07626ab9ebbc -local source_staged_list_experiment c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98 84036fb35637c1e1e7eb7c3c1b3481c9af9ad6d59fe62ba2fa8eec1d2f08fe0c +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d dd9c680147cd7887a7a969d516bc895843c165abafc8f342e618659858ff6d21 +local source_core 83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2 e93b69c307bb43c1034827f828cf896420f3e0927c0d8bc8f15f6d9627e50b3e +local source_ast c6579f89e47a16694f1c08fe9825b183d8bc272e23cd744eb954c106b689c32f 96c0b3c88de2473704eeb7256d42ee66001fdeed9fce27d31ca8ba37cc1c39f1 +local source_pattern d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123 66bdec95af1e424fe22f8892d043008f2d83422c23da0893a45dbd7676d7053a +local source_compile fb41d71410ad8fb9c0da35a5ab0a6b782b9814bd7629737b710b7404648939cd 365cf27e925e13fd80bc65cc5706f7f6ef1cb7f785415f644d23d0476b6542be +local source_resolve ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe 3f3bc11e7133fd96404bf87417f2de3f8bb4ced30ec296d402e9c3a825719a4c +local source_program 447aa7d321344146772a2693e83b2ab87a0058e281c42780e1bb5b24e0ea995d 4d631065c0199885377470c547c83aca47e64cf443a14d5ef01e2a9b9514d056 +local source_order 1d55b1616e892be989d2c987099c84bef61686fc67f6a3eb5b1a373626b0ca5e c2cd841d3c863ed466518d3614b38c572f89ba93c5c2386008cbbb23f9bbf2b3 +local source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 3f3ac8b79dce2b1f94495deabe74fd9263263c3f6c955ca48f6c771c4b00202d +local source_staging_experiment db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 11c1393dde53422e7cb5e201507cd6cb44e1021590fac92b1029d5e1e04eafae +local source_staged_list_experiment c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98 2fa7f7ffcf59d70767c293e0f6199e8769b6540525f43efd674fdaf087e5873a pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/source_staged_list_experiment.receipt b/test/fixtures/support-receipts/source_staged_list_experiment.receipt @@ -1,13 +1,13 @@ arboricx.build-receipt.v1 source 179aa28c5e59df55dc1fe9abdf616b509e8f6cb485ba5e615d956aadfd1d4a54 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output c1c062475b966bd6adae0148db1751959400f59ba73c6f84ca0e561599639e98 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d 271e1e5144ed8b4eecc317cf9f8a952f07f83fb62a2966ce5e0437011c508763 -local source_core 83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2 d0396c92042ff414851f64a8f42edc4ce54d117c9a6a63f6a0d0a37c25214b69 -local source_pattern d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123 521ec559ce57747ad0349c6fbfa2d3ce3c29c42c9bb1d33177eb38cdcd4642b2 -local source_staging_experiment db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 0d2b58ab1f75f21368a5d5cabdfb3466e5d521bac9b18aa6c6ac07626ab9ebbc +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d dd9c680147cd7887a7a969d516bc895843c165abafc8f342e618659858ff6d21 +local source_core 83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2 e93b69c307bb43c1034827f828cf896420f3e0927c0d8bc8f15f6d9627e50b3e +local source_pattern d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123 66bdec95af1e424fe22f8892d043008f2d83422c23da0893a45dbd7676d7053a +local source_staging_experiment db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 11c1393dde53422e7cb5e201507cd6cb44e1021590fac92b1029d5e1e04eafae pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/source_staging_experiment.receipt b/test/fixtures/support-receipts/source_staging_experiment.receipt @@ -1,11 +1,11 @@ arboricx.build-receipt.v1 source 1993cce4a57c4225bf46d355be9436b48ceeffbcaddffd2f5e8184550f0fe7b4 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local source_core 83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2 d0396c92042ff414851f64a8f42edc4ce54d117c9a6a63f6a0d0a37c25214b69 -local source_pattern d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123 521ec559ce57747ad0349c6fbfa2d3ce3c29c42c9bb1d33177eb38cdcd4642b2 +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local source_core 83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2 e93b69c307bb43c1034827f828cf896420f3e0927c0d8bc8f15f6d9627e50b3e +local source_pattern d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123 66bdec95af1e424fe22f8892d043008f2d83422c23da0893a45dbd7676d7053a pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/tests_stagedConditionalRunner.receipt b/test/fixtures/support-receipts/tests_stagedConditionalRunner.receipt @@ -1,13 +1,13 @@ arboricx.build-receipt.v1 source 9f9f7c11a470595fa8d92933219bdb7653bc5ff647eff7afc352504e0c29eb66 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output 5ade29f516881ba475705960a54294300c1ae56a50296d1c6cec2a8f8478f159 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d 271e1e5144ed8b4eecc317cf9f8a952f07f83fb62a2966ce5e0437011c508763 -local source_resolve ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe 048bb0fbeeb4817c58c790cc7bcb1a318d66a21939933e140aec025152ddf624 -local source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 91f3faa6fafd792dffefedd83caa74b5b3b0af1618a8648aa0e75eacf650db45 -local source_staged_conditional_experiment 43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682 5ae47c992ad2130d251d8767c1b1136b57c66cf64a95facce55f409b8279f8ec +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local parser 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d dd9c680147cd7887a7a969d516bc895843c165abafc8f342e618659858ff6d21 +local source_resolve ed934dd01014852aa2e3734444538d0c0807da41bc661cd3965eca6ed16ceffe 3f3bc11e7133fd96404bf87417f2de3f8bb4ced30ec296d402e9c3a825719a4c +local source_unit 1e395371289a7c43d4237a30b3a6c186a38039f0b7c9a87f27e64f43f4da90a9 3f3ac8b79dce2b1f94495deabe74fd9263263c3f6c955ca48f6c771c4b00202d +local source_staged_conditional_experiment 43113de593d0e70f3f94e8dafff0600d6f60b5a011d80cdc6e84166fed29b682 5d755b7b8fa91c39198c419729e397ee2db5c269b87beaf9f2fca5a40ae766ae pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/fixtures/support-receipts/tests_stagingExperiment.receipt b/test/fixtures/support-receipts/tests_stagingExperiment.receipt @@ -1,13 +1,13 @@ arboricx.build-receipt.v1 source 7a5a63f29ea34723bf72330c66f6c4b1b9a9a58988c5b8bd2d04ca9ab861fb03 -compiler 7feafa413be4da75dcd0218795679065bf5b6894f022e2eb044c0a0ba827eb86 +compiler 7183c07ce4797c9f39949045213ddba26110f9974ba0f965bf9972f36abed22d options 307cf2eafa67562f5c3a6d4555c1fc1ccb9b91f43d0b74d564f5aa0bb73ffd0f output a28053bcf2363d097dca2b03d8f55614c90898333ca1e893078ef5015a18f687 -local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 fb089d73a8923bb5211af6a7f93025c268745660ace4a6c89a38bd6e89f218f8 -local source_pattern d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123 521ec559ce57747ad0349c6fbfa2d3ce3c29c42c9bb1d33177eb38cdcd4642b2 -local source_compile fb41d71410ad8fb9c0da35a5ab0a6b782b9814bd7629737b710b7404648939cd 38ea36490ad3d1a7e91d3c8979c438e663535575d66f7c98cda86e7c7f8fe416 -local source_core 83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2 d0396c92042ff414851f64a8f42edc4ce54d117c9a6a63f6a0d0a37c25214b69 -local source_staging_experiment db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 0d2b58ab1f75f21368a5d5cabdfb3466e5d521bac9b18aa6c6ac07626ab9ebbc +local prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 2a04cc11b1b5160b487e2391f0b84e8a18741c5816327e3c925bd20f7f6a37f2 +local source_pattern d14232cb623da7a59883ae5e0c773fa71037a887a7dcf3352e48cc8dff421123 66bdec95af1e424fe22f8892d043008f2d83422c23da0893a45dbd7676d7053a +local source_compile fb41d71410ad8fb9c0da35a5ab0a6b782b9814bd7629737b710b7404648939cd 365cf27e925e13fd80bc65cc5706f7f6ef1cb7f785415f644d23d0476b6542be +local source_core 83e1dc8052f806b19ebc737998bbdfff23211b23a6bbe2ae6adbede8ad1652a2 e93b69c307bb43c1034827f828cf896420f3e0927c0d8bc8f15f6d9627e50b3e +local source_staging_experiment db8e78afce2dadd987a2e7840b1107da75d3cc9c4ac3e5cd217cf0bc2ff00790 11c1393dde53422e7cb5e201507cd6cb44e1021590fac92b1029d5e1e04eafae pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 false 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 _ 6f93378feaa8bf5ab7cefc071175a654c871bfd53f165d7d96795fb2b38483f1 pin prelude 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 true 2177bae1081d12a6f888b5efc89a27616aaf6e5df019f156a0f3d4c2d14bb270 diff --git a/test/generation_io.py b/test/generation_io.py @@ -0,0 +1,85 @@ +"""Declared generation framing, never inferred from execution or tree shape. + +Missing metadata denotes isolated historical v1. New records carry both versions: +a transition can execute v1 while producing v2, using a separately selected output +adapter. These are caller assertions bound to recorded artifact bytes, not a proof +of arbitrary worker semantics or an authority sandbox. +""" +import hashlib +import argparse + +FIELDS = ('execution_io_version', 'produced_io_version', 'produced_adapter_sha256') + + +def version(value): + if type(value) is not int or value not in (1, 2): + raise ValueError('IO version must be integer 1 or 2') + return value + + +def cli_version(text): + if text not in ('1', '2'): + raise argparse.ArgumentTypeError('IO version must be 1 or 2') + return int(text) + + +def versions(record): + if not isinstance(record, dict): + raise ValueError('invalid generation IO metadata') + present = [key in record for key in FIELDS] + if any(present) and not all(present): + raise ValueError('incomplete generation IO metadata') + if not any(present): + return 1, 1 + pin = record['produced_adapter_sha256'] + if not isinstance(pin, str) or len(pin) != 64 or any(c not in '0123456789abcdef' for c in pin): + raise ValueError('invalid produced adapter digest') + return version(record['execution_io_version']), version(record['produced_io_version']) + + +def produced_adapter(directory, record): + versions(record) + name = 'produced-io-adapter.arboricx' if 'produced_io_version' in record else 'io-adapter.arboricx' + payload = (directory / name).read_bytes() + expected = record.get('produced_adapter_sha256', record.get('adapter_sha256')) + if hashlib.sha256(payload).hexdigest() != expected: + raise ValueError('produced adapter identity mismatch') + return payload + + +def execution_flags(io_version, host='haskell'): + value = version(io_version) + if host not in ('haskell', 'zig'): + raise ValueError('selected execution host does not support IO v2') + return ['--unsafe-io'] if value == 1 else [] + + +def check_pair(first, second): + _, output = versions(first) + execution, reproduced = versions(second) + if output != execution or output != reproduced: + raise ValueError('generation IO version chain mismatch') + a = first.get('produced_adapter_sha256', first['adapter_sha256']) + b = second.get('produced_adapter_sha256', second['adapter_sha256']) + if a != second['adapter_sha256'] or a != b: + raise ValueError('generation adapter chain mismatch') + + +def check_invocation(record, command, host='haskell'): + execution, _ = versions(record) + execution_flags(execution, host) + if host != 'haskell': + return + if not isinstance(command, list) or not all(isinstance(arg, str) for arg in command): + raise ValueError('invalid generation invocation') + grants = {'--unsafe-io', '--allow-read', '--allow-write', '--allow-read-all', '--allow-write-all'} + if execution == 2 and any(arg.split('=', 1)[0] in grants for arg in command): + raise ValueError('invocation IO authority mismatch') + if execution == 1 and '--unsafe-io' not in command: + raise ValueError('invocation IO authority mismatch') + + +def compiler_flags(io_version): + # Retained v1 runtimes predate the selector. Their documented default is v1; + # do not probe failure and retry. V2 always requires the explicit new flag. + return [] if version(io_version) == 1 else ['--toolchain-io-version', '2'] diff --git a/test/handler_costs.py b/test/handler_costs.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +"""Matched-obligation handler/direct comparison and test-only check ablations. + +nix build .# +python3 test/handler_costs.py result/bin/tricu --directory /tmp/handler-costs + +No production checks are removed. Private contract bodies are shared by exposing +extra exports ONLY in a temporary compilation workspace. Every successful result +and malformed-reply control has a Python oracle; samples include full rendering. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import re +import statistics + +from handler_library import tree, ok, err, diagnostic, http +from http_headers import result, pair, number, tree_list +from source_order import Runner, end, field +from stable_bundle import byte_tree + +ROOT = Path(__file__).resolve().parent.parent +PROFILES = { + 'matched': [1, 1, 1, 1, 1], + 'without-api-digest': [0, 1, 1, 1, 1], + 'without-body-bytes': [1, 0, 1, 1, 1], + 'without-context-list': [1, 1, 0, 1, 1], + 'without-event-shape': [1, 1, 1, 0, 1], + 'without-sink-reply': [1, 1, 1, 1, 0], + 'without-added-checks': [0, 0, 0, 0, 0], +} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + parser.add_argument('--repeats', type=int, default=5) + parser.add_argument('--checks-only', action='store_true') + args = parser.parse_args() + if os.geteuid() == 0: + parser.error("OS-denial controls require non-root") + if not 3 <= args.repeats <= 10: + parser.error('--repeats must be within 3..10') + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + inputs = directory / 'inputs'; inputs.mkdir() + original_workspace = ROOT / 'test/handlers/tricu.workspace' + locations = {} + for line in original_workspace.read_text().splitlines()[1:]: + alias, location = line.split('\t') + locations[alias] = (original_workspace.parent / location).resolve() + originals = dict(locations) + for alias, exports in [('handler_artifacts', ['loadReplyC', 'byteC', 'bytesC']), ('handler_log', ['eventC', 'replyC'])]: + original = locations[alias].read_text() + extra = ''.join('!export '+name+'\n' for name in exports if '!export '+name+'\n' not in original) + path = inputs / (alias+'.tri') + lines = original.splitlines(keepends=True) + boundary = max(i for i, line in enumerate(lines) if line.startswith('!export '))+1 + prefix, suffix = ''.join(lines[:boundary]), ''.join(lines[boundary:]) + path.write_text(prefix+extra+suffix) + assert prefix+suffix == original + locations[alias] = path + locations['handler_fixture'] = ROOT / 'test/handlers/library.tri' + (inputs / 'tricu.workspace').write_text('tricu.workspace.v1\n'+''.join( + alias+'\t'+os.path.relpath(path, inputs)+'\n' for alias, path in locations.items())) + source = ROOT / 'test/handlers/equivalent.tri' + subject = inputs / 'equivalent.tri'; subject.write_bytes(source.read_bytes()) + runner = Runner(args.tricu, directory) + bundle = directory / 'equivalent.arboricx' + runner.run('compile', ['compile', '--file', subject, '--store', runner.store, '--output', bundle, '--compile-timeout', '600'], seconds=660) + pin = runner.imported(bundle, 'import') + passed = [] + + def arguments(label, values): + out = [] + for i, value in enumerate(values): + path = directory / f'{label}-{i}.tree'; path.write_bytes(tree(value)) + out += ['--arg-tree-file', path] + return out + + def invoke(label, export, values, permissions=()): + output = runner.run(label, ['run', '--module', pin, '--name', export, '--store', runner.store, + '--io', '--format', 'ternary', '--profile', *arguments(label, values), *permissions]) + text = next(re.match(r'^([012]+)', line)[1] for line in output.splitlines() if re.match(r'^[012]+', line)) + raw = bytes(map(int, text)); return raw[:end(raw)] + + def check(label, export, values, expected, permissions=()): + actual = invoke(label, export, values, permissions) + wanted = tree(expected) + if actual != wanted: + (directory / (label+'-actual.tree')).write_bytes(actual) + (directory / (label+'-expected.tree')).write_bytes(wanted) + raise AssertionError(label) + passed.append(label) + + # Exact structural domains, not numeric coercion: all small trees plus + # canonical values and malformed bit/tail mutations. Batch only to avoid + # process startup dominating the exhaustive octet/hex checks. + shapes = {1: [b'\0']} + for nodes in range(2, 8): + shapes[nodes] = [b'\1'+v for v in shapes[nodes-1]] + for left in range(1, nodes-1): + shapes[nodes] += [pair(a, b) for a in shapes[left] for b in shapes[nodes-1-left]] + candidates = {number(n) for n in range(1025)} + candidates.update(v for group in shapes.values() for v in group) + for n in range(256): + bits = [b'\1\0' if n >> i & 1 else b'\0' for i in range(n.bit_length())] + candidates.add(tree_list(bits+[b'\0'])) # redundant high zero + candidates.add(tree_list(bits, b'\1\0')) # improper tail + for i in range(len(bits)): + for bad in [b'\1\1\0', pair(b'\0', b'\0')]: + changed = bits.copy(); changed[i] = bad + candidates.add(tree_list(changed)) + octets = {number(n) for n in range(256)} + hexes = {number(n) for n in b'0123456789abcdef'} + def pure_check(label, export, values, expected): + actual = runner.invoke(label, pin, export, arguments(label, [values])) + wanted = tree(expected) + if actual != wanted: + (directory / (label+'-expected.tree')).write_bytes(wanted) + (directory / (label+'-actual.tree')).write_bytes(actual) + raise AssertionError(label) + passed.append(label) + candidates = sorted(candidates) + for start in range(0, len(candidates), 128): + batch = candidates[start:start+128] + pure_check(f'octets-{start}', 'validateOctets', batch, + [result(True, v, number(7)) if v in octets else result(False, tree('expected octet'), number(7)) for v in batch]) + pure_check(f'hex-{start}', 'validateHex', batch, + [b'\1\0' if v in hexes else b'\0' for v in batch]) + def length_oracle(value): + count = 0 + while value[:1] == b'\2': + count += 1 + value = field(value, 1) + return number(count) if value == b'\0' else err('not a list') + lengths = candidates + [tree([255]*n) for n in (0, 1, 7, 8, 63, 64, 255, 256, 1023, 1024, 4095, 4096)] + for start in range(0, len(lengths), 128): + batch = lengths[start:start+128] + expected = [length_oracle(value) for value in batch] + pure_check(f'lengths-{start}', 'validateLengths', batch, expected) + pure_check(f'reference-lengths-{start}', 'validateReferenceLengths', batch, expected) + pure_check('length-demand', 'lengthDemand', [], [1, err('not a list')]) + list_cases = [([], None), (list(range(256)), None), ([0, 256], 'expected octet'), + (b'\1\0', 'not a list'), (tree_list([number(0)], b'\1\0'), 'not a list'), + (tree_list([number(256)], b'\1\0'), 'expected octet')] + pure_check('byte-lists', 'validateByteLists', [v for v, _ in list_cases], + [result(True, tree(v), number(7)) if message is None else result(False, tree(message), number(7)) for v, message in list_cases]) + hashes = [] + for position in (0, 31, 63): + for char in range(256): + value = [97]*64; value[position] = char + hashes.append((tree(value), char in b'0123456789abcdef')) + for length in (0, 1, 63, 64, 65, 128): + hashes.append((tree([97]*length), length == 64)) + hashes += [(tree_list([number(97)]*64, b'\1\0'), False), + (tree_list([number(97)]*63+[pair(b'\0', b'\0')]), False)] + for start in range(0, len(hashes), 128): + batch = hashes[start:start+128] + pure_check(f'hashes-{start}', 'validateHashes', [v for v, _ in batch], + [result(True, v, number(7)) if valid else result(False, tree('expected 64 lowercase hexadecimal hash characters'), number(7)) for v, valid in batch]) + pure_check('hash-demand', 'hashDemand', [], + [result(False, tree('expected 64 lowercase hexadecimal hash characters'), number(7))]*2) + validator_cases = 2*len(candidates)+len(list_cases)+len(hashes)+2 + length_cases = 2*len(lengths)+2 + + digest = 'a'*64 + fields = [['request', 7]] + body = b'\0\xffA' + good_read, good_log = ok(byte_tree(body)), ok(0) + read_trace = ['read', digest] + def log_trace(hash=digest, status=200, context=fields): + return ['log', ['info', context, [hash, status]]] + label = ['bundle-get'] + invalid_digest = 'expected 64 lowercase hexadecimal hash characters' + cases = [ + ('success', fields, digest, good_read, good_log, ok(http(200, body)), [read_trace, log_trace()]), + ('ordinary-read-error', fields, digest, err('private path'), good_log, + ok(http(404, 'Artifact unavailable\n')), [read_trace, log_trace(status=404)]), + ('invalid-digest', fields, '../secret', good_read, good_log, + ok(http(400, invalid_digest)), [log_trace(hash='../secret', status=400)]), + ('invalid-context', b'\1\0', digest, good_read, good_log, + err(diagnostic('invalid-request', 'logging', 'context', 'context must be a proper list')), []), + ('malformed-read', fields, digest, tree(0), good_log, + err(diagnostic('invalid-response', 'artifacts', 'load', 'expected artifact-load Result with empty remainder', label)), [read_trace]), + ('read-remainder', fields, digest, result(True, byte_tree(body), tree(1)), good_log, + err(diagnostic('invalid-response', 'artifacts', 'load', 'expected artifact-load Result with empty remainder', label)), [read_trace]), + ('byte-256', fields, digest, ok([256]), good_log, + err(diagnostic('invalid-response', 'artifacts', 'load', 'expected artifact bytes', label)), [read_trace]), + ('noncanonical-byte', fields, digest, ok([pair(b'\0', b'\0')]), good_log, + err(diagnostic('invalid-response', 'artifacts', 'load', 'expected artifact bytes', label)), [read_trace]), + ('improper-bytes', fields, digest, ok(pair(tree(65), b'\1\0')), good_log, + err(diagnostic('invalid-response', 'artifacts', 'load', 'expected artifact bytes', label)), [read_trace]), + ('ordinary-log-error', fields, digest, good_read, err('offline'), + err(diagnostic('application-failure', 'logging', 'emit', 'offline', label)), [read_trace, log_trace()]), + ('malformed-log', fields, digest, good_read, tree(0), + err(diagnostic('invalid-response', 'logging', 'emit', 'expected log Result with empty remainder', label)), [read_trace, log_trace()]), + ('log-nonunit', fields, digest, good_read, ok(1), + err(diagnostic('invalid-response', 'logging', 'emit', 'expected log Result with empty remainder', label)), [read_trace, log_trace()]), + ('log-remainder', fields, digest, good_read, result(True, tree(0), tree(1)), + err(diagnostic('invalid-response', 'logging', 'emit', 'expected log Result with empty remainder', label)), [read_trace, log_trace()]), + ] + for name, context, hash, read_reply, log_reply, value, trace in cases: + for export in ['directProbe', 'handlerProbe']: + check(export+'-'+name, export, [context, hash, read_reply, log_reply], [value, trace]) + + # Counterexamples establish that the switches really omit checks, while + # invariant controls prove the envelope/path protections remain present. + invalid_byte_response = tree([*b'HTTP/1.1 200 OK\r\nContent-Type: application/vnd.arboricx.bundle\r\nContent-Length: 1\r\nConnection: close\r\n\r\n', 256]) + check('omitted-byte-check', 'directProbeProfile', + [PROFILES['without-body-bytes'], fields, digest, ok([256]), good_log], + [ok(invalid_byte_response), [read_trace, log_trace()]]) + check('envelope-still-checked', 'directProbeProfile', + [PROFILES['without-body-bytes'], fields, digest, result(True, byte_tree(body), tree(1)), good_log], + [err(diagnostic('invalid-response', 'artifacts', 'load', 'expected artifact-load Result with empty remainder', label)), [read_trace]]) + check('omitted-reply-check', 'directProbeProfile', + [PROFILES['without-sink-reply'], fields, digest, good_read, ok(1)], + [ok(http(200, body)), [read_trace, log_trace()]]) + check('omitted-api-check-with-mock', 'directProbeProfile', + [PROFILES['without-api-digest'], fields, 'bad', good_read, good_log], + [ok(http(200, body)), [['read', 'bad'], log_trace(hash='bad')]]) + + root = directory / 'artifacts'; root.mkdir() + permissions = () + # Every profile retains the backend boundary, including the all-off control. + check('raw-invalid', 'rawReader', [str(root), '../secret'], err(['invalid-digest', invalid_digest])) + check('matched-invalid', 'ioEquivalent', [PROFILES['matched'], str(root), '../secret', [0]], + ok([[http(400, invalid_digest)], [['info', [['request', 0]], ['../secret', 400]]]])) + check('all-off-invalid', 'ioEquivalent', [PROFILES['without-added-checks'], str(root), '../secret', [0]], + ok([[http(400, invalid_digest)], [['info', [['request', 0]], ['../secret', 400]]]])) + + variants = [('direct', 'ioDirect', None), + ('matched-direct', 'ioEquivalent', PROFILES['matched']), + ('library-sink', 'ioLibrarySink', None), + ('library-capture', 'ioLibraryCapture', None)] + variants += [(name, 'ioEquivalent', profile) for name, profile in PROFILES.items() if name != 'matched'] + benchmarks = [] + if not args.checks_only: + # Larger bodies expose the cost of the response-octet traversal. These + # are still warm local files, not a network or concurrent-server test. + for size, count in [(3, 20), (1024, 5)]: + body = (bytes(range(256))*((size+255)//256))[:size] + digest = hashlib.sha256(body).hexdigest() + path = root / 'objects' / digest[:3] / (digest+'.arboricx') + path.parent.mkdir(parents=True, exist_ok=True); path.write_bytes(body) + contexts = list(range(count)) + expected = ok([[http(200, body)]*count, + [['info', [['request', context]], [digest, 200]] for context in contexts]]) + # Negative permission control for each primary path, same policy. + for name, export, profile in variants[:4]: + values = ([profile] if profile is not None else []) + [str(root), digest, [0]] + denied = ok([[http(404, 'Artifact unavailable\n')], + [['info', [['request', 0]], [digest, 404]]]]) + root.chmod(0) + try: + check(f'{name}-denied-{size}', export, values, denied) + finally: + root.chmod(0o700) + rows = {name: [] for name, _, _ in variants} + for repeat in range(args.repeats): + # Deterministic rotation across fresh processes; no --repeat. + offset = (repeat*3) % len(variants) + order = variants[offset:]+variants[:offset] + for name, export, profile in order: + label = f'bench-{size}-{name}-{repeat}' + values = ([profile] if profile is not None else []) + [str(root), digest, contexts] + check(label, export, values, expected, permissions) + rows[name].append(runner.records[-1]) + for name, export, profile in variants: + samples = rows[name] + benchmarks.append(dict(variant=name, export=export, profile=profile, + body_bytes=size, requests=count, repeats=args.repeats, + seconds=statistics.median(r['invoke_seconds'] for r in samples), + seconds_min=min(r['invoke_seconds'] for r in samples), + seconds_max=max(r['invoke_seconds'] for r in samples), + allocated_bytes=statistics.median(r['allocated_bytes'] for r in samples))) + paths = {*originals.values(), ROOT / 'test/handlers/library.tri', source, + Path(__file__).resolve(), ROOT / 'test/handler_library.py', + ROOT / 'bootstrap/build-stable.arboricx', *inputs.iterdir()} + report = dict(schema='tricu.handler-costs.v1', passed=passed, benchmarks=benchmarks, + comparison='same-run obligation-matched direct versus library sink; simple direct omits handler-layer contracts', + obligations=['API digest validation', 'backend digest validation', 'service Result envelope/remainder', + 'canonical octet list', 'request context list', 'event shape before and after decoration', + 'sink Result unit/remainder', 'contract success-envelope validation', + 'identical host IO state logging', 'complete HTTP response/log rendering'], + profiles=PROFILES, validator_cases=validator_cases, length_cases=length_cases, host=str(args.tricu.resolve()), + bundle_sha256=hashlib.sha256(bundle.read_bytes()).hexdigest(), + inputs={str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(paths)}, + limits=['all allocation figures are volume, not residency', + 'direct/library share authenticated filesystem policy; trace accumulation is explicit source state, not native state', + 'ablations are test-only and never disable backend digest/path or Result envelope checks', + 'marginal ablation savings are not additive and are not pure contract execution timings', + 'full response/log observation, warm reads, no networking or concurrency']) + (directory / 'report.json').write_text(json.dumps(report, indent=2)+'\n') + print(json.dumps(report, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/test/handler_library.py b/test/handler_library.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python3 +"""Opt-in native handler-library checks (no compiler-generation changes). + +nix build .# +python3 test/handler_library.py result/bin/tricu --directory /tmp/handler-library + +Compiles source with the packaged stable compiler. Uses independent full-tree +oracles, real OS permission denials, and complete HTTP-byte expectations. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import re +import statistics + +from http_headers import number, result, tree_list +from source_order import Runner, end, field +from stable_bundle import byte_tree + +ROOT = Path(__file__).resolve().parent.parent +TRUE = b'\1\0' + + +def tree(value): + if isinstance(value, bytes): + return value + if isinstance(value, str): + return byte_tree(value.encode()) + if isinstance(value, int): + return number(value) + return tree_list([tree(v) for v in value]) + + +def ok(value): + return result(True, tree(value)) + + +def err(value): + return result(False, tree(value)) + + +def diagnostic(kind, service=0, operation=0, detail=0, labels=()): + return [kind, service, operation, detail, labels] + + +def http(status, body): + if isinstance(body, str): + body = body.encode() + phrase = {200: 'OK', 400: 'Bad Request', 404: 'Not Found'}[status] + mime = 'application/vnd.arboricx.bundle' if status == 200 else 'text/plain; charset=utf-8' + return byte_tree((f'HTTP/1.1 {status} {phrase}\r\nContent-Type: {mime}\r\n' + f'Content-Length: {len(body)}\r\nConnection: close\r\n\r\n').encode()+body) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + parser.add_argument('--benchmark', action='store_true') + args = parser.parse_args() + if os.geteuid() == 0: + parser.error("OS-denial controls require non-root") + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + source = ROOT / 'test/handlers/library.tri' + bundle = directory / 'library.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle, '--compile-timeout', '270'], seconds=300) + pin = runner.imported(bundle, 'import') + passed = [] + + def arguments(label, values): + out = [] + for index, value in enumerate(values): + path = directory / f'{label}-{index}.tree' + path.write_bytes(tree(value)) + out += ['--arg-tree-file', path] + return out + + def check(name, expected, values=(), label=None): + label = label or name + actual = runner.invoke(label, pin, name, arguments(label, values)) + wanted = tree(expected) + if actual != wanted: + (directory / (label+'-expected.tree')).write_bytes(wanted) + raise AssertionError(label) + passed.append(label) + + for name, value in [('pureValue', 42), ('handled', 8), ('override', 9), + ('restore', [9, 8]), ('lexical', 8), ('noReentry', 8), + ('escaped', 8), ('transformed', 20), ('discard', 42)]: + check(name, ok(value)) + for name, labels in [('missing', ['request-42']), ('labelRestore', []), + ('labelNested', ['outer', 'inner']), ('captureMissing', ['outer', 'inner'])]: + check(name, err(diagnostic('unhandled-request', 'store', 'read', 7, labels))) + check('malformed', err(diagnostic('invalid-computation'))) + # noIO's argument is an opaque host action; verify service/op/labels and the + # negative stdout control rather than deriving the host action as an oracle. + raw = runner.invoke('noIO', pin, 'noIO', []) + assert field(raw, 0) == b'\0' + problem = field(raw, 1, 0) + def list_values(raw): + values = [] + while raw != b'\0': + values.append(field(raw, 0)); raw = field(raw, 1) + return values + pieces = list_values(problem) + assert pieces[:3] == [tree('unhandled-request'), tree('host.io'), tree('run')] + assert pieces[4] == tree(['pure-only']) + assert 'MUST NOT PRINT' not in (directory / 'noIO.log').read_text() + passed.append('noIO') + check('invalidRequest', err(diagnostic('invalid-request', 'math', 'id', 'not a natural number'))) + check('invalidResponse', err(diagnostic('invalid-response', 'math', 'id', 'not a natural number'))) + check('brokenContract', err(diagnostic('invalid-request-contract', 'math', 'id', 1))) + check('badInputContract', err(diagnostic('invalid-request-contract', 'math', 'id', 1))) + check('badOutputContract', err(diagnostic('invalid-response-contract', 'math', 'id', 1))) + for name, value in [('checkAccepted', 10), ('checkRejected', ['declined', 12]), + ('checkMalformed', 7), ('checkMalformedLazy', 13), ('checkTruthy', 42), + ('checkRejectedLazy', 14)]: + check(name, ok(value)) + for name in ['requestStopped', 'inputBeforeReply']: + check(name, [err(diagnostic('invalid-request', 'math', 'id', 'not a natural number', ['checked'])), [], []], [[]]) + event = ['math', 'id', 17] + check('outputAfterReply', [err(diagnostic('invalid-response-contract', 'math', 'id', 321, ['checked'])), [event], []], [[[event, 321]]]) + check('sinkStopped', err(diagnostic('invalid-request', 'logging', 'emit', 'expected [level context payload]', ['sink']))) + changed = ['math', 'id', 10] + check('transformedScript', [ok(20), [changed], []], [[[changed, 30]]]) + check('rawBadLog', err(diagnostic('invalid-request', 'logging', 'emit', 'expected [level context payload]', ['raw']))) + events = lambda fields: [['info', fields, 'started'], ['warn', fields, 'finished']] + check('captured', ok([42, events([['request', 7]])])) + check('nestedContext', ok([42, events([['outer', 1], ['inner', 2]])])) + check('nestedCapture', ok([[42, events([])], []])) + check('captureOrder', ok([42, events([])])) + check('captureFailure', err(diagnostic('example', 'store', 'read', 'failed', ['failure-scope']))) + check('captureForward', ok([8, [['info', [], 'before'], ['info', [], 'after']]])) + check('invalidContext', err(diagnostic('invalid-request', 'logging', 'context', 'context must be a proper list'))) + check('badLogReply', err(diagnostic('invalid-response', 'logging', 'emit', 'expected log Result with empty remainder'))) + check('logFailure', ok(err('offline'))) + check('unknownLog', err(diagnostic('unhandled-request', 'logging', 'unknown', []))) + event = ['store', 'read', 7] + script = [[event, 8]] + check('scripted', [ok(8), [event], []], [script]) + check('scripted', [err(diagnostic('script-unused', detail=script)), [event], script], [script*2], 'script-unused') + check('scripted', [err(diagnostic('script-exhausted', 'store', 'read', 7, ['scripted'])), [event], []], [[]], 'script-empty') + wrong = [[['other', 'read', 7], 9]] + check('scripted', [err(diagnostic('script-mismatch', 'store', 'read', [wrong[0][0], event], ['scripted'])), [event], wrong], [wrong], 'script-mismatch') + check('scriptedCapture', [ok([8, []]), [event], []], [script]) + + digest = 'a'*64 + bad_digest_message = 'expected 64 lowercase hexadecimal hash characters' + invalid = err(['invalid-digest', bad_digest_message]) + check('artifactLoad', [ok(invalid), [], []], ['../secret', []], 'invalid-digest') + for export, service in [('artifactLoad', 'artifacts'), ('artifactNamed', 'source')]: + event = [service, 'load', digest] + check(export, [ok(ok(byte_tree(b'\0\xffA'))), [event], []], [digest, [[event, ok(byte_tree(b'\0\xffA'))]]]) + event = ['artifacts', 'load', digest] + check('artifactLoad', [err(diagnostic('invalid-response', 'artifacts', 'load', 'expected artifact bytes')), [event], []], + [digest, [[event, ok([256])]]], 'bad-artifact-bytes') + check('artifactLoad', [err(diagnostic('invalid-response', 'artifacts', 'load', 'expected artifact-load Result with empty remainder')), [event], []], + [digest, [[event, result(True, tree([]), tree('extra'))]]], 'bad-artifact-remainder') + event = ['artifacts', 'publish', [0, 255]] + check('artifactPublish', [ok(ok(digest)), [event], []], [[0, 255], [[event, ok(digest)]]]) + check('artifactPublish', [err(diagnostic('invalid-request', 'artifacts', 'publish', 'expected octet')), [], []], [[256], []], 'bad-publish-input') + check('artifactPublish', [err(diagnostic('invalid-response', 'artifacts', 'publish', 'expected canonical artifact digest')), [event], []], + [[0, 255], [[event, ok('bad')]]], 'bad-publish-digest') + + # The first application consumer, with an independently constructed response. + request_event = ['artifacts', 'load', digest] + log = ['info', [['request', 7]], [digest, 200]] + expected_http = http(200, b'\0\xffA') + check('getRequest', [ok([expected_http, [log]]), [request_event], []], + [digest, [[request_event, ok(byte_tree(b'\0\xffA'))]]]) + check('getDemo', ok([expected_http, [log]])) + check('getRequest', [ok([http(400, bad_digest_message), [['info', [['request', 7]], ['bad', 400]]]]), [], []], ['bad', []], 'get-invalid') + check('getRequest', [ok([http(404, 'Artifact unavailable\n'), [['info', [['request', 7]], [digest, 404]]]]), [request_event], []], + [digest, [[request_event, err('private path must not leak')]]], 'get-failed') + + # Complete handler-server policy: responses/logs/effects, not comparisons + # against an implementation-generated oracle. + def registry(method, target, body, package_reply, script, response, status, trace, label): + check('registryRequest', [ok([response, [['info', [['method', method], ['target', target]], status]]]), trace, []], + [method, target, body, package_reply, script], label) + target = '/_arboricx/bundle/hash/'+digest + event = ['artifacts', 'load', digest] + registry('GET', target, [], err('unused'), [[event, ok(byte_tree(b'abc'))]], + http(200, b'abc'), 200, [event], 'registry-get') + registry('GET', target, [], err('unused'), [[event, err('PRIVATE')]], + http(404, 'Artifact unavailable\n'), 404, [event], 'registry-redacted-read') + event = ['artifacts', 'publish', [0, 255]] + # HTTP.createdResponse uses the ordinary text/plain response MIME type. + created = byte_tree((f'HTTP/1.1 201 Created\r\nContent-Type: text/plain; charset=utf-8\r\n' + f'Content-Length: 64\r\nConnection: close\r\n\r\n{digest}').encode()) + registry('POST', '/_arboricx/bundle', [0, 255], err('unused'), [[event, ok(digest)]], + created, 201, [event], 'registry-publish') + registry('POST', '/_arboricx/bundle', [0, 255], err('unused'), [[event, err('PRIVATE')]], + http(400, 'Upload failed\n'), 400, [event], 'registry-redacted-publish') + event = ['artifacts', 'load', digest] + check('registryRequest', [err(diagnostic('invalid-response', 'artifacts', 'load', + 'expected artifact bytes', ['registry-request'])), [event], []], + ['GET', target, [], err('unused'), [[event, ok([256])]]], 'registry-invalid-service-reply') + + root = directory / 'artifacts' + root.mkdir() + grants = () # Native authority is the process OS identity, not grant flags. + def io_run(label, export, values, permissions): + output = runner.run(label, ['run', '--module', pin, '--name', export, '--store', runner.store, + '--io', '--format', 'ternary', '--profile', *arguments(label, values), *permissions]) + text = next(re.match(r'^([012]+)', line)[1] for line in output.splitlines() if re.match(r'^[012]+', line)) + raw = bytes(map(int, text)); return raw[:end(raw)] + assert io_run('registry-stdout', 'registryStdout', [], []) == ok(ok(0)) + assert 'registry status=200\n' in (directory / 'registry-stdout.log').read_text() + passed.append('registry-stdout') + body = b'\0\xffA' + digest = hashlib.sha256(body).hexdigest() + artifact_path = root / 'objects' / digest[:3] / (digest+'.arboricx') + root.chmod(0) + try: + denied = io_run('publish-denied', 'ioPublish', [str(root), byte_tree(body)], []) + finally: + root.chmod(0o700) + assert field(denied, 0) == TRUE and field(field(denied, 1, 0), 0) == b'\0' + assert not artifact_path.exists() + assert io_run('prepare-real', 'ioPrepare', [str(root)], grants) == ok(ok(0)) + assert io_run('publish-real', 'ioPublish', [str(root), byte_tree(body)], grants) == ok(ok(digest)) + assert artifact_path.read_bytes() == body + for export in ['ioLoad', 'ioNamed']: + assert io_run(export, export, [str(root), digest], grants) == ok(ok(byte_tree(body))) + assert io_run('ioCapture', 'ioCapture', [str(root), digest], grants) == ok([ok(byte_tree(body)), [['info', [], 'loaded']]]) + root.chmod(0) + try: + denied = io_run('load-denied', 'ioLoad', [str(root), digest], []) + finally: + root.chmod(0o700) + assert field(denied, 0) == TRUE and field(field(denied, 1, 0), 0) == b'\0' + assert io_run('raw-invalid', 'ioRawLoad', [str(root), '../secret'], grants) == ok(invalid) + assert io_run('get-real', 'ioGet', [str(root), digest], grants) == ok([http(200, body), [['info', [['request', 7]], [digest, 200]]]]) + passed.extend(['publish-denied', 'prepare-real', 'publish-real', 'ioLoad', 'ioNamed', 'ioCapture', 'load-denied', 'raw-invalid', 'get-real']) + # Bounded upload sessions through the source handler facade. The preexisting + # store is initialized; failed producers must leave no staging or new object. + staging = root / 'tmp' + assert io_run('lazy-io-label', 'lazyIOLabel', [0], []) == ok(7) + assert io_run('lazy-failed-continuation', 'lazyFailedContinuation', [0], []) == err(diagnostic('test-failure', 'test', 'producer')) + passed.extend(['lazy-io-label', 'lazy-failed-continuation']) + stream_body = b'incremental\0\xffpublication' + stream_hash = hashlib.sha256(stream_body).hexdigest() + assert io_run('stream-valid', 'streamChunks', [str(root), len(stream_body), + [byte_tree(stream_body[:3]), byte_tree(stream_body[3:])]], grants) == ok(ok(stream_hash)) + assert (root / 'objects' / stream_hash[:3] / (stream_hash+'.arboricx')).read_bytes() == stream_body + assert list(staging.iterdir()) == [] + passed.append('stream-valid') + for export, wanted in [('streamAbandon', ok(err('stopped'))), + ('streamProtocolFailure', err(diagnostic('test-failure', 'test', 'producer'))), + ('streamFalseAck', ok(err('Incomplete artifact stream'))), + ('streamIgnoreFailure', ok(err(['write-all', 0, ['invalid-argument', 'stream/write', '', 'none']])))]: + before = sorted(p.relative_to(root) for p in root.rglob('*')) + assert io_run(export, export, [str(root)], grants) == wanted + assert sorted(p.relative_to(root) for p in root.rglob('*')) == before + assert list(staging.iterdir()) == [] + passed.append(export) + for name, count, chunks, message in [('stream-short', 2, [[1]], 'Incomplete artifact stream'), + ('stream-excess', 1, [[1, 2]], 'Artifact exceeds declared length'), + ('stream-empty-chunk', 1, [[]], 'Empty artifact chunk'), + ('stream-bad-byte', 1, [[256]], ['write-all', 0, ['invalid-argument', 'stream/write', '', 'none']]), + ('stream-large-chunk', 4097, [[0]*4097], ['write-all', 0, ['invalid-argument', 'stream/write', '', 'none']])]: + assert io_run(name, 'streamChunks', [str(root), count, chunks], grants) == ok(err(message)) + assert list(staging.iterdir()) == [] + passed.append(name) + benchmarks = [] + if args.benchmark: + for count in (1, 20): + contexts = list(range(count)) + expected = ok([[http(200, body)]*count, + [['info', [['request', context]], [digest, 200]] for context in contexts]]) + samples = {export: [] for export in ('ioDirectMany', 'ioMany', 'ioSinkMany')} + for repeat, order in enumerate([('ioDirectMany', 'ioMany', 'ioSinkMany'), + ('ioMany', 'ioSinkMany', 'ioDirectMany'), + ('ioSinkMany', 'ioDirectMany', 'ioMany')]): + for export in order: + label = f'bench-{export}-{count}-{repeat}' + assert io_run(label, export, [str(root), digest, contexts], grants) == expected, label + samples[export].append(runner.records[-1]) + passed.append(label) + for export, rows in samples.items(): + benchmarks.append(dict(export=export, count=count, + seconds=statistics.median(row['invoke_seconds'] for row in rows), + allocated_bytes=statistics.median(row['allocated_bytes'] for row in rows))) + sources = [ROOT / 'lib/handlers.tri', *sorted((ROOT / 'lib/handlers').glob('*.tri')), + source, Path(__file__).resolve(), ROOT / 'test/handlers/tricu.workspace', + ROOT / 'demos/handlers/artifact-get.tri', ROOT / 'demos/handlers/main.tri', + ROOT / 'lib/base.tri', ROOT / 'lib/io.tri', ROOT / 'lib/io-v2.tri', ROOT / 'lib/http.tri', + ROOT / 'lib/arboricx/server.tri', ROOT / 'lib/arboricx/handlers.tri', ROOT / 'test/handlers/observe.tri', ROOT / 'test/handlers/state.tri', ROOT / 'bootstrap/build-stable.arboricx'] + report = dict(passed=passed, benchmarks=benchmarks, host=str(args.tricu.resolve()), + inputs={str(path.relative_to(ROOT)): hashlib.sha256(path.read_bytes()).hexdigest() for path in sources}) + (directory / 'report.json').write_text(json.dumps(report, indent=2)+'\n') + print(json.dumps(report, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/test/handlers/README.md b/test/handlers/README.md @@ -0,0 +1,128 @@ +# Handler library validation fixtures + +`library.tri` is the normal native-library corpus, run by +`test/handler_library.py`. Its direct benchmark intentionally uses the lighter +production server route. The library consumer has more validation obligations. + +The corpus also checks `H.checkWith` and `H.checkRequest` with transformed values, +nonempty success/rejection remainders, malformed truth flags, and divergent +unselected callbacks/payloads. Scripted requests check that rejected inputs stop +before dispatch and reply checks run after the observed request. Logger rejection +checks preserve labels and leave the sink uncalled. + +`equivalent.tri` is compiled only by `test/handler_costs.py`, through a generated +workspace. That runner exposes private contract functions by inserting export +lines into temporary copies of `handler_artifacts` and `handler_log`. The +functions' bodies are unchanged; the application/library and direct comparison +both import those same copies. No test-only switches are added to `lib/`. + +The full direct comparison has these boundaries: + +| Boundary | Direct implementation | Library implementation | +| --- | --- | --- | +| Per-request context | Proper-list check before entering the diagnostic label | `Log.withContext` | +| API digest | `Server.bundleHashC`; invalid input is an ordinary Result | `Artifacts.loadFrom` | +| Request callback protocol | `checkedInvoke`, with `B.anyC` input | `H.checkedRequest` | +| Adapter digest/path | Shared authenticated filesystem adapter | `Artifacts.withFilesystemV2` | +| Load reply | Exact shared `Artifacts.loadReplyC` | Same contract | +| Initial logging event | Exact shared `Log.eventC` | `Log.emit` | +| Decorated event | Same contract after context concatenation | `Log.withSink` | +| Logging storage | Explicit sequential source state, ordinary Result unit | `recordLog` through the test observer | +| Logging reply | Exact shared `Log.replyC` | `Log.emit` reply check | +| Application policy | Redacted 404, invalid-digest 400, terminal logger failure | Demo GET consumer | +| Final observation | Execution Result containing all responses and logs | Same output | + +Full-profile malformed-input/reply probes compare independently specified +execution diagnostics AND read/log traces. These include invalid contexts, +invalid digests, octets above 255, noncanonical naturals, improper byte lists, +invalid Result envelopes/remainders, and ordinary read/log failures. The facade's +mocked read service is deliberately separate from the real raw-reader boundary; +permission-denial and raw invalid-digest controls exercise the latter. + +Profiles are `[API-digest body-bytes context-list event-shape sink-reply]` flags. +Disabled contract callbacks use `B.anyC` or the test-local envelope-only contract. +This leaves callback/envelope plumbing in place rather than treating removal as +an isolated cycle count for a predicate. The all-off profile still checks digests +at the filesystem boundary and still checks service Result shape/remainder. +The raw-reader counterexample runs without IO permission: invalid path input must +be rejected before requesting filesystem access. + +The profiles intentionally cease to satisfy particular library invariants; their +counterexamples document that fact. They are never used as production substitutes +or to update expected behavior of the full library. Handler lookup, lifetime, +resource ownership, and security authority are not altered by this experiment. + +Reproduction and measurement boundaries are in +[Testing](../../docs/validation.md.html#obligation-matched-handler-cost-comparison). +Generated measurements and investigation notes belong outside the repository's +`docs/`, not in this fixture guide. + +## Validator optimization controls + +The cost runner also exposes `byteC` and `bytesC` in its temporary workspace and +checks batched results against Python-constructed structural domains. It covers +all canonical octets, canonical numerals through 1024, all tree shapes through +seven nodes, and mutations with invalid bits, redundant high zeros and improper +tails. Digest checks cover every octet at the first, middle and last positions, +length boundaries and malformed tails. Contracts must preserve both accepted +values and a nonempty supplied remainder, including first-error diagnostics. +`validator_cases` counts individual results separately from subprocess runs. + +`hashDemand` supplies deliberately divergent terms as the 65th element/suffix, +and after an invalid first character. These must remain unforced. The process +guard bounds an accidental demand regression. These are rejection controls, not +claims that validation terminates on every arbitrary computation supplied as data. + +The optimized octet scanner checks exact Boolean bits and canonical termination +through eight fixed continuation steps; it never calls unchecked arithmetic on input. +The byte-list contract scans without rebuilding the accepted list or creating a +Result for every successful element. This specialization is valid because the +octet contract does not transform values; it is not a replacement for general +`listOf`, whose transformation/proxy behavior remains necessary. + +The digest predicate checks disjoint structural ranges for the sixteen lowercase +hex characters and uses fixed unary fuel at the 64-character boundary. The +explicit-length `bundleHashBytes` helper retains its numeric-counter interface. +Both the API check and the raw filesystem adapter check remain enabled. + +## Large response controls + +`validateLengths` checks the specialized HTTP body-spine count; the Python +runner also checks the original general-purpose `length` against the same +independent expectations. Arbitrary small trees, malformed list tails, canonical +numerals used as lists, and lists crossing binary count boundaries are included. +`length_cases` is separate from octet/digest validation counts. `lengthDemand` +ensures elements are not evaluated, including before an improper-tail failure. +The specialized count preserves `length`'s `not a list` Result on malformed +spines; it does not assert that elements are octets. Octet checks remain at the +artifact boundaries and host send encoding. + +The HTTP count forces each successor's outer constructor before traversing the +next list cell. This avoids a per-element successor continuation chain and the +transformed list required by general `listOf`; general collection contracts and +`base.length` are unchanged. The body is still materialized, not streamed. + +The stdout sink test checks both its Result and the actual rendered status line. +A successful sink Result without the line is not accepted as a logging oracle. + +## Streaming upload demand boundaries + +The opt-in `handler_upload` producer protocol uses native incremental staging, +not a source list of chunks. `streamChunks` tests the source facade with small +preconstructed chunks; it is not the memory benchmark. Native `UploadTests` +exercise canonical decoding, exact counts, owner/lifecycle rules, atomic rename, +permissions, abandonment and teardown. `streaming_protocol.py` exercises real +HTTP fragmentation, EOF, staging visibility and admission failure. + +The streaming benchmark exposed retention in the handler interpreter itself: +lazy popped-frame tail projections and unused diagnostic label spines could +retain consumed request contexts. The interpreter now demands the popped tail's +outer list constructor and settles label list spines at host-IO boundaries. +It does not force label payloads or unselected continuations. Dynamic divergent +payload/continuation controls in `library.tri` verify those demand boundaries. +This changes evaluation demand for interpreter bookkeeping, not lexical handler +selection, forwarding, scope restoration, or ordinary Result semantics. + +The bounded live test enforces both RSS and GC-sampled live-heap limits. A body +protocol with small chunks but growing continuation history is not accepted as +bounded merely because its native writer only retains one buffer. diff --git a/test/handlers/equivalent.tri b/test/handlers/equivalent.tri @@ -0,0 +1,165 @@ +!import base as B +!import test_state as State +!import test_observe as Observe +!import handlers as H +!import handler_log as Log +!import handler_artifacts as Artifacts +!import handler_get_demo as Get +!import handler_fixture as Existing +!import arboricx_server as Server +!import http as HTTP +!export * + +-- Comparison fixture, compiled ONLY by handler_costs.py. Its temporary workspace +-- exposes the existing private contracts without changing their bodies. No +-- validation switches below are part of the production libraries. +-- Profile [API-digest body-bytes context-list event-shape sink-reply]. The adapter +-- digest/path check and service Result-envelope check are NEVER switched off. +choose 0 _ disabled = disabled +choose _ enabled _ = enabled +labels = ["bundle-get"] + +-- Same contract callback protocol/diagnostics as H.checkedRequest, sequenced +-- directly with host IO rather than interpreted requests. Contract successes +-- retain transformed values and require an empty remainder. +checked kind service operation contexts contract value next = case B.checkContract contract value of + ((t t), (validated, t)) = next validated + (t, (message, _)) = State.pure (B.err (H.diagnostic kind service operation message contexts) t) + _ = State.pure (B.err (H.diagnostic (B.append kind "-contract") service operation value contexts) t) + +-- This fixture uses explicit lambda grouping (not a different pipe precedence). +checkedInvoke service operation inputC outputC argument implementation next = + checked "invalid-request" service operation labels inputC argument (checkedArgument : do State.bind + reply <- implementation checkedArgument + checked "invalid-response" service operation labels outputC reply next) + +-- Envelope-only control for isolating deep byte-list validation. This is NOT an +-- admissible production load contract: it deliberately trusts successful bytes. +loadEnvelope value{((t t), (bytes, t))} rest = B.contractOk value rest +loadEnvelope value{(t, (message, t))} rest = B.contractOk value rest +loadEnvelope _ rest = B.contractErr "expected artifact-load Result with empty remainder" rest + +load api body digest reader next = let + emit checkedDigest = checkedInvoke "artifacts" "load" B.anyC + (choose body Artifacts.loadReplyC loadEnvelope) checkedDigest reader next + in case api of + 0 = emit digest + _ = case B.checkContract Server.bundleHashC digest of + ((t t), (checkedDigest, t)) = emit checkedDigest + (t, (message, _)) = next (B.err ["invalid-digest" message] t) + _ = State.pure (B.err (H.diagnostic "invalid-contract" "artifacts" "load" digest labels) t) + +status ((t t), _) = 200 +status (t, (["invalid-digest" message], t)) = 400 +status _ = 404 +render ((t t), (bytes, t)) = HTTP.response 200 "application/vnd.arboricx.bundle" bytes +render (t, (["invalid-digest" message], t)) = HTTP.badRequestResponse message +render _ = HTTP.errorResponse 404 "Artifact unavailable\n" + +-- Sink boundary uses the SAME event contract as Log.withSink, while the caller +-- uses that contract before decoration just like Log.emit. Context is validated +-- once per request before entering the bundle-get diagnostic label. +logWith events replies fields payload sink next = let + eventC = choose events Log.eventC B.anyC + replyC = choose replies Log.replyC B.anyC + deliver [level inner message] = case B.checkContract eventC [level (B.append fields inner) message] of + ((t t), (event, t)) = State.bind (sink event) + (reply : checked "invalid-response" "logging" "emit" labels replyC reply next) + (t, (message, _)) = State.pure (B.err (H.diagnostic "invalid-request" "logging" "emit" message labels) t) + _ = State.pure (B.err (H.diagnostic "invalid-request-contract" "logging" "emit" payload labels) t) + deliver _ = State.pure (B.err (H.diagnostic "invalid-request" "logging" "emit" payload labels) t) + in checked "invalid-request" "logging" "emit" labels eventC ["info" [] payload] deliver + +one [api body context events replies] fields digest reader sink = let + finish reply logged = case logged of + ((t t), _) = State.pure (B.ok (render reply) t) + (t, (message, _)) = State.pure (B.err (H.diagnostic "application-failure" "logging" "emit" message labels) t) + _ = State.pure (B.err (H.diagnostic "application-failure" "logging" "emit" "invalid logger reply" labels) t) + go checkedFields = load api body digest reader (reply : logWith events replies checkedFields [digest (status reply)] sink (finish reply)) + in case B.checkContract (choose context (B.listOf B.anyC) B.anyC) fields of + ((t t), (checkedFields, t)) = go checkedFields + _ = State.pure (B.err (H.diagnostic "invalid-request" "logging" "context" "context must be a proper list" []) t) +one _ _ _ _ _ = State.pure (B.err "invalid benchmark profile" t) + +-- Identical raw filesystem boundary to Artifacts.withFilesystem; it cannot be +-- disabled by a profile. Invalid raw digests never become filesystem paths. +reader root digest = case B.checkContract Server.bundleHashC digest of + ((t t), (checkedDigest, t)) = State.lift (Existing.readArtifact root checkedDigest) + (t, (message, _)) = State.pure (B.err ["invalid-digest" message] t) + _ = State.pure (B.err (H.diagnostic "invalid-contract" "artifacts" "load" digest labels) t) +record event = do State.bind + events <- State.current + State.replace (B.pair event events) + State.pure (B.ok t t) + +many profile root digest [] = State.pure (B.ok [] t) +many profile root digest [context | rest] = do State.bind + reply <- one profile [["request" context]] digest (reader root) record + case reply of + ((t t), (response, t)) = do State.bind + following <- many profile root digest rest + case following of + ((t t), (responses, t)) = State.pure (B.ok (B.pair response responses) t) + _ = State.pure following + _ = State.pure reply +many _ _ _ _ = State.pure (B.err "invalid benchmark contexts" t) +ioEquivalent profile root digest contexts = State.io <| do State.bind + State.replace [] + reply <- many profile root digest contexts + events <- State.current + case reply of + ((t t), (responses, t)) = State.pure (B.ok [responses (B.reverse events)] t) + _ = State.pure reply + +ioDirect = Existing.ioDirectMany +ioLibraryCapture = Existing.ioMany +ioLibrarySink = Existing.ioSinkMany + +-- Independent negative controls record attempted operations and compare both +-- the execution Result and trace, not just the final HTTP success output. +probeRead reply digest = do State.bind + events <- State.current + State.replace (B.pair ["read" digest] events) + State.pure reply +probeLog reply event = do State.bind + events <- State.current + State.replace (B.pair ["log" event] events) + State.pure reply +withTrace action = State.io <| do State.bind + State.replace [] + reply <- action + events <- State.current + State.pure [reply (B.reverse events)] +directProbe fields digest readReply logReply = withTrace + (one [1 1 1 1 1] fields digest (probeRead readReply) (probeLog logReply)) +handlerProbe fields digest readReply logReply = withTrace <| Observe.run + (Log.withSink (event : H.bind (H.request "test.trace" "record" ["log" event]) (_ : H.pure logReply)) + (Log.withContext fields + (H.handle "artifacts" (op arg : H.bind (H.request "test.trace" "record" ["read" arg]) + (_ : H.pure readReply)) (Get.getBundle digest)))) + +rawReader root digest = State.io (reader root digest) + +-- Explicitly unsafe profiles exist only in this fixture. Counterexamples in +-- the runner prove which invariant an omission abandons; they are not APIs. +directProbeProfile profile fields digest readReply logReply = withTrace + (one profile fields digest (probeRead readReply) (probeLog logReply)) + +-- Batched validator probes preserve nonempty contract remainders as well as +-- accepted trees. Python constructs expectations independently of predicates. +validateOctets values = B.map (value : Artifacts.byteC value 7) values +validateByteLists values = B.map (value : Artifacts.bytesC value 7) values +validateHex values = B.map Server.bundleHexByte? values +validateHashes values = B.map (value : Server.bundleHashC value 7) values + +neverReturns value = neverReturns value +-- These deliberately divergent suffixes must remain unobserved. The subprocess +-- guard turns accidental forcing into a bounded test failure, not a hung suite. +hashDemand _ = [ + (Server.bundleHashC (B.append "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" (B.pair (neverReturns t) (neverReturns t))) 7) + (Server.bundleHashC (B.pair 47 (neverReturns t)) 7)] + +validateLengths values = B.map HTTP.httpBodyLength values +validateReferenceLengths values = B.map B.length values +lengthDemand _ = [(HTTP.httpBodyLength [(neverReturns t)]) + (HTTP.httpBodyLength (B.pair (neverReturns t) (t t)))] diff --git a/test/handlers/library.tri b/test/handlers/library.tri @@ -0,0 +1,196 @@ +!import arboricx_handlers as Registry +!import io_v2_artifacts as Upload +!import test_observe as Observe +!import io_v2_storage as Storage +!import base as B +!import io_v2 as IO +!import handlers as H +!import handler_log as Log +!import handler_artifacts as Artifacts +!import handler_testing as Test +!import handler_get_demo as Get +!import arboricx_server as Server +!import http as HTTP +!export * + +constant value _ _ = H.pure value +route service target action = H.handle service (op arg : H.forward target op arg) action +read = H.request "store" "read" 7 + +pureValue = H.runPure (H.pure 42) +handled = H.runPure (H.handle "store" (constant 8) read) +override = H.runPure (H.handle "store" (constant 8) (H.handle "store" (constant 9) read)) +restore = H.runPure (H.handle "store" (constant 8) (do H.bind + first <- H.handle "store" (constant 9) read + second <- read + H.pure [first second])) +lexical = H.runPure <| H.handle "other" (constant 8) + (route "store" "other" (H.handle "other" (constant 9) read)) +noReentry = H.runPure (H.handle "store" (constant 8) (route "store" "store" read)) +missing = H.runPure (H.label "request-42" read) +labelRestore = H.runPure <| do H.bind + H.label "finished" (H.pure 0) + read +labelNested = H.runPure (H.label "outer" (H.label "inner" read)) +escaped = H.runPure (H.handle "store" (constant 8) (do H.bind + action <- H.handle "store" (constant 9) (H.pure read) + action)) +malformed = H.runPure (B.pair 999 t) + +-- Pure evaluation must never execute the host bridge. +noIO = H.runPure (H.label "pure-only" (H.liftIO (IO.fail "MUST NOT PRINT"))) +invalidRequest = H.runPure (H.checkedRequest "math" "id" B.nat? B.nat? (t t)) +invalidResponse = H.runPure (H.handle "math" (constant (t t)) (H.checkedRequest "math" "id" B.nat? B.nat? 1)) +brokenC _ _ = t +brokenContract = H.runPure (H.checkedRequest "math" "id" brokenC B.anyC 1) +-- Transformed contract values must be used, not the original request/reply. +replaceC value _ rest = B.contractOk value rest +transformed = H.runPure (H.handle "math" (op arg : H.pure arg) + (H.checkedRequest "math" "id" (replaceC 10) (replaceC 20) 1)) + +logging = do H.bind + Log.info "started" + Log.warn "finished" + H.pure 42 +captured = logging |> Log.withContext [["request" 7]] |> Log.capture |> H.runPure +nestedContext = logging |> Log.withContext [["inner" 2]] |> Log.withContext [["outer" 1]] |> Log.capture |> H.runPure +nestedCapture = logging |> Log.capture |> Log.capture |> H.runPure +captureOrder = logging |> Log.capture |> Log.withContext [["outside" 9]] |> H.runPure +captureFailure = H.label "failure-scope" (H.fail "example" "store" "read" "failed") |> Log.capture |> H.runPure +captureForward = H.runPure (H.handle "store" (constant 8) (Log.capture (do H.bind + Log.info "before" + value <- H.label "read-scope" read + Log.info "after" + H.pure value))) +captureMissing = H.runPure (H.label "outer" (Log.capture (H.label "inner" read))) +invalidContext = H.runPure (Log.withContext (t t) logging) +badLogReply = H.runPure (Log.withSink (_ : H.pure 0) (Log.info "x")) +logFailure = H.runPure (Log.withSink (_ : H.pure (B.err "offline" t)) (Log.info "x")) +discard = logging |> Log.discard |> H.runPure +unknownLog = H.runPure (Log.discard (H.request "logging" "unknown" [])) + +scripted script = Test.run script (H.label "scripted" read) +scriptedCapture script = Test.run script (Log.capture (H.label "inside" read)) +artifactLoad digest script = Test.run script (Artifacts.load digest) +artifactNamed digest script = Test.run script (Artifacts.loadFrom "source" digest) +artifactPublish bytes script = Test.run script (Artifacts.publish bytes) + +-- Test observer wraps source diagnostics as Results; production uses terminal +-- v2 outcomes. See observe.tri, not the retired v1 H.runIO contract. +ioLoad root digest = IO.io (Observe.result (Artifacts.withFilesystemV2 root 1048576 (Artifacts.load digest))) +ioRawLoad root digest = IO.io (Observe.result (Artifacts.withFilesystemV2 root 1048576 (H.request "artifacts" "load" digest))) +ioPublish root bytes = IO.io (Observe.result (Artifacts.withFilesystemV2 root 1048576 (Artifacts.publish bytes))) +ioNamed root digest = IO.io (Observe.result (Artifacts.withFilesystemForV2 "source" root 1048576 (Artifacts.loadFrom "source" digest))) +ioCapture root digest = IO.io (Observe.result (Artifacts.withFilesystemV2 root 1048576 (Log.capture (do H.bind + reply <- Artifacts.load digest + Log.info "loaded" + H.pure reply)))) +ioFailure root digest = IO.io (Observe.result (H.label "request" (Artifacts.withFilesystemV2 root 1048576 (H.fail "example" "artifacts" "load" digest)))) + +getRequest digest script = Test.run script (Log.capture (Log.withContext [["request" 7]] (Get.getBundle digest))) +ioGet root digest = IO.io (Observe.result (Log.capture (Log.withContext [["request" 7]] (Artifacts.withFilesystemV2 root 1048576 (Get.getBundle digest))))) +getDemo = Get.main +ioPrepare root = IO.io (Observe.result (Artifacts.prepareFilesystemV2 root)) +rawBadLog = H.runPure (Log.capture (H.label "raw" (H.request "logging" "emit" 0))) +transformedScript script = Test.run script (H.checkedRequest "math" "id" (replaceC 10) (replaceC 20) 1) +badRemainderC value _ = B.contractOk value 7 +badInputContract = H.runPure (H.checkedRequest "math" "id" badRemainderC B.anyC 1) +badOutputContract = H.runPure (H.handle "math" (constant 1) (H.checkedRequest "math" "id" B.anyC badRemainderC 1)) + +-- Full-response benchmark. The direct baseline is successful GETs only; it +-- constructs the same context events without the handler/contract layer. +manyGet digest [] = H.pure [] +manyGet digest [context | rest] = do H.bind + response <- Log.withContext [["request" context]] (Get.getBundle digest) + responses <- manyGet digest rest + H.pure (B.pair response responses) +manyGet _ _ = H.fail "invalid-request" "benchmark" "contexts" t +ioMany root digest contexts = IO.io <| Observe.result + (Log.capture (Artifacts.withFilesystemV2 root 1048576 (manyGet digest contexts))) +readStatus ((t t), _) = 200 +readStatus _ = 404 +directMany root digest [] = IO.pure [[] []] +directMany root digest [context | rest] = do IO.bind + loaded <- readArtifact root digest + response <- IO.pure (case loaded of + ((t t), (bytes, t)) = Server.bundleResponse bytes + _ = HTTP.errorResponse 404 "Artifact unavailable\n") + following <- directMany root digest rest + case following of + [responses logs] = IO.pure [(B.pair response responses) (B.pair ["info" [["request" context]] [digest (readStatus loaded)]] logs)] + _ = IO.pure t +directMany _ _ _ = IO.pure t +ioDirectMany root digest contexts = IO.io (IO.bind (directMany root digest contexts) (value : IO.pure (B.ok value t))) +-- A real sink records attempts through the explicit test observer, distinct +-- from Log.capture's production boundary transformer. +recordLog event = H.bind (H.request "test.trace" "record" event) (_ : H.pure (B.ok t t)) +ioSinkMany root digest contexts = IO.io (IO.bind + (Observe.run (Log.withSink recordLog + (Artifacts.withFilesystemV2 root 1048576 (manyGet digest contexts))) []) + (observed : case observed of + [((t t), (responses, t)) events] = IO.pure (B.ok [responses (B.reverse events)] t) + [failure _] = IO.pure failure + _ = IO.fail "Invalid sink observation")) +-- Direct comparison shares the real authenticated filesystem adapter, not just +-- raw reads with fewer obligations. The observer adds an execution Result only. +readArtifact root digest = IO.bind + (Observe.result (Artifacts.withFilesystemV2 root 1048576 (H.request "artifacts" "load" digest))) + (reply : case reply of + ((t t), (domain, t)) = IO.pure domain + _ = IO.fail "Invalid artifact adapter observation") + +-- Application policy checked independently of the real socket smoke. Exact +-- scripts check effects; capture checks request-local logging context. +registryRequest method target body packageReply script = Test.run script + (Log.capture (Log.withContext [["method" method] ["target" target]] + (Registry.request (_ : H.pure packageReply) method target [] body))) + +registryStdout = IO.io (Observe.result (Registry.stdoutLogV2 ["info" [] 200])) + +writeChunks [] = H.pure (B.ok t t) +writeChunks [chunk | rest] = do H.bind + written <- Upload.write chunk + case written of + ((t t), (t, t)) = writeChunks rest + _ = H.pure written +writeChunks _ = H.fail "invalid-request" "test" "chunks" t +streamChunks root expected chunks = IO.io (Observe.result (Artifacts.publishStreamV2 root expected (writeChunks chunks))) +streamAbandon root = IO.io (Observe.result (Artifacts.publishStreamV2 root 1 (do H.bind + Upload.write [65] + H.pure (B.err "stopped" t)))) +streamProtocolFailure root = IO.io (Observe.result (Artifacts.publishStreamV2 root 1 (H.fail "test-failure" "test" "producer" t))) +streamFalseAck root = IO.io (Observe.result (Artifacts.publishStreamV2 root 1 + (H.handle "artifact-stream" (_ _ : H.pure (B.ok t t)) (Upload.write [65])))) +streamIgnoreFailure root = IO.io (Observe.result (Artifacts.publishStreamV2 root 1 (do H.bind + Upload.write [256] + H.pure (B.ok t t)))) +unforcedForever value = unforcedForever value +lazyIOLabel value = IO.io (Observe.result (H.label (unforcedForever value) (H.liftIO (IO.pure 7)))) +lazyFailedContinuation value = IO.io (Observe.result + (H.bind (H.fail "test-failure" "test" "producer" t) (_ : unforcedForever value))) + +-- Independent helper controls use divergent unselected callbacks and payloads. +rejectedRemainderC _ _ = B.err "declined" 7 +malformedReplacementC _ _ = B.contractOk 99 7 +rejectedLazyC _ _ = B.err (unforcedForever 0) (unforcedForever 1) +truthyReplyC value _ = B.pair (t (t t)) (B.pair value t) +checkAccepted = H.runPure (H.checkWith (replaceC 10) (unforcedForever 0) + (unforcedForever 1) (unforcedForever 2) H.pure) +checkRejected = H.runPure (H.checkWith rejectedRemainderC (unforcedForever 0) + (message : H.pure [message 12]) (unforcedForever 1) (unforcedForever 2)) +checkMalformed = H.runPure (H.checkWith malformedReplacementC 7 + (unforcedForever 1) H.pure (unforcedForever 2)) +checkMalformedLazy = H.runPure (H.checkWith brokenC (unforcedForever 0) + (unforcedForever 1) (_ : H.pure 13) (unforcedForever 2)) +checkTruthy = H.runPure (H.checkWith truthyReplyC 42 + (unforcedForever 1) H.pure (unforcedForever 2)) +requestStopped script = Test.run script (H.label "checked" + (H.checkRequest "math" "id" B.nat? (t t) (unforcedForever 0))) +inputBeforeReply script = Test.run script (H.label "checked" + (H.checkedRequest "math" "id" B.nat? (unforcedForever 0) (t t))) +outputAfterReply script = Test.run script (H.label "checked" + (H.checkedRequest "math" "id" B.anyC badRemainderC 17)) +sinkStopped = H.runPure (H.label "sink" (Log.withSink (unforcedForever 0) + (H.request "logging" "emit" 0))) +checkRejectedLazy = H.runPure (H.checkWith rejectedLazyC (unforcedForever 0) + (_ : H.pure 14) (unforcedForever 1) (unforcedForever 2)) diff --git a/test/handlers/observe.tri b/test/handlers/observe.tri @@ -0,0 +1,34 @@ +!import base as B +!import io_v2 as IO +!import handlers as H +!export run +!export result + +-- Test observer: retain source diagnostics as data and record attempted events. +-- This is NOT the production terminal-outcome runner. Host failures/cancellation +-- still terminate normally; only H.view diagnostics are observed as Results. +-- State is explicit and sequential, never a native slot or shared child cell. +walk current events = let + finish reply = IO.pure [reply events] + bad = B.err (H.diagnostic "invalid-view" t t t []) t + in case current of + (100, value) = finish (B.ok value t) + (103, problem) = finish (B.err problem t) + (106, [["test.trace" "record" event] labels next]) = walk (next t) (B.pair event events) + (106, [["host.io" "run" action] labels next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = IO.bind action (reply : walk (next reply) events) + _ = finish bad + (106, [[service operation argument] labels next]) = finish + (B.err (H.diagnostic "unhandled-request" service operation argument labels) t) + (107, [labels body next]) = case B.checkContract (B.listOf B.anyC) labels of + ((t t), _) = IO.bind (IO.scope (owner : walk (body owner) events)) (observed : case observed of + [((t t), (value, t)) following] = walk (next value) following + [failure following] = IO.pure [failure following] + _ = IO.fail "Invalid test observation") + _ = finish bad + (108, _) = finish (B.err (H.diagnostic "state-boundary" "test.trace" "record" "No implicit child state" []) t) + _ = finish bad +run action events = walk (H.view action) events +result action = IO.bind (run action []) (observed : case observed of + [reply _] = IO.pure reply + _ = IO.fail "Invalid test observation") diff --git a/test/handlers/state.tri b/test/handlers/state.tri @@ -0,0 +1,20 @@ +!import io_v2 as IO +!export pure +!export bind +!export lift +!export current +!export replace +!export io + +-- Sequential test-only state transformer over IO v2. Each action is a function +-- from explicit state to IO [value nextState]; no host state or concurrency. +pure value state = IO.pure [value state] +bind action next state = IO.bind (action state) (reply : case reply of + [value following] = next value following + _ = IO.fail "Invalid test state action") +lift action state = IO.bind action (value : IO.pure [value state]) +current state = IO.pure [state state] +replace value _ = IO.pure [t value] +io action = IO.io (IO.bind (action []) (reply : case reply of + [value _] = IO.pure value + _ = IO.fail "Invalid test state action")) diff --git a/test/handlers/tricu.workspace b/test/handlers/tricu.workspace @@ -0,0 +1,24 @@ +tricu.workspace.v1 +handlers ../../lib/handlers.tri +handler_log ../../lib/handlers/log.tri +handler_artifacts ../../lib/handlers/artifacts.tri +handler_testing ../../lib/handlers/testing.tri +handler_get_demo ../../demos/handlers/artifact-get.tri +arboricx_server ../../lib/arboricx/server.tri +base ../../lib/base.tri +bytes ../../lib/bytes.tri +conversions ../../lib/conversions.tri +http ../../lib/http.tri +io ../../lib/io.tri +native_math ../../lib/math/native.tri +parser ../../lib/parser.tri +prelude ../../lib/prelude.tri +socket ../../lib/socket.tri +arboricx_handlers ../../lib/arboricx/handlers.tri +handler_upload ../../lib/handlers/upload.tri +arboricx_streaming ../../lib/arboricx/streaming.tri +io_v2 ../../lib/io-v2.tri +io_v2_storage ../../lib/io-v2/storage.tri +io_v2_artifacts ../../lib/io-v2/artifacts.tri +test_observe observe.tri +test_state state.tri diff --git a/test/interaction_inspection.py b/test/interaction_inspection.py @@ -100,7 +100,7 @@ def main(): assert 'MUST-NOT-PRINT' not in output assert '\\n\\ESC' in output report = json.loads(run('--as', 'io', '--json').stdout) - assert report['schema'] == 'tricu.interaction-inspection.v1' + assert report['schema'] == 'tricu.interaction-inspection.v2' assert report['bundleSha256'] == hashlib.sha256(artifact.read_bytes()).hexdigest() assert [o['path'] for o in report['occurrences']] == [ 'root', 'root.action', 'root.action.action', 'root.action.action.path', 'root.action.resume'] @@ -118,13 +118,25 @@ def main(): run('--as', 'io', '--max-depth', 257, ok=False) run('--as', 'io', '--preview-items', 0, ok=False) - for tree, expected in [(leaf, 'invalid'), (envelope(leaf, 2), 'unknown'), + for tree, expected in [(leaf, 'invalid'), (envelope(leaf, 3), 'unknown'), (envelope(action(999, leaf)), 'unknown'), (envelope(action(1, leaf)), 'invalid')]: artifact.write_bytes(bundle([('main', tree)])) failure = json.loads(run('--as', 'io', '--json', ok=False).stdout) assert expected in [o['status'] for o in failure['occurrences']] + # V2 uses named facilities; arguments and callbacks are not evaluated, + # previewed as v1 actions, or treated as evidence of backend support. + req = action(10, intern(string('file'), intern(number(1), + intern(string('open-writer'), intern(leaf, string(str(victim))))))) + artifact.write_bytes(bundle([('main', envelope(action(1, intern(req, dangerous)), 2))])) + v2 = json.loads(run('--as', 'io', '--json').stdout) + assert v2['supportedVersions'] == [1, 2] + assert [o['summary'] for o in v2['occurrences'][:3]] == [ + 'tricuIO v2', 'Bind', 'Facility file@1/open-writer'] + assert [o['status'] for o in v2['occurrences'][3:]] == ['opaque', 'opaque'] + assert victim.read_text() == 'untouched' + # Huge logical value is reachable, but remains an opaque DAG root. huge = leaf for _ in range(200): diff --git a/test/io-v2/README.md b/test/io-v2/README.md @@ -0,0 +1,1436 @@ +# IO v2: scoped tasks, streams, files and publication + +Current hosts execute canonical IO v2 envelopes. Native execution uses OS process +authority; retired grant arguments and unsupported envelope versions fail before +effects. Haskell provides filesystem, TCP/resolver, process/context and clock +facilities. Zig provides the compiler/file profile; browser hosts provide +structured core/task execution and terminal streams. Capability queries report +the selected backend's facilities. TLS, UDP and hard deadlines require additional +facilities. Historical v1 execution requires a retained runtime. + +The operation layout is a research protocol subject to revision. + +## Reproduce + +```sh +nix flake check +nix build .# +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_protocol.py result/bin/tricu \ + --directory /tmp/new-v2-protocol-check +PYTHONDONTWRITEBYTECODE=1 python3 test/interaction_inspection.py result/bin/tricu +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_streams.py result/bin/tricu \ + --directory /tmp/new-v2-stream-check +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_tcp.py result/bin/tricu \ + --directory /tmp/new-v2-tcp-check +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_directory.py result/bin/tricu \ + --directory /tmp/new-v2-directory-check +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_positioned.py result/bin/tricu \ + --directory /tmp/new-v2-positioned-check +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_files.py result/bin/tricu \ + --directory /tmp/new-v2-file-check +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_tasks.py result/bin/tricu \ + --directory /tmp/new-v2-task-check +PYTHONDONTWRITEBYTECODE=1 python3 test/io_v2_publication.py result/bin/tricu \ + --directory /tmp/new-v2-publication-check +``` + +Use new output directories. The scripts compile current source through the +packaged v2 compiler, execute under OS process authority, and record source hashes, +commands, full results and measurements. File checks cover complete binary output +through 1 MiB, direct and handled borrowing, ownership transfer, implicit joining, +acquisition failures, and cancellation after an acknowledged chunk. Copying uses +**direct truncating output**. Inputs and outputs must be +distinct even through hard links/symlinks; interruption can leave partial output. + +Task source oracles independently check the three outcomes, consumed tickets, +ordinary domain errors, partial-write progress/failure traces for the actual +`IO.writeAll` helper, child handler inheritance, contextual overrides, callback +routing, lazy label payloads, and explicit state-boundary rejection. Retention +workloads run 16/256/1024 children over fresh 4096-byte chunks in one process. They +force each chunk's list spine before returning it, then join or discard the result; +using only constant or unforced payloads would conceal important retention bugs. + +The file/task/publication scripts enforce workload-specific 128-MiB sampled RSS and 16-MiB GC maximum +live-heap guards. Allocation volume is recorded separately. Shared runner reports now also include +`rts_process`: whole-process allocated/copied bytes, MUT and GC CPU/elapsed time, +and CPU/elapsed productivity from GHC's `-s` output. Missing measurements are +absent, not invented zeros. These include loading, rendering and profiling's +forced collections; they are **not** the separately measured invocation deltas. +MUT includes allocation, interpreter and native work, not just useful reductions. These are sequential +fresh-process observations, not concurrent capacity guarantees or hard runtime +resource bounds. RSS polling can miss short peaks; RTS residency is GC-sampled. + +`IOV2Tests.hs` exercises the resource registry and driver independently of the +compiler. `IOV2TaskTests.hs` adds deterministic blocking/failure controls, lease +cancellation, fail-fast supervision, lexical cleanup, completion races, join +consumption/cycles, transfer publication, and registry reclamation. Fake backends +supply controls without production fault-injection flags. + +## Binary standard streams and reusable copying + +`IO.stdin`, `IO.stdout` and `IO.stderr` return ordinary Results containing borrowed +stream references. `IO.copyStream reader writer` composes bounded `readSome` and +`writeAll`, stopping at explicit EOF or the first error. It acquires/closes neither +endpoint and adds no host copy primitive. It expects the native read/progress +protocol; it does not validate arbitrary replacement interpreters. Error values +preserve `writeAll`'s confirmed prefix and original uncertain progress; copying is +not a transaction, a whole-message atomic write, or a whole-file retry operation. +These APIs use octet lists, **not arbitrary Unicode strings**. + +Each native run starts with exactly three inherited root entries. Repeated lookup +returns the same reference/lease for that slot, without adding registry history. +Descendants borrow them, including a first lookup while their parent is joining; +child scope exit does not revoke them. Explicit close returns `borrowed-resource`. +Root-owned handoff also returns `borrowed-resource`; descendant attempts can fail +`not-owner` first. No transfer occurs on rejection. Root settlement drains leases +and removes the references but **never closes, flushes or changes the mode of the +process handles**. Run-local references become stale afterwards. + +Each successful output operation flushes the host handle before acknowledging its +byte count. That is not durability, peer consumption or transactionality. Broken +pipes are ordinary `io-error` operation failures with `unknown` progress. A failed +or cancelled inherited write poisons that slot for the rest of the run: subsequent +writes return `invalid-state` with unknown progress rather than implicitly retrying +bytes left in a failed host buffer. The host still owns that buffer and may flush +it independently, including during process shutdown. Embedders must perform their +own recovery before reuse in a new run; teardown does not discard/rollback bytes. + +Lease exclusion is **per standard slot per run**. It does not coordinate separate +runs, arbitrary host IO, or stdout/stderr redirected to aliases of the same OS +stream. Avoid unsynchronized mixing. Terminal modes/line discipline are unchanged; +this is not raw terminal IO or a text/line codec. Existing cancellation and foreign +call limits still apply. No deadlines or platform-wide descriptor isolation exist. + +`demos/io-v2/streams.tri` provides direct, handled and attached-child pipelines. +Compile/import it as usual, then run its stored export with **`--io --no-result`** +to reserve stdout for program bytes (no `--repeat` framing): + +```sh +printf 'hello\n' | result/bin/tricu arboricx run --module "$APP_MANIFEST" \ + --store "$WORK/runtime" --name main --io --no-result > "$WORK/output" +``` + +The stream harness compares entire binary outputs and stderr, exercises actual +pipes plus file redirects, empty/boundary inputs through 1 MiB, broken pipes, +close/handoff rejection, handlers and attached children. It enforces 128-MiB RSS +and 16-MiB GC residency guards. These runs have **no final rendering or forced +profile collections**: their RTS totals are whole-process observations, not +matched comparisons with the publication/profile workloads. Native tests also +exercise shared blocked-read leases, read/write cancellation, poisoned writers, +host buffering preservation and non-closing settlement. All seven inherited-stream +native cases are included in the Nix suite. + +## Positioned files, updates and append + +`IO.openReader` opens a readable file; `IO.openWriter` creates/truncates a writable +file. `IO.openUpdate` opens a readable/writable file at position zero without +truncating an existing file, creating it if absent. `IO.openAppender` creates if +absent and writes using the host's append mode. None provides exclusive creation, +atomic replacement, snapshot isolation or publication durability. + +`IO.seekFile file offset` uses an **absolute byte offset**; `IO.filePosition file` +queries the logical cursor, including read-ahead adjustment. `IO.fileSize file` +queries the opened file, not its potentially replaced pathname. `IO.resizeFile +file size` may shrink or grow a writable non-append file, preserving the logical +cursor even beyond the new EOF. Regular-file growth reads as zero-filled space; +seeking beyond EOF alone does not extend the file. These operations use canonical +naturals through **9223372036854775807** (reported as `file-offset-max`). Smaller +filesystem/backend constraints can still fail. Malformed/out-of-range wire +arguments are rejected before that operation's backend IO; negative or excessive +host query results cannot escape as invalid naturals. + +All byte and position/size operations on a file share **one lease**. They reject +concurrent use of the same registry entry with `busy`; handoff preserves the cursor +and invalidates old aliases. A `seek` followed by a `read` is **not an atomic +compound operation**. Coordinate borrowers, transfer ownership, or use independent +read handles; no positioned `read-at`/`write-at` operation is promised here. +Separate OS handles/processes can change file contents or size independently. +Queries are point-in-time observations, not a race-free verification policy. + +Append handles have no seek, position or resize method: those calls return +`unsupported`. Reads on writers/appenders return `wrong-kind`; resize on a reader +returns `unsupported`. Standard streams and stages are not positioned-file handles, +even if their underlying storage happens to be a file. Non-seekable/special files +can report `unsupported` or backend IO errors. Haskell's process-local Handle +locking may reject conflicting file opens that POSIX alone would permit; this +restriction is advertised. Do not infer filesystem confinement or external-writer +serialization from that host behavior. + +Append placement is at the backend's current EOF on writes, not an open-time seek. +This is not a whole-record atomicity guarantee: partial writes, interleaving with +other writers and failure remain possible. IO exceptions during seek/resize report +`unknown` progress/state, not a claim that nothing changed. There is no automatic +retry, rollback, restoration after an operation failure, or new durability promise. +Ordinary Results and scope cleanup retain the existing rules. + +`demos/io-v2/positioned.tri` composes bounded patching, resizing, prefix inspection +and append. It is deliberately **in-place editing**, not a safe publication +replacement. The focused harness compares complete outputs/results, including +Unicode paths, empty files, shrinking/growth, updates/appends against 1-MiB files, +mode errors and malformed offsets. A wide-offset control writes a sparse byte +above 4 GiB and shrinks before comparing its entire final output; it does not +materialize a multi-GiB source value or assert multi-GiB streaming performance. +Native tests cover cursor mode switches, read-ahead invalidation, current-inode +size, external-fd growth before append, bounds, cancellation/lease exclusion, +transfer, host locking and uncertain mutation errors. + +## Paths and directory cursors + +`IO.statPath` follows symlinks; `IO.lstatPath` observes a final link itself. Both +use normal host resolution of intermediate components and trailing slashes. They +return a regular-file size only for a regular file; other tags deliberately do +not invent a zero byte length. Missing paths and permission errors remain ordinary +errors, not a false existence Boolean. These observations do not pin later opens +or constitute race-free authorization, immutable verification or confinement. + +`IO.createDirectory` creates one directory with host mode 0777 subject to umask; +it fails with `already-exists` rather than silently accepting another object. +`IO.ensureDirectory` is a **source** convenience: attempt creation, and on +`already-exists` use stat to check for a directory. Existing symlinks to directories +are accepted. It is nonrecursive; caller-ordered initialization fits store/cache +layouts. The check is only a point-in-time observation. Direct files retain their +host creation modes/umask, not the private-stage permission contract. + +`IO.removeFile` unlinks a file/link, `IO.removeDirectory` removes an empty directory, +and `IO.renamePath` performs the host rename/replacement operation. No recursive +removal, cross-filesystem copy fallback, no-replace rename, sync, rollback, or +transaction is supplied. Path mutations persist after scope exit: scopes own +handles, not arbitrary pathname changes. Mutation IO errors conservatively report +`unknown` progress/state. Failed close IO now also reports `unknown` (correcting +its former `none` report); this does not authorize retrying raw release operations. + +`IO.openDirectory owner path` creates a scoped cursor over the opened directory. +`IO.nextDirectory` yields **one** name at a time, skipping `.` and `..`, with an +explicit, sticky EOF. It never sorts/materializes a complete listing. Host internal +buffering is not specified. Enumeration order and concurrent directory mutation +are not snapshot guarantees. Renaming the directory does not retarget the cursor, +but later path operations built from its original pathname can target another +object; no dirfd-relative child operations are provided here. + +The cursor uses one exclusive lease and the ordinary lifetime/transfer/close +rules. Failed or cancelled advancement poisons it: subsequent next calls return +`invalid-state`; close or explicitly reopen instead of implicitly replaying an +uncertain advance. EOF does not implicitly close the cursor. Closing attempts +`closedir` once, never retrying a possibly freed native directory pointer. + +Paths use canonical Unicode-scalar text with no NUL, mapped through the host's +filesystem encoding. Directory names must be Unicode-scalar single components, +up to **4096 scalars** (advertised as `directory-name-scalars`). Undecodable names +are not silently replaced or skipped: they fail and poison enumeration. This is +not an arbitrary raw-byte POSIX pathname interface. Empty path arguments are +rejected by these path/directory operations; use `.` for the current directory. +Native capability restrictions expose host encoding, unsorted/non-snapshot +iteration and cursor poisoning. No Unicode normalization is performed. + +`demos/io-v2/directory.tri` provides caller-ordered initialization and an inventory +that intentionally accumulates entries, alongside a count that discards names and +forces its small numeric accumulator before each request. The latter avoids +retaining a lazy arithmetic chain; bounded native enumeration alone would not +make arbitrary source accumulation bounded. The focused test compares complete +inventories without assuming order, checks symlink/dangling-link and mutation +behavior, rejects a real invalid-encoding name, and consumes up to 16,384 entries +under RSS/live-heap guards. Native controls exercise ownership transfer, renamed +open directories, cancellation/busy exclusion, EOF, invalid names, poisoning and +once-only close, including uncertain/async release failures. + +## TCP listeners and duplex streams + +Endpoints are canonical tagged lists: + +- `["ipv4" [a b c d] port]`: four octets and a 16-bit port. +- `["ipv6" [a b c d e f g h] scope port]`: eight 16-bit words, a 32-bit + interface scope ID and a 16-bit port. Flow information is zero. + +`IO.ipv4` / `IO.ipv6` construct these values; they do not resolve names. +`IO.listenTCP owner endpoint backlog` acquires an already bound/listening socket. +Backlog must be 1–4096; OS clamping/queue policy still applies. Port zero requests +an ephemeral listening port; query `IO.localAddress` to learn it. IPv6 listeners +are explicitly IPv6-only, not silently dual-stack. Reuse-address is enabled; +there is no reuse-port, ownership or exclusive-bind security guarantee. +`IO.connectTCP owner endpoint` connects exactly that numeric address; zero peer +port is rejected. There is no hidden DNS, address fallback or reconnect policy. +`tcp-address-families` reports implemented encodings, not OS/network availability. + +`IO.acceptTCP listener owner` leases the listener and reserves acquisition in the +selected live ancestor/current scope before accepting. It publishes the connection +before acknowledging success; cancellation cannot leave an unregistered accepted +socket. The caller can use the existing atomic spawn-with-transfer operation to +move it to a worker, with fresh handles and stale old aliases. Listener acceptance +is exclusive; multiple pending accepts return `busy`. Acquiring into an explicitly +selected ancestor retains that ancestor's lifetime, just as file factories do. + +A connected stream permits **one read and one write concurrently**. Another read +or another write returns `busy`. Read EOF is sticky and does not close the write +direction. Write acknowledgements are positive accepted prefixes, not complete +messages, delivery, peer consumption or transaction boundaries. `IO.writeAll` +retains its ordinary partial/uncertain-progress rules. Source multi-call writes +are not whole-message atomic. TCP has no datagram/message framing here. + +`IO.shutdownWrite` leases the write direction, sends the local write half-close +and preserves reading; successful repeats are harmless. Writes afterwards fail +with `invalid-state` and no progress. Failed/cancelled reads or writes poison only +the affected direction; later use reports `invalid-state`/unknown progress rather +than replaying ambiguous work. Failed shutdown poisons writing too. Socket IO +errors include stable categories such as `connection-refused`, `connection-reset`, +`address-in-use`, `broken-pipe` and `timed-out` when the host supplies those errno +values; otherwise the ordinary generic categories remain. An OS timeout error is +not a new language deadline facility. + +Address queries currently lease the whole resource. Transfer and close require +**both** directions idle. Close prevents new leases and waits for pending ones; +it does not close/reuse the descriptor beneath a blocked read/accept to wake it. +Cancel the owning IO task to interrupt supported pending work, then settle/close. +Scope failure/cancellation does that through the structured-task machinery. +There are no hard deadlines; OS/kernel queued output can still reach a peer after +local acknowledgement/close. Cancellation does not undo network effects. +Files, standard streams and directories retain their existing single-lease rules. + +`demos/io-v2/tcp.tri` composes finite concurrent echo servers, lexical child handler +inheritance, and a file/socket client with simultaneous sending and receiving to +avoid bidirectional backpressure deadlock. The handled server's binary banner is +source policy, not a native operation. Default workers are fail-fast; an explicitly +supervised variant tests containment and discards outcomes intentionally, not as +a production logging recommendation. These are raw TCP demonstrations, not HTTP, +TLS or hardened public services. Registry deployments remain unchanged. + +The focused real-socket suite compares every echoed/output byte through 1 MiB, +fragmented sends, IPv4/IPv6, concurrent clients, repeated handoff, EOF/half-close, +refusal/malformed-address errors, and accepted-peer resets with both fail-fast and +contained workers. Native controls additionally cover directional lease exclusion, +close/transfer while only one direction remains busy, blocked receive/write +cancellation, listener reuse after cancelled accept, scope admission, structured +settlement, endpoint bounds and non-demand of excess components. RSS/live-heap +guards and ordinary RTS observations are regression controls, not throughput or +maximum-concurrency guarantees. + +## Explicit host name resolution + +`IO.resolveTCP host family port limit` uses the host TCP name resolver. Family is +`"ipv4"`, `"ipv6"` or `"any"`; port is 0–65535 and result limit is 1–64. Host input +is 1–253 ASCII letters/digits/dots/hyphens. This admits ordinary host names and +IPv4 literals accepted by the host; it is not strict DNS-label validation, IDNA +conversion, hostname authorization or a numeric-address parser. Use the numeric +endpoint constructors for IPv6 literals. Host search rules, hosts/NSS configuration, +ordering, duplicates and address availability remain host-defined. + +The result is `[endpoints truncated]`, with a canonical natural 0/1 truncation flag. +Only the bounded prefix is published; excess endpoint values are not demanded, +and the returned prefix does not retain the discarded host list. **The host +resolver may internally buffer more results and delay cancellation**. There is +no detached resolver worker, guaranteed prompt interruption, public DNS deadline, +exhaustive-answer claim, implicit connection, deduplication or address preference. +OS resolution failures return ordinary `resolution-failed` with bounded diagnostic +text; cancellation/unexpected faults are not converted to that Result. + +`IO.connectFirstTCP owner endpoints` is an explicit **source** policy that tries +candidates sequentially, stops at the first success and returns the last ordinary +failure unchanged if none succeeds. An empty list returns `not-found`. It does not +race candidates, add deadlines or elevate the selected endpoint into authorization. +The named client demo chooses the resolver's bounded prefix, then uses that policy. +Applications needing other retry, selection or authentication policies should +compose their own. TLS peer identity is not supplied by successful resolution. + +Native controls cover local/numeric host lookup, bounds/non-demand, host ordering, +truncation, ordinary errors and cancellation propagation without requiring public +DNS. The real socket source suite also resolves numeric/localhost names, checks +complete output, and exercises failed-first/successful-second source fallback, +including non-execution of a malformed later candidate and scope-bound lifetime. + +## Authenticated indexed-tree reads + +`IO.readIndexedTree reader directory limit size digest` consumes exactly `size` +bytes from the current reader position, under one read lease. `limit` is the +caller's maximum encoded-frame size; both are canonical integers in `[0,2^63-1]` +and size must not exceed limit. Digest is exactly 64 lowercase hexadecimal digits, +using the existing `arboricx.indexed-tree.v1` SHA-256 domain (UTF-8 domain, NUL, +encoded bytes). The reply is an ordinary Result containing the decoded tree. +No tree is evaluated, exposed, or decoded before authentication succeeds. +The caller supplies the expected digest: this checks content integrity, not +publisher identity or source provenance. + +The native transport creates a temporary file in the explicitly supplied directory +and unlinks its name before receiving input. It copies/hashes bounded chunks and +then decodes **that same private snapshot**, not a second pass over the public +source. Changing the original inode cannot change the snapshot. This is not a +point-in-time filesystem snapshot: authentication checks the bytes actually +received. A mutation during copying ordinarily makes the expected digest fail. +The directory must exist; empty directory strings are rejected (use `"."`). + +Temporary payload storage is at most `size` bytes, plus filesystem bookkeeping: +no expanded-tree serialization. Storage is reclaimed before ordinary completion; +on terminal fault/cancellation, its registered resource is released during lexical +settlement, with the existing primary-error/cleanup-diagnostic precedence. The +snapshot is private transport, not a source-visible handle, durable publication, +or recovery archive. Guessed registry keys cannot read, explicitly close, or +transfer private spool entries; active private leases still delay scope release. +This is lifecycle protection, not confinement against OS process authority. No fsync or publication is performed. A tmpfs directory uses +RAM-backed storage; ordinary filesystems may cache writes too. Choose the directory +and frame budget accordingly. There is **no aggregate disk quota across tasks**. + +Framing is explicit: no EOF probe consumes bytes of the following frame. The source +reader is not closed or rewound. Digest mismatch, truncation, or invalid format +may have consumed input; their `unknown` progress is not authorization to retry. +Validation of bounds and digest spelling precedes operation IO. An unauthenticated +malformed frame reports digest mismatch, never a parser error. Snapshot acquisition +errors and resource errors remain ordinary Results; unexpected faults/cancellation +remain terminal. Ordinary primary failures retain bounded cleanup detail if release +also fails. + +Decoded nodes preserve sharing and the existing reader's structural/manifest checks, +including backward references and duplicate-node rejection. Additional metadata +limits are 64 section entries and 65,536 manifest bytes. Noncritical opaque sections +remain supported and are authenticated even though not decoded. These are reader +semantics, not a claim that every accepted encoding equals the canonical writer's +bytes. Offset arithmetic is checked without integer wraparound. Transfer buffers +are bounded, but **the indexed DAG and verification tables are resident and grow +with node count**; the caller's frame budget must allow for that amplification. +There is no hard parser CPU deadline or universal safe maximum DAG size. + +The facility works on readable files, borrowed input and duplex streams. File and +borrowed-input leases stay exclusive; duplex connections permit concurrent writing +but not another reader. Other operations cannot interleave reads between chunks. +The reader alone implies no compiler caller migration or deployment/backend switch; +canonical writing and comparison are described below. + +```sh +python3 test/io_v2_indexed.py result/bin/tricu --directory /tmp/new-indexed-check +``` + +`demos/io-v2/indexed-read.tri` demonstrates source-selected framing and spool policy. +Native `SnapshotTests` cover authentication ordering, immutable-source isolation, +shared DAGs, malformed offsets/nodes, metadata bounds, cleanup faults, cancellation, +lease exclusion and wire non-demand. The independent source suite compares complete +results and framing through 64-MiB encoded inputs. Its large inputs grow an opaque +section while keeping the decoded DAG tiny: they test bounded transport, **not** +large-DAG capacity. RTS reports and sampled RSS are included; very short runs may +exit before a representative RSS sample. The 64-MiB transfer has 48-MiB sampled-RSS +and 16-MiB maximum-live regression guards, not service guarantees. + +## Canonical indexed writing and exact comparison + +`IO.hashIndexedTree limit tree` returns Result `[size digest]` for the canonical, +name-neutral indexed encoding. `IO.writeIndexedTree writer limit digest tree` +prepares the same encoding and checks its size and expected digest **before any +writer call**; success returns `[size digest]`. It holds one write lease for the +whole operation and handles positive partial acknowledgements without replaying +acknowledged bytes. Reads may proceed concurrently on duplex connections; files, +stages and borrowed outputs remain exclusive. Cancellation and host faults retain +the existing structured-settlement rules. + +Transport buffers are at most 4096 bytes. The native encoder retains the DAG and +exact interning tables, not an expanded tree or whole encoded ByteString. Hash and +write passes regenerate chunks rather than retaining a shared lazy chunk history. +Bytes and digest domains match the existing canonical indexed writer. The size +budget bounds **encoded output**, not tree evaluation, graph construction memory, +CPU time or an aggregate quota. Preparation can allocate before discovering that +output exceeds the limit; divergent source trees have no hard deadline. + +Preflight failures have `none` writer progress. Once sending starts, IO failures +have `unknown` aggregate progress: no atomicity, rollback, automatic retry, or +resumption promise. A caller opening a truncating writer has already truncated +its file before invoking this operation. Use a stage for publication safety. + +`IO.verifyIndexedTree reader limit tree` compares one canonical frame byte-for-byte +against a known tree and returns `[size digest]` on success. It does not merely +compare hashes, decode untrusted bytes, or probe EOF. Whole-file winner verification +must additionally check EOF; `demos/io-v2/indexed-write.tri` does so. Its publication +composition leaves a corrupt/excess existing winner untouched. An `exists` result +is exact verification at observation time, **not** existing-winner durability or +protection from subsequent external mutation. Compiler publication requires additional synchronization and ordering; the +source-owned object library below supplies that policy without changing this demo +into a durability claim. + +```sh +python3 test/io_v2_indexed_write.py result/bin/tricu --directory /tmp/new-indexed-write +``` + +Native tests cover canonical byte equivalence at varying chunk boundaries, physical +sharing, partial progress, preflight non-effects/non-demand, interrupted leases, +uncertain failures and exact comparison. Independent source checks cover hashes, +complete output bytes, staged publication and corrupt/excess winners, plus a DAG +with 200 shared fork levels and chains through 100,000 nodes. Those graphs remain +indexed during round-trip; their node-table memory is expected to grow. Reported +RSS is sampled and tiny runs may finish before representative samples. + +## Synchronization and source-owned indexed objects + +`IO.syncFile handle` flushes writable file buffering and requests filesystem +synchronization of the **opened inode**. It shares the file's exclusive cursor +lease, preserves its logical position, and supports read/update/write/append file +modes. It is not a standard-stream, stage, socket or directory-cursor operation. +`IO.syncDirectory path` opens and validates a directory, then requests synchronization +of that directory itself (not recursively). Paths follow the existing host/symlink +rules. Nonblocking acquisition avoids hanging on a FIFO substituted for a directory. + +Both operations return ordinary Results. Success means the host synchronization +request succeeded, not a universal crash/power-loss guarantee, peer consumption, +atomicity with another operation, or protection against subsequent mutation. +Filesystem/device behavior can be weaker; some filesystems reject synchronization. +Failures during sync have unknown durability/progress and do not imply rollback. +Transient descriptor resources are registered before use and have exclusive private +leases. Fault/cancellation cleanup follows lexical settlement; failed raw closes are +not retried against potentially reused descriptor numbers. Ordinary sync failures +retain their primary detail and bounded release diagnostics if both fail. + +`io_v2_objects` supplies source-owned `putTree root limit tree` and +`getTree root limit digest` computations. These are library policy, not native +object-store services. The store root must already exist; its creation and parent +namespace durability belong to the caller. Put creates/synchronizes `objects` and +three-character shard directories, uses a scoped stage and no-replace publication, +then verifies **every** winner byte-for-byte through EOF. It synchronizes the +verified open inode and shard directory before returning the digest. Callers must +publish manifests/locks/aliases only after that successful return. + +A mismatch or synchronization failure never deletes/overwrites a winning name and +can occur after a new object became visible. There is no crash pruning or rollback. +Root paths are trusted: this is not symlink confinement or a transaction against +external mutable writers. Get validates lowercase digest spelling before path IO, +gets the opened-file size, and uses the authenticated snapshot reader with the +caller's encoded-size budget. Object domains, paths, canonical bytes and sharding +match the existing compiler store. Returning an authenticated tree does not execute +it or establish publisher identity. + +```sh +python3 test/io_v2_objects.py result/bin/tricu --directory /tmp/new-object-check +``` + +`demos/io-v2/objects.tri` exercises these policies. Independent controls cover +complete object bytes/results, fresh/existing/corrupt/excess winners, malformed +digests, absent/blocked roots, limits, exponentially shared cross-store copying, +and actual concurrent publishers. Native synchronization tests cover opened-inode +identity/cursor behavior, modes, non-directories/FIFOs, leases, cancellation, +primary/cleanup faults and once-only descriptor release. Power-loss durability is +outside these tests. The packaged compiler uses this source-owned v2 storage +policy; full reproduction is covered by the compiler rebuilding guide. + +## Strict UTF-8 and source-text conveniences + +`IO.encodeUTF8 text` accepts at most 1024 canonical Unicode scalars and returns at +most 4096 octets. `IO.decodeUTF8 final bytes` accepts at most 4096 canonical octets +and returns `[text carry]`. Final is canonical 0/1. A nonfinal valid incomplete +suffix is returned as at most three octets; supply it before the next input chunk. +Final input rejects incomplete suffixes. Invalid prefixes fail immediately, even +when incomplete. Overlong encodings, surrogate code points, stray continuation +bytes and values above U+10FFFF are rejected; no replacement decoding occurs. +Empty chunks, NUL, BOM and Unicode noncharacters are valid codec data. No BOM +stripping, newline folding or Unicode normalization occurs. Errors report offsets +within this supplied chunk and return no partial decoded output. + +These bounded codecs are stateless. Carry is ordinary source data, not hidden +per-stream state; callers own sequencing and can use any byte transport. At most +4093 fresh bytes plus a three-byte carry fit a decode request. Bounds and canonical +scalar/octet validation precede conversion; excess elements are not demanded. + +`io_v2_text` composes them into: + +- `readSource reader limit`: whole-source Result `(rawOctets, scalars)`, preserving + exactly the bytes used for decoding and rejecting an initial BOM. The reader is + borrowed, not closed. The byte limit is canonical `[0,2^63-1]`; a one-byte EOF + probe at the boundary may consume one excess byte before rejecting oversize input. + Failure is `["read-source" confirmedInput cause]`; a failing read's own uncertain + progress remains in the unchanged cause. No automatic retry or cursor rewind. +- `encodeText limit scalars`: whole-value encoded octets under a byte budget, without + source-specific BOM rejection. It does not write a resource. +- `writeText writer scalars`: bounded sequential encoding/writing with no aggregate + output quota or ownership transfer. Failure is `["write-text" confirmedBefore + cause]`; writeAll's partial-progress diagnostic is preserved when applicable. + +Read/encode conveniences retain whole values and are **not** constant-memory source +compilation. Budgets bound bytes, not all graph/interpreter memory or CPU. Individual +read/write operations are leased; these source conveniences do not acquire one +whole-text lease, guarantee whole-record atomicity, or make concurrent cursor use +safe. The low-level codecs remain usable in bounded streaming applications instead. + +```sh +python3 test/io_v2_text.py result/bin/tricu --directory /tmp/new-text-check +``` + +Native tests independently exercise all 1,112,064 Unicode scalars against standard +UTF-8 encoding, boundary/invalid vectors, every split of mixed text, canonical +wire bounds and non-demand. Source checks compare complete encoded bytes, raw source +provenance and scalar values across chunk boundaries; retain CR/LF, NUL, combining +characters and interior BOM; reject initial BOM, malformed/incomplete input, excess +budgets and surrogates; preserve an acknowledged prefix on later encoding failure. +Whole-source pipelines through 64 KiB record RTS and sampled RSS observations +for their retained source/decoded values. + +## Internal protocol + +Envelope: `("tricuIO", (2, action))`. Action is a pair of canonical numeric tag and +payload. Comma-separated payload fields below are right-nested pairs, except +where a list is explicitly shown. Pure/bind do not add Result envelopes. + +| Tag | Payload | Reply | +| --- | --- | --- | +| 0 | value | raw value | +| 1 | action, continuation | sequencing | +| 2 | diagnostic | terminal failure | +| 3 | callback | new scope supplied to callback; returns body value after children and release | +| 4 | ignored | current scope reference | +| 5 | supervised, keepResult, handles, callback | Result `[task joinTicket]`; callback receives fresh handle list | +| 6 | join ticket | Result terminal outcome; consumes ticket at claim | +| 7 | join ticket | Result unit; discards the result, not the child | +| 8 | task, reason | Result unit acknowledging cancellation request | +| 9 | reason | cancel this computation, without resuming its continuation | +| 10 | facility name, group revision, operation name, arguments | Result (named external operation) | + +Current core tags are listed above. Facility calls use named requests. Earlier +v2 tags 20–42 reject; historical v1 actions require a retained runtime: + +``` +IO.request group revision operation arguments +-- (10, (group, (revision, (operation, arguments)))) +``` + +Group/operation names are nonempty ASCII identifiers of at most 64 characters: +an initial lowercase letter followed by lowercase letters, digits or hyphens. +Revisions are positive canonical 16-bit naturals. The outer envelope version is +also a canonical 16-bit natural, and its sentinel is exactly canonical `tricuIO`. +Native execution and indexed inspection enforce these envelope checks for both +versions; noncanonical envelope aliases are deliberately rejected. + +Implemented group revisions are all **1**: + +| Group | Operation | Arguments | Reply inside Result | +| --- | --- | --- | --- | +| runtime | capabilities | unit `t` | capability association rows | +| runtime | inspect | action | shallow header description | +| standard | input / output / error | unit `t` | borrowed stdin reader / stdout writer / stderr writer | +| file | open-reader | scope, path | reader | +| file | open-writer | scope, path | writer (create/truncate) | +| file | open-appender | scope, path | append-only writer (create if absent) | +| file | open-update | scope, path | readable/writable file (create if absent, no truncation) | +| file | seek | file, absolute byte offset | unit | +| file | position | file | current logical byte offset | +| file | size | file | observed byte length of the opened file | +| file | resize | file, byte length | unit | +| path | stat | path | `["file" bytes]`, `["directory"]`, `["symlink"]`, or `["other"]` | +| path | lstat | path | same tags, without following final symlink (host path resolution rules apply) | +| path | remove-file | path | unit; unlink, not recursive deletion | +| path | rename | source, destination | unit; host replacement semantics | +| directory | create | path | unit; strict nonrecursive creation | +| directory | remove | path | unit; empty-directory removal | +| directory | open | scope, path | directory cursor | +| directory | next | cursor | pair `("entry", name)` or `("eof", t)` | +| tcp | listen | scope, (endpoint, backlog) | listener | +| tcp | connect | scope, endpoint | duplex stream | +| tcp | accept | listener, scope | duplex stream owned by the selected scope | +| tcp | local-address | listener or duplex stream | endpoint | +| tcp | peer-address | duplex stream | endpoint | +| tcp | shutdown-write | duplex stream | unit | +| resolver | resolve | `[host family port limit]` | `[endpoints truncated]` | +| indexed-tree | read | `[reader directory limit size digest]` | decoded tree | +| file | sync | handle | unit | +| directory | sync | path | unit | +| utf8 | encode | scalar list | octet list | +| utf8 | decode | `[final octets]` | `[scalars carryOctets]` | +| indexed-tree | hash | `limit, tree` | `[size digest]` | +| indexed-tree | write | `[writer limit digest tree]` | `[size digest]` | +| indexed-tree | verify | `[reader limit tree]` | `[size digest]` | +| stream | read | reader, positive limit | `(data, nonempty bytes)` or `(eof, t)` | +| stream | write | writer, nonempty bytes | positive accepted count | +| resource | close | resource | unit | +| stage | open | scope, directory | staged writer | +| stage | seal | stage | unit after flush, sync and close | +| stage | publish | stage, replacement mode, destination | `"published"` or `"exists"` | +| digest | open | scope, algorithm | digest context; currently `"sha256"` | +| digest | update | digest, nonempty bytes | unit | +| digest | finish | digest | lowercase hexadecimal digest; consumes state | + +`IO.capabilities` reports association rows `[key value]`: `schema` (currently 1), +`io-version`, `backend`, `authority`, `core` constructor names, `groups` as +`[name revision operationNames]`, `limits` (chunk bytes, transfer handles and maximum file offset and directory-name scalar limit TCP backlog, resolver input/output bounds and indexed metadata bounds), +`digest-algorithms`, and `restrictions`. It describes this backend's implementation, +not OS permissions, resource availability or a guarantee that a future call works. +For example, no-replace publication still needs filesystem hard-link support. +No browser/Zig v2 capabilities are claimed by the native report. + +A well-formed named request with an unavailable group, revision or operation +returns `unsupported` **without demanding its arguments**. Unknown names do not +reserve or specify future argument schemas. Malformed headers and unknown core +constructors are terminal protocol failures; bad arguments to implemented requests +are ordinary operation errors. Protocol failures carry +`["protocol-error" "core/decode" boundedDetail "none"]` as their diagnostic. + +`IO.inspect action` returns `["core" constructorName]` or +`["facility" group revision operation "available"/"unsupported"]` inside Result. +This inspects only the header: it does not validate arguments, inspect payloads, +follow bind actions, apply callbacks, resolve handles or predict success. Even a +malformed bind payload can have a recognizable bind header. An invalid queried +header returns an ordinary `invalid-argument` error from `runtime/inspect`. +This is distinct from the richer read-only **indexed bundle inspector**, which +follows explicit bind action structure under depth/occurrence limits while keeping +v2 request arguments and callbacks opaque. Its JSON schema is +`tricu.interaction-inspection.v2` with `supportedVersions: [1,2]`; offline inspection +never infers a deployment backend's support from an artifact's named header. + +Spawn flags are canonical 0/1. A non-result child has `t` in the ticket position; +its task control handle is still available while active. Transfer lists contain +at most 256 handles. Task and join references use distinct markers, run identity +and monotonic keys; they are not portable resources or secret capabilities. + +Joined outcomes are lists: `["completed" value]`, `["failed" diagnostic cleanup]`, +or `["cancelled" reason cleanup]`. Cancellation is not an ordinary operation error +inside the cancelled child. A supervisor can observe its final outcome as data. +The CLI reports terminal failure/cancellation without forcing arbitrary diagnostic +payloads. `runDetailed`/`runInRuntime` retain structured outcomes for embedding; +a supplied runtime belongs to that execution and closes on exit. + +Ordinary errors are four-element lists: category, operation, bounded detail and +progress. Wire operation errors now use the requested `group/operation`, e.g. +`file/open-reader`, `stage/publish`, or `core/join`, rather than leaking incidental +backend labels such as `open` or `task`. Category/detail/progress are preserved. +Terminal user diagnostics remain arbitrary trees; lifecycle cleanup diagnostics +retain their resource-release context and are not fabricated operation replies. Canonical byte chunks/read bounds are 4096 bytes; paths are canonical +Unicode scalar lists without NUL. Empty read means EOF, never transient progress. +The file backend accepts the whole chunk on successful writes; source `writeAll` +also handles partial positive acknowledgements. Its failures are +`["write-all" confirmedBefore originalProblem]`: confirmed bytes from earlier +successful calls, plus the failing operation's unchanged diagnostic. They do not +claim the failing operation accepted zero bytes. This deliberately replaces raw +error forwarding, which lost earlier progress. Native write errors can have unknown +progress, so blindly retrying an entire chunk is unsafe. Acknowledgement is not +durability. Unknown/malformed computation constructors fail the task; invalid +arguments to recognized operations return ordinary errors. + +## Lifetimes, admission and handoff + +Scopes own resources and child tasks. Descendants borrow ancestor resources; +returning a handle does not extend its lifetime. Normal exit closes admission, +joins attached children, then releases resources. Existing descendants keep their +borrowing rights and may acquire into their own still-open scopes. Failure or +cancellation requests child cancellation before waiting. There are no detached +tasks. An endless child makes normal scope exit wait until explicitly stopped. + +Operations hold leases. Closing rejects new operations and waits for an existing +lease before releasing; file cursor overlap returns `busy`. Repeated explicit +close reports stale/closed, without retained tombstones. Scope cleanup finishes +inner scopes first and releases resources in reverse acquisition order. + +Spawn creates a gated worker before commitment. Resource reservation, transfer, +task registration and opening its gate occur in one STM transaction. Invalid +handle sets start no child and move nothing. Only idle resources owned by the +attaching scope move; borrowed ancestor resources remain borrowed. Old aliases +become stale; fresh handles preserve input order, while release follows original +acquisition order. Explicit close claims closing atomically with token validation, +so an old alias cannot close a newly transferred resource. Once published, a +worker startup/execution fault is a task failure, not a rollback of successful +spawn. OS/thread allocation before publication must succeed before any transfer. + +## Outcomes, cancellation and cleanup + +Ordinary Results remain values. `IO.require` explicitly promotes a selected +operation error to terminal failure. Uncontained child failure records the group's +failure and cancellation of siblings transactionally, then wakes the body. +Supervision must be selected at spawn, before the child runs. Joining later does +not retroactively contain a fault; discarding a ticket does not disable supervision. + +The first recorded stop request wins. Completion linearizes only after child and +resource settlement, so a cancellation during normal joining/release can still +win over a successful body. Cancellation after final completion reports stale or +completed-task. A task has one consumable ticket: claiming join removes it before +waiting; concurrent/duplicate claims fail. An interrupted join does not restore +the ticket. Explicit and implicit joins participate in wait-cycle detection; +an explicit cycle returns an ordinary error, while an unavoidable implicit scope +cycle fails the group. Non-result and discarded completions leave no result +archive. Unconsumed tickets deliberately retain their results until scope exit. + +Controllers do not run user computation. A body worker executes the interaction +tree; sticky cancellation is checked before interpreting each action. Waking a +worker cannot replace its recorded reason or turn cancellation into a recoverable +Result. Even a backend that catches the wakeup cannot resume the next source +continuation. Nested lexical controllers receive cancellation through group state, +not an exception injected into their resource-release protocol. + +Cancellation waits for pending work to settle; it cannot undo bytes written or +other external effects. Cleanup attempts remaining releases after synchronous +failures, preserves an existing failure/cancellation, and bounds diagnostics to +sixteen plus an omitted-count marker. Successful execution with failed required +cleanup becomes failure. A reported failed release is not proof the OS reclaimed +a broken backend resource. Host interruption of the outer runner is rethrown only +after subtree settlement; repeated external interruption/process death remains +outside the cleanup guarantee. + +`Runtime.Tree.apply` is a lazy recursive evaluator, not an explicit budgeted +reduction machine. The driver supplies action-boundary safe points; native pure +reduction relies on GHC scheduling/allocation safe points. No prompt cancellation +or hard deadline is promised for arbitrary divergent pure calculations, foreign +calls or OS cleanup. Pending-IO cancellation tests do not establish those claims. + +## Handler bridge + +`H.scopeV2`, `H.spawnV2`, and `H.runIOV2` are concurrency-aware source boundaries. +Normalization snapshots the actual lexical service environment and labels for the +child, with an empty child continuation stack. A handler callback's child inherits +the callback's outer environment, not the original requester's inner overrides. +Child-local handler changes do not mutate parent routing. Label list spines are +settled at boundaries without forcing payloads or unselected continuations. + +`H.runIO` aliases `H.runIOV2`, which returns successful values directly and maps +handler failures/unhandled requests to terminal failures. +Service-level Results remain ordinary values. `H.liftIO (IO.spawn ...)` alone does +not capture handler routing: use `H.spawnV2` for handler computations. + +`H.collect` is a state-threading transformer, not shared mutable state. It rejects +v2 scope/spawn boundaries rather than silently sharing or dropping its state; +install child-local collection inside the child body explicitly. General shared +cells/channels and implicit child-state snapshots are not implemented. Captured +services that refer to resources still obey the real resource scopes. + +The handled copier exercises the current host bridge and lexical service routing. + +## Staged publication and independent digests + +`IO.openStage` creates a mode-private temporary file in an existing directory +(nonempty path; use `.` explicitly for the working directory). +It is not visible at the destination name, but its temporary name is discoverable +by the OS owner; this is not a sandbox. `IO.writeSome`/`writeAll` work on stages. +`IO.sealStage` flushes, synchronizes the actual file descriptor and closes it; +subsequent writes fail. On sync failure/interruption, the descriptor remains +registered for scope cleanup, so release failure cannot replace the original +sync failure. Directory synchronization uses the same ownership discipline. +Failed descriptor closes are not blindly retried against a possibly +reused OS descriptor; other cleanup is still attempted. No length, hash, artifact name or upload-session policy is +hidden in the native staged writer. + +Publication may cross directories on the same filesystem. Destination directories +must already exist. Atomic rename/hard-link syscalls enforce the filesystem boundary; +there is no copy fallback or automatic retry on cross-filesystem failure. This +replaces the earlier same-directory-only restriction. Basenames containing +`.tricu-stage-` are reserved for temporary files, preventing publication over +this or another stage's private name. `publishReplace` atomically renames over a destination; +`publishNew` atomically creates a hard link without replacement, then removes the +private name. The latter requires filesystem hard-link support. Both synchronize +the destination directory after successful publication, then the staging directory +when it is distinct. Same-directory publication synchronizes the directory once. +A failure during either synchronization or descriptor release can follow visible +publication; it is not rollback permission. Synchronization stops at failure, and +owned descriptor/private-name cleanup still runs. These are POSIX filesystem mechanics, +not race-resistant confinement against other actors changing managed directories. +Already-open readers of a replaced destination can still see its old inode. + +Mode 0 means no-replace; mode 1 means replacement. `"exists"` leaves the stage +sealed and makes no claim about the winner's contents or durability. `"published"` +means this backend's file/directory synchronization requests completed, not a +universal crash-consistency guarantee across every OS/filesystem/storage device. + +Write, seal and publication execution failures freeze the stage against further +writes/publication. Publication errors classify visibility in the progress field: +`not-published` before the namespace operation, `unknown` on an ambiguous namespace +failure, or `published` after successful link/rename but failed removal/directory +sync/descriptor release. A cancellation after namespace change may also leave output visible. +**Neither failure nor cancellation authorizes retrying publication or deleting +the destination.** Explicit close/scope cleanup only closes the owned handle and +removes the private name. A failed release can leave private staging files and is +reported; automatic crash recovery/pruning is not implemented. Invalid decoded +arguments never reach the backend; phase errors and publication execution errors +are distinct from those argument checks. + +Digest contexts have the same ownership, borrowing, transfer, lease and close +rules, independently of files. Updates accept bounded nonempty canonical chunks; +finish consumes the context, including the valid empty-input digest. Updates or +another finish after finish report `invalid-state`; close releases the registry +entry. Unsupported algorithm names return `unsupported`, not protocol failure. +The native implementation forces each updated context so it does not retain a +chain of old chunks. + +### Source-owned artifact policy + +`io_v2_artifacts` (`lib/io-v2/artifacts.tri`) composes stages and digests through +handlers. Its producer submits `write` requests and finally returns Result unit. +The boundary owns the remaining count: early success, excess bytes, a producer +ignoring failed writes, or an inner override forging acknowledgements cannot +advance that count or authorize publication. A failed step ends the boundary +without resuming the producer. The adapter's declared-length limit is 64 MiB; +it is application policy, not a native staging quota. An excess chunk may have +been written privately before its excess is detected (at most one bounded chunk); +it is never published and is removed on scope exit. + +The adapter validates the finished digest before deriving a flat destination +name. An existing winner is independently read and hashed, with exact-length +checking, before returning success. This is point-in-time content verification, +not certification of another writer's crash durability or protection against +subsequent external modification. It returns an ordinary Result digest; handler +protocol failures remain terminal failures. The directory must already exist. +Producer code remains trusted process-authorized code: this boundary does not +constrain direct native IO or external filesystem mutation. It checks submitted +chunks and acknowledgements, not arbitrary malicious programs. +The producer is sequential: other requests are forwarded with lexical labels, +but nested producer scope/spawn views are rejected instead of duplicating count +state. The whole publication computation can itself run in a child task. + +`demos/io-v2/publish.tri` is a bounded file producer. The default source registry +uses the streaming composition described below for socket uploads and publication. + +`IOV2StorageTests.hs` independently covers SHA-256 vectors, phase/kind/bound checks, +atomic no-replace races, transfer, pre/post-publication faults, cancellation during +write/seal/publication, descriptor settlement, release failures and repeated +scope reclamation. `io_v2_publication.py` checks complete files and independent +Python SHA-256 expectations through 1 MiB, Unicode paths, private file modes, +existing-winner verification, corrupt +winners, counter/acknowledgement controls, cancellation, and digest/path validation. +It records allocation separately from the same workload-specific RSS/live-heap +guards used by the first slice. Separate write/digest calls deliberately decode +each chunk twice; no fusion or routing-only performance improvement is claimed. + +## Explicit compiler invocation boundary + +The native supervisor defaults to IO v2 and accepts an explicit +`--toolchain-io-version 2`. Selected compilers execute with OS process authority. +Canonical envelope validation precedes action demand; v1 selection requires a +retained host. Returned manifests and receipts are authenticated. Startup loads +and verifies the worker inertly. + +```sh +python3 test/compiler_io_version.py result/bin/tricu --directory /tmp/new-compiler-version-check +``` + +Independent native workers check framing, inert startup, retired-grant rejection, +invalid selections and authenticated outputs. The eleven CLI controls cover +packaged and explicit selection. Full generation acceptance uses the separate +[reproduction workflow](../../docs/compiler-rebuilding.md.html). + +## Source storage and isolated compiler projection + +`io_v2_storage` composes native mechanisms without adding a native store service: + +- `readBytes path limit` returns a whole byte list, with bounded reads and a one-byte + EOF probe at the budget boundary. Excess input can consume that one byte before + failure. It is not framed input or a constant-memory whole-file representation. +- `hashBytes limit bytes` validates canonical octets and the byte budget while + feeding bounded chunks to a scoped SHA-256 context. Empty bytes are valid. +- `publishBytes directory destination replace limit bytes` preflights before stage + creation. Canonical flag 0 publishes without replacement and verifies **every** + winner byte-for-byte through EOF, then synchronizes its open inode and directory. + Flag 1 atomically replaces a mutable name after sealing; it does not race-read a + subsequent legitimate alias replacement. Both return Result Boolean: false only + for a verified existing immutable winner. Publication errors do not authorize + rollback or retry; a destination can already be visible when synchronization fails. +- `publishTree directory destination limit digest tree` preflights canonical identity, + uses native indexed transport, and verifies/synchronizes every no-replace winner. + `readTree directory path limit digest` authenticates a snapshot before decoding; + the explicit snapshot directory must be writable. +- `parentPath` is a POSIX lexical parent helper, including trailing separators and + leading root separators. It does not resolve symlinks or `..`, validate a path, + or establish confinement. Directory creation/durability remains caller policy. + +Limits are canonical `[0,2^63-1]` encoded-byte budgets, not graph/heap or CPU quotas. +Resident caller byte lists remain resident values; these helpers bound transport, +not all application memory. Failures preserve the underlying operation problem or +writeAll diagnostic. They do not provide a transactional whole-operation progress +count or safe implicit retry. Immutable-store policy assumes cooperating writers; +there is no transaction against external mutation or universal crash guarantee. + +`lib/source/io-v2.tri` is the **compiler storage facade**, imported as +`compiler_io`. It constructs v2 actions for the operations used by the source compiler. +It preserves Result continuation/remainder conventions, uses a declared 64-MiB +source/metadata/encoded-tree budget, and requires parent-first directory creation. +Missing ancestors fail. Directory +entries are synchronized before dependent publication. File-existence queries +return false for missing paths and non-files, but propagate other operation errors. +Indexed reads spool in the object's parent directory, suitable for the writable +compiler application store. Callers must supply a writable parent directory. + +Both compiler source maps select `compiler_io` and its v2 dependencies. The +general application module `io` reexports the complete `io_v2` facade. + +```sh +python3 test/io_v2_compiler_store.py result/bin/tricu \ + --directory /tmp/new-compiler-store-check --compiler-smoke +``` + +Without `--compiler-smoke`, the test exercises actual `lib/source/object.tri` +callers and storage policy. With it, the retained compiler builds a candidate +stable `sourceBuildGraph`; explicit IO-version 2 selection then compiles an actual +application dependency graph with cold and warm caches. Controls compare complete +bundle bytes with the retained compiler, evaluate independent numeric and combining- +character expectations, inspect exact raw UTF-8 provenance digests, and preserve +prior output on BOM/malformed-UTF-8 failures. CRLF source rejection is compared +with the retained compiler: preserving bytes is not a promise that the parser +accepts every Unicode scalar as source whitespace. + +Reports record projected input hashes, candidate bundle identity and IO version, +commands and routine RTS observations. This procedure does **not** produce or accept +an entire self-rebuilt compiler generation pair. Generation/reproduction metadata, +backend support, deployment/default selection and rolling retention acceptance +remain separate gates. Source bundle encoding and resident source/metadata values +are not claimed to have become bounded-memory operations. + +## Artifact handler filesystem adapter + +`handler_artifacts.withFilesystemV2 root byteLimit action` and +`withFilesystemForV2 service root byteLimit action` interpret the existing artifact +request interface using native v2. Run the computation through `handlers.runIOV2`. +`prepareFilesystemV2 root` explicitly initializes the registry layout; the root's +parent must already exist. Lexical overrides, named captured services and unknown-operation +forwarding retain the interface's ordinary handler behavior. + +The caller selects a byte limit. Heap and execution-time limits require separate controls. These operations +retain whole byte lists. Loads authenticate the returned bytes against the requested +raw SHA-256 name. Publication verifies and synchronizes every immutable winner +through EOF; corrupt existing objects remain intact and produce a failure. +Synchronization follows host-filesystem guarantees; objects can remain visible +after a post-publication failure. Callers provide bundle validation and publisher +authentication when required. Paths follow OS process authority. Invalid digest paths and malformed raw +publication requests are rejected at the adapter boundary too. + +```sh +python3 test/io_v2_artifact_service.py "$TRICU" --directory /tmp/new-artifact-service +python3 test/handler_library.py "$TRICU" --directory /tmp/new-handler-controls +``` + +Controls include exact binary data across chunk boundaries, corrupt/trailing +winners, byte budgets, startup failures, raw request validation, lexical and captured +routing, forwarding, concurrent publication, stage cleanup and rejection of v1 +grants before effects. These controls exercise the storage service; the HTTP +listener has separate socket-level checks below. + +`arboricx_handlers.routerV2 root byteLimit sink method target headers body` reuses +registry routing/response policy with those services. `packageLookupV2` captures its +root independently of artifact overrides; `stdoutLogV2` writes status-only access +logs to the borrowed standard output. Each request interpretation runs in a +supervised child whose join ticket is consumed. Interpretation/logger failures yield +a redacted 500 after child cleanup; cancellation remains cancellation. Publication +can precede either outcome and is not undone. Byte budgets also apply to captured +package reads. This router expects the HTTP transport to validate framing and own +the connection; it neither accepts sockets nor makes request deadlines available. +The source probe checks complete HTTP bytes, captured package roots, authentication +failures, logging failure after publication, ordinary status logging and cancellation. + +### Digest-selected streaming destinations + +`io_v2_artifacts.publishWith stagingDirectory expected destination producer` uses +the existing sequential producer interface. After exact-length completion, digest +validation and sealing, `destination digest` returns a handler Result containing +the final path. Destination policy may initialize a digest-derived directory; +it remains source code, not native upload/store policy. Callback failure abandons +the stage. The existing `publish` helper retains its flat-directory behavior. +Existing winners are length/digest verified through EOF and their opened inode and +parent directory are synchronized before success. + +`handler_artifacts.publishStreamV2 root expected producer` uses this mechanism for +the registry layout: stage under `root/tmp`, then publish without replacement under +`root/objects/<first-three-digest-characters>/<digest>.arboricx`. Initialization is +explicit via `prepareFilesystemV2`; the temporary and object directories must be on +the same filesystem. There is no whole-body buffer or second file copy. The source +probe exercises new/existing winners through 1 MiB, short/excess streams, corrupted +winners and private-stage cleanup. The 64-KiB/1-MiB checks retain generous 128-MiB RSS +and 16-MiB live-heap regression guards for these workloads. + +### Native v2 HTTP registry + +`io_v2_http` reuses the existing pure HTTP grammar, validation precedence, +Content-Length decoding, 65,536-byte accumulated header-buffer limit (including +prefetch), and 1,048,576-byte body limit. Transfer coding and duplicate lengths +remain rejected. HEAD suppresses bodies only after the historical trusted parsing +boundary. Each connection serves one request; surplus/pipelined bytes are ignored +on close. Response writes are explicitly split into chunks of at most 4096 bytes: +Each `IO.writeAll` call completes one bounded chunk. + +`arboricx_streaming_v2` connects this transport to `routerV2` and +`publishStreamV2`. POST bundle input is sequential and acknowledged before the next +read. Other request bodies, artifact downloads and package downloads remain +whole-value operations whose memory cost grows with the retained values. +Upload interpretation/logging runs in a supervised child and is joined before the +response. Cancellation remains cancellation, and post-publication logging failure +cannot undo publication. Connection ownership transfers atomically into its worker +scope, which closes the socket after success, failure, reset or cancellation. + +`serve` and `serveCount` take a canonical artifact byte limit (also enforced on +uploads, in addition to the HTTP body limit) and an explicit connection window in +[1,256]. Admission +consumes the oldest join ticket before exceeding that window. This bounds active +connections and retained outcomes but deliberately permits head-of-line blocking: +a slow oldest connection delays new admission even if later workers finish. No +request deadline, idle timeout, completion-order scheduling or slow-client +availability guarantee is implemented. Excess connections wait in the OS backlog, +whose capacity and behavior follow host configuration. `serveCount` drains its finite selection before returning; explicit +scope cancellation cancels/joins active workers and cleans incomplete stages. + +Build/run the native-OS-authority streaming runner: + +```sh +nix build .#arboricxStreamingV2Runner +./result/bin/arboricx-streaming-v2 +nix build .#arboricxStreamingV2Test +python3 test/io_v2_http.py /path/to/nix-built/tricu --directory /new/output +``` + +The runner binds port 8080 and uses `./store` with a 16-connection FIFO window. +Its packaged runtime store needs no compiler cache in the working directory. +The default `arboricxServerRunner` and image use the same v2 composition. Symlinks +follow OS authority; wrong-digest bytes fail authentication. Deployment tests use +v2 and retain retired-grant rejection controls. Public deployment is a separate +operator action. + +Socket controls include complete HTTP-byte expectations, binary upload boundaries +through 1 MiB, authenticated downloads, corrupt winners, header/body rejection, +prefetch/surplus, half-close/reset, logger failure after publication, bounded +concurrent admission, repeated join-ticket consumption, startup failure, v1 grant +rejection before effects, and explicit cancellation of active uploads. Streaming, +admission and lifecycle jobs enforce 128-MiB RSS/16-MiB live-heap regression guards; +these are test guards, not process-wide service capacity promises. The separate +packaged deployment test also exercises whole-value 1-MiB download/persistence. + +### Monotonic observations, relative waits and scheduler yield + +Native `clock` revision 1 provides `monotonic` (unit argument) and `sleep` +(canonical milliseconds in [0,9223372036854775807]); `scheduler` revision 1 provides +`yield` (unit argument). Source helpers are `IO.monotonicNanoseconds`, +`IO.sleepMilliseconds` and `IO.yield`. All return ordinary Results. Clock samples +are the host's unsigned 64-bit monotonic nanoseconds, with an unspecified local +origin. Use differences within that clock domain to measure elapsed time. +The host counter's representation/wrap limitations remain applicable. + +Relative waits split host delays into at most one second, avoiding conversion of +an enormous millisecond value to a bounded host microsecond integer. They are +interruptible, may finish late, and make no hard deadline promise. Zero wait is a +no-op, not an implicit scheduling barrier. Yield offers a scheduling opportunity; +it does not guarantee alternation, fairness, ordering or message atomicity. No +wall-clock reads, timeout/race combinator or deadline API is implied by this group. + +`demos/interactionTrees.tri` now uses v2 scoped children and consumed join tickets; +the parent alone writes stdout. `demos/interactionTrees/yield.tri` installs child- +local log collection and prints the collected results from the parent. This +explicitly retires the old demo's incorrect claim that yield guarantees alternating +writes and avoids concurrent stdout lease conflicts. Published historical fixture +bundles retain their original provenance alongside current v2 publications. + +```sh +python3 test/io_v2_clock.py result/bin/tricu --directory /new/clock-controls +python3 test/io_v2_protocol.py result/bin/tricu --directory /new/protocol-controls +``` + +Native controls independently check capability data, canonical arguments, inert +inspection, monotonic ordering, minimum elapsed sleep, exact waiter chunking and +maximum-duration interruption. Actual source probes check zero/short waits, +normal scope joining, explicit cancellation of a maximum-duration child wait, +invalid inputs, and both migrated demos' complete output. Test timeouts are harness +guards, not runtime deadline guarantees. + +### Direct-child subprocesses and bounded pipes (native POSIX) + +`IO.spawnProcess executable arguments directory environment graceMilliseconds` +uses the `process` revision 1 `spawn` operation. Its payload is the five-element +list of those arguments; its success value is +`[task joinTicket stdinWriter stdoutReader stderrReader]`. Pipes belong to the +launching scope and support ordinary stream operations, borrowing, handoff and +close. A failed/cancelled pipe operation poisons that endpoint against replay. +Other runtime descriptors are not deliberately inherited (`close_fds` is enabled). + +Directory selection is `["inherit"]` or `["at" path]`; environment selection is +`["inherit"]` or `["replace" [[key value] ...]]`. Empty replacement means an empty +environment. Duplicate/empty keys and keys containing `=` are rejected. Executable +must be nonempty. Arguments are passed as a vector, not parsed as a shell command; +executable lookup, script loading, path resolution and encoding follow the host +process library/OS. An explicitly named shell can of course interpret its arguments. +Execution uses OS process authority. + +Preflight limits are 256 arguments, 256 environment entries, 4096 Unicode scalars +per text field and 65536 scalars across executable, arguments, explicit cwd, keys +and values. Text must be canonical, NUL-free Unicode scalars. These are not encoded +OS argument-size guarantees: host limits/errors still apply. Grace is canonical +milliseconds in [0,9223372036854775807], explicitly selected by the caller. Individual +field/list bounds are checked before constructing host values; the aggregate bound +is then checked before spawning. Excess elements beyond individual bounds are inert. + +The process is monitored as an unsupervised structured child task. Normal scope +exit joins it, even without consuming its ticket. Explicit `IO.join` returns the +usual Result outcome; completion contains `["exit" code]` or `["signal" number]`. +**Exit status is data, including nonzero exits.** Callers must consume and check +the join outcome when command success matters. +Launch errors are ordinary Results, acknowledged after internal task settlement. +Unexpected monitor/cleanup failures propagate through the existing task machinery. + +`IO.cancel task reason`, owning-scope cancellation or failure terminates the direct +child with TERM, allows the selected grace, escalates to KILL if necessary, and +reaps. Normal waiting stays in cancellable task work, including after the parent's +body has completed. It does not block protected resource release. Status observation +uses nonblocking host polling with nominal 10-ms pauses, not a detached waiter. +Termination/reaping also attempts subsequent cleanup after an earlier release +error; errors are retained with bounded detail and an explicit additional-error +count when needed. Very long selected grace periods can delay cancellation settlement. +Scheduling, OS calls and cleanup provide **no hard deadline guarantee**. Host process +death, repeated external interruption and power loss are not cleanup guarantees. + +Only the direct child is owned: no process-group signalling, descendant confinement, +job control or automatic grandchild reaping is claimed. A descendant can retain a +pipe writer after the direct child exits. This can prevent EOF indefinitely. + +**Pipe policy is explicit:** close stdin to deliver EOF; drain stdout and stderr +concurrently when either may fill. Waiting before doing that can deadlock. No +implicit whole-output buffering, automatic input close on normal scope joining, +rollback of external effects, or automatic command-success checking is supplied. +The source demo `demos/io-v2/process.tri` pumps a file into stdin while two scoped +children drain stdout/stderr into files, then checks task completion and returns +command status for its caller to judge. Files can contain partial output on failure. + +```sh +nix flake check +nix build .# +python3 test/io_v2_process.py result/bin/tricu --directory /new/process-controls +python3 test/io_v2_protocol.py result/bin/tricu --directory /new/process-protocol +python3 test/io_v2_tasks.py result/bin/tricu --directory /new/task-controls +``` + +Native controls cover real exit/argv/env/cwd behavior, binary backpressure, missing +executables, implicit joins, failure, cancellation after observing closed scope +admission with a live child, TERM-ignoring escalation/reaping, repeated lifecycle +registry counts, failed-pipe poisoning, bounded/non-demanding protocol validation, +and injected termination/cleanup failures and clock wrap. Source controls compare +complete binary outputs through 1 MiB, including simultaneously full stdout/stderr, +normal joining without an explicit ticket wait, literal arguments and nonzero +status, and malformed bounds. Runtime RSS/maximum-live guards exclude child-process +memory and are regression guards, not capacity or throughput promises. This slice +is validated on native Linux; it does not add Zig/browser process support or change +compiler fixtures, deployment defaults, or accepted compiler generations. + +### Bounded, incremental line reading + +`io_v2_text.lineCursor reader` starts a single-consumer source cursor. +`Text.readLine cursor byteLimit` returns an ordinary Result containing +`[event nextCursor]`. Events are `["line" text "lf"]`, `["line" text "eof"]` +(for a nonempty final unterminated line), or `["eof"]`. An empty LF-terminated +line is a line, not EOF; a trailing LF does not manufacture an extra EOF line. + +The explicit canonical limit is0..9223372036854775807 **encoded bytes per line, +excluding LF**. CR, NUL, BOM and all valid Unicode scalars remain content; no CRLF +normalization, BOM stripping or Unicode normalization occurs. This is generic text, +not `readSource`'s stricter compiler-source policy. UTF-8 decoding is strict and +incremental with at most three carry bytes. Framing splits at LF before decoding, +so malformed text in a later buffered line does not invalidate an earlier line. +Byte bounds are checked before decoding each framed piece. + +Reads request at most4093 bytes and preserve bounded read-ahead in the next cursor. +Each returned line is a whole value, occupying space proportional to its selected +limit; processing many lines need not retain the entire stream. This is not a +constant-space arbitrary-length-line facility. No aggregate input, CPU or wall-time +limit is implied. `demos/io-v2/lines.tri` includes both collecting and discarding +consumers; only the latter is used for repeated-line retention controls. + +**Thread only the returned cursor forward.** Serialize cursor use with all other +reads on the same reader. The cursor is ordinary source data; native leases cover +individual reads. Buffered bytes and observed EOF are cached data that can survive +resource closure. A fresh read requires a live reader handle. Cursor representation +is an internal library detail. + +Read/UTF-8/line-size failures return `["read-line" observedLineBytes cause]`, without +a next cursor. Retire that cursor on failure or cancellation; automatic draining, +resynchronization and recovery are not provided. The count includes the current +framed piece on a bound/decode failure, excludes LF/read-ahead belonging to later +lines, and is **not** a file offset or safe retry position. Native decoder byte +locations are relative to that decoder invocation. The underlying stream may already +have consumed read-ahead; reconstructing a fresh cursor is not lossless recovery. +Invalid limits reject before reading, including when the reader is stale. + +The migrated `getLineAsync` demo starts a child read, writes prompts from the parent, +then consumes the child's join ticket. EOF and an empty name differ, and write/read +failures are checked. `greet` and `forkAwait` also use v2; the parent serializes +`forkAwait` output rather than assuming concurrent writes are atomic. Historical +published v1 fixtures remain frozen alongside the current v2 releases. + +```sh +python3 test/io_v2_lines.py result/bin/tricu --directory /new/line-controls +python3 test/io_v2_text.py result/bin/tricu --directory /new/text-controls +``` + +Independent controls cover LF/EOF/CR/BOM/NUL, multibyte carry at the4093-byte read +boundary, malformed and incomplete UTF-8, line limits, a valid line before malformed +buffered data, stale readers, preflight, cancellation with stdin held open, and full +interactive output. Split pipe writes exercise fragmentation but may be coalesced +by the OS; stateless UTF-8 controls independently cover every split. Prompts must +arrive before the harness supplies input. Repeated-line RSS/maximum-live guards are +regression bounds, not capacity guarantees; whole-line collection remains bounded +by the caller's selected per-line budget. + +### Read-only process context + +`IO.workingDirectory` (unit payload, `process/working-directory`) returns Result +host cwd. `IO.environmentVariable name` (`process/environment-variable`) returns +Result `["missing"]` or `["value" text]`; an empty value is `["value" ""]`. Names are +nonempty, NUL-free Unicode scalar text, exclude `=`, and fit4096 scalars. Returned +cwd/values have the same scalar bound and strict text requirements; oversized or +unrepresentable values fail rather than truncate or normalize. Host acquisition +may materialize a value before checking its size; this is not an allocation quota. + +These are read-only, point-in-time OS observations under OS process authority. +Application arguments are explicit exported-function parameters. Subprocess +cwd/env selection is supplied explicitly when spawning each process. + +```sh +python3 test/io_v2_context.py result/bin/tricu --directory /new/context-controls +``` + +Native controls check actual cwd/PATH observations, malformed names/payloads, +strict scalar/size validation and inert inspection. Independent source controls +run in a selected cwd and distinguish absent, empty, Unicode, boundary-size and +oversized environment values. These facilities expose only a requested variable; +value-validation diagnostics use fixed error messages. + +### Basic callers: lexical configuration, state, files and bundle bytes + +`Text.readText reader byteLimit` is a bounded whole-text convenience using the strict +incremental decoder without compiler-source BOM rejection. Initial BOM, NUL and CRLF +are preserved. It shares the whole-source accumulation machinery and retains +the complete decoded text. `readSource` retains its separate raw-byte and source +policy contract unchanged. + +The `environment` demo now uses lexical configuration handlers and demonstrates +restoration of the outer lexical value. +The `state` demo explicitly passes state through a sequential state machine rather +than recreating a global host get/put slot. Neither implies shared mutable cells. +`safeRead` and `shout` use scoped generic UTF-8 reads with an explicit1-MiB budget. +`writeThenRead` checks write and close results before opening the reader. Its main +requires `TRICU_DEMO_OUTPUT` instead of overwriting a shared fixed `/tmp` filename; +its exported `run path text` entrypoint also accepts an explicit path. + +`demos/runArboricxBundle.tri` reads bounded binary bytes and uses the existing portable reader to select/apply the default export. +Validation is not authentication, sandboxing, or a computation-resource quota. +The portable reader permits bytes outside declared sections; its Result remainder +is an internal directory cursor, **not** an EOF indicator. Invalid/truncated sections +and trailing bytes *inside the manifest section* still reject. This migration does +not silently tighten the independently documented container profile. + +```sh +python3 test/io_v2_callers.py result/bin/tricu --directory /new/caller-controls +``` + +Controls compare complete output/results for generic text/BOM/CRLF/NUL, missing and +malformed input, read budgets, failed writes, explicit output-path selection, +lexical restoration, state transitions, binary bundle execution, permitted external +padding and malformed section/container negatives. Frozen historical fixtures and +published package releases remain separate from these live-source migrations. + +### Offline v2 package inspector + +`lib/packages/io.tri` and `demos/package-inspector/` are the canonical native-v2 +package facade and consumer. Reads and hashes take explicit byte budgets; the +inspector selects1MiB and hashes the same byte value it validates. Native failures +remain structured diagnostics. The `arboricx.inspect.v1` report format is unchanged. +Inspection validates graphs and exports without executing them. + +```sh +nix build .# .#inspectorOracle --no-link +HOST=$(nix path-info .#)/bin/tricu +ORACLE=$(nix path-info .#inspectorOracle)/bin/inspector-oracle +nix shell --inputs-from . nixpkgs#python3 -c python3 test/library_inspector.py "$HOST" \ + --oracle "$ORACLE" --directory /new/native-package-acceptance +nix shell --inputs-from . nixpkgs#python3 -c python3 test/provenance.py "$HOST" \ + --accepted /new/native-package-acceptance --directory /new/package-replay +``` + +The first command publishes into a new local registry under default label +`0.5.0-io-v2`, fetches and binds a clean consumer, stops the registry, then compiles +and runs offline. Complete reports are compared with the independent Haskell +oracle. Controls cover malformed and unselected exports, sharing, inert IO, +immutable publication, isolated dependency updates, missing files, OS permission +denial, byte budgets, and rejection of both read grants and `--unsafe-io`. Run as a +non-root user for the OS permission check. Explicit `--io-version 2` is accepted; +current-source publication no longer offers v1 selection. + +The second command captures four input packs, removes its disposable producer, +and rebuilds the facade, foundation, inspector library and consumer from retained +inputs. It checks exact modules/receipts, distinct source histories, and rejects an +authenticated false output claim and producer-cache poisoning. + +Existing published releases retain their bytes. Acceptance writes to the +caller-selected new directory. The optional `--stage-site` operation adds accepted +bundles/releases to an exclusively owned local static-site directory; network +deployment remains a separate operator action. + +The remaining `demos/interactionTrees/{echoServer,httpServer,arboricxServer}.tri` +entrypoints now explicitly construct v2 programs. Echo and simple HTTP are +sequential demonstrations: one scoped connection at a time, no idle timeout or +availability guarantee. Echo pumps bounded chunks until EOF instead of truncating +at the first receive. HTTP retains the existing framing/body limits and HEAD rules. +The registry demo uses the already validated streaming service with FIFO16 +admission and a source-selected1-MiB budget. It initializes `./store`, not a shared +fixed `/tmp/store`, and announces its port only after successful startup. Each +exposes a counted `server` entrypoint for reproducible tests; `main` serves forever. +The packaged registry application and image also default to v2. Historical images +and their authenticated inputs remain available for explicit rollback. + +`python3 test/io_v2_demo_servers.py result/bin/tricu --directory /new/demo-servers` +checks real binary echo, HTTP method/framing/HEAD responses, binary registry +publication/download, and that listeners no longer accept after their scopes finish. +The larger native HTTP/streaming controls remain the service's independent coverage. + +### Validated program/backend boundary + +IO envelope versions, compiler entrypoints and owned-handle ABI versions are +separate contracts. The current Zig owned-handle ABI is **3** and IO protocol is **2**. + +| Program/operation | Haskell native (validated Linux) | Zig native | Haskell browser / Zig browser | +| --- | --- | --- | --- | +| Pure tree evaluation / stored definitions | Yes | Yes | Yes | +| Pure stable module-compiler entrypoint | Yes | Yes | Yes, independently pinned browser compiler | +| Filesystem compiler workflow | IO v2 | IO v2 compiler/file profile | Use pure compilation over supplied sources | +| Historical IO v1 | Retained runtime required | Retained ABI2 runtime required | Inert inspection only | +| IO v2 envelope/core | Structured core | Structured core | Structured core and terminal profile | +| Named facilities | Query `IO.capabilities` | Compiler/file catalogue | Runtime, standard streams, strict UTF-8 | +| Applications | Files, pipelines, tasks, processes and registry | Scoped regular files/tasks and compiler workflows | Structured tasks and bounded terminal pipelines | + +Browser terminal input is encoded as UTF-8 bytes through the session's line/EOF +controls. Stop requests structured cancellation and waits for admitted terminal +operations, tasks and leases to settle; compiled definitions remain usable. +Emergency teardown disposes the Worker without acknowledging resource settlement. +A non-returning native reduction can delay structured cancellation indefinitely. +Current execution accepts canonical v2 framing before observing the action. +Historical trees remain inspectable data. The JS reference graph supports pure +evaluation; IO runs through native browser adapters. +See [public browser integration](../../ext/demo-site/README.md#io-version-boundary) +for terminal byte preservation, capabilities, stepping, result views and acceptance. + +Browser compilation invokes `source_unit.compileStableModuleDefinitions` as a pure +function from its independently authenticated compiler library. Browser execution +provides the terminal profile; native filesystem/network/process workflows use the +corresponding native hosts. Published browser assets retain their own source and +compiler provenance. + +Reproduce the boundary controls without changing release assets or defaults: + +```sh +nix build .#tricu-zig .#tricu-zig-tests .#tricu-wasm-tests .#arboricxSite --no-link +nix shell --inputs-from . nixpkgs#nodejs -c \ + node --test ext/demo-site/test/io-version-boundary.test.mjs +SITE=$(nix path-info .#arboricxSite) +for BACKEND in haskell-wasm zig-wasm; do + TEST_IO_VERSION=1 TEST_BACKEND="$BACKEND" ARBORICX_SITE_ROOT="$SITE" \ + nix shell --inputs-from . nixpkgs#nodejs nixpkgs#chromium -c \ + node ext/demo-site/test/backend-browser.mjs "$SITE" +done +``` + +Boundary controls reject retired v1 and unsupported versions before demanding +poisoned action payloads or invoking callbacks. Both native browser adapters also +exercise current v2 terminal sessions and independently pinned pure compilation. +Full native/browser suites and cross-host compiler reproduction supply their +respective application and generation acceptance. + +### Private Zig structured-core validation + +`test/io_v2_zig_core.py` compiles the actual task/handler controls and checks both +Haskell and `io-v2-core-probe` against independent complete-tree expectations. The +additional core corpus covers cancellation, scope identity, malformed flags and +transfers, consumed tickets, lexical self-join cycles (including a deliberately +wrong expectation), ordinary Results, non-demand of failure/cancellation payloads, +and unsupported requests. A v1 envelope is rejected before its divergent action. +The existing Haskell task suite retains its independent expectations and separate +fresh-host-byte retention workloads; the private Zig probe cannot run those file +operations yet. + +`core-retention.tri` adds distinct, forced 4096-cell child results at16/128/512 +iterations for both consumed joins and discarded tickets. The private probe runs +with frequent GC and a128-MiB process RSS guard. This is bounded-history evidence +for this workload, not an OS-streaming or general memory-quota claim. See the +[Zig guide](../../ext/zig/README.md#io-protocol-boundary) for the Nix/probe command. +Production Zig v2 and cross-host compiler reproduction have separate acceptance +suites; this private probe isolates structured-core behavior. + +`test/io_v2_zig_files.py` validates the separate private native regular-file probe: +49 exact-byte copy/cancellation cases through1MiB, five path-failure/type controls, +position/update/append/close controls, failure after a committed write, and +non-demand of an unknown revision. Fourteen selected compositions/Unicode-path/mode/failure +cases also run on Haskell against independent expectations. This probe exercises its selected regular-file subset. The production compiler/file +profile has separate staging, digest and codec controls. See the Zig guide for +current facilities and synchronous-call limitations. diff --git a/test/io-v2/artifact-service.tri b/test/io-v2/artifact-service.tri @@ -0,0 +1,63 @@ +!import base as B +!import io_v2 as IO +!import handlers as H +!import handler_artifacts as A +!export prepare +!export publish +!export load +!export rawLoad +!export rawPublish +!export routed +!export captured +!export forwarded +!export rejectedView +!import arboricx_handlers as Registry +!import handler_log as Log +!export route +!export routeFailedLog +!export capturedPackage +!export routeStdout +!export routeCancelledLog +!import io_v2_artifacts as Stream +!export streamPublish + +prepare root = IO.io (H.runIOV2 (A.prepareFilesystemV2 root)) +publish root limit bytes = IO.io (H.runIOV2 (A.withFilesystemV2 root limit (A.publish bytes))) +load root limit digest = IO.io (H.runIOV2 (A.withFilesystemV2 root limit (A.load digest))) +rawLoad root limit digest = IO.io (H.runIOV2 (A.withFilesystemV2 root limit (H.request "artifacts" "load" digest))) +rawPublish root limit bytes = IO.io (H.runIOV2 (A.withFilesystemV2 root limit (H.request "artifacts" "publish" bytes))) +routed outer inner limit bytes = IO.io (H.runIOV2 + (A.withFilesystemV2 outer limit (A.withFilesystemV2 inner limit (A.publish bytes)))) +captured outer inner limit bytes = IO.io (H.runIOV2 + (A.withFilesystemForV2 "captured" outer limit + (A.withFilesystemV2 inner limit (A.publishTo "captured" bytes)))) +forwarded root = IO.io (H.runIOV2 + (H.handle "artifacts" (operation payload : H.pure [operation payload]) + (A.withFilesystemV2 root 0 (H.request "artifacts" "other" 42)))) + +rejectedView root limit bytes = H.view (A.withFilesystemV2 root limit (H.request "artifacts" "publish" bytes)) + +silent _ = H.pure (B.ok t t) +failedLog _ = H.pure (B.err "private logger diagnostic" t) +route root limit method target body = IO.io (Registry.routerV2 root limit silent method target [] body) +routeFailedLog root limit body = IO.io (Registry.routerV2 root limit failedLog "POST" "/_arboricx/bundle" [] body) +capturedPackage root other limit target = IO.io (H.runIOV2 + (Registry.request (Registry.packageLookupV2 root limit) "GET" target [] [] + |> A.withFilesystemV2 other limit + |> Log.withSink silent)) +cancelledLog _ = H.liftIO (IO.cancelled "logger stopped") +routeStdout root limit = IO.io (Registry.routerV2 root limit Registry.stdoutLogV2 "GET" "/_arboricx/health" [] []) +routeCancelledLog root limit body = IO.io (IO.scope (_ : do IO.require + spawned <- IO.spawn 1 1 [] (_ : Registry.routerV2 root limit cancelledLog "POST" "/_arboricx/bundle" [] body) + case spawned of + [task ticket] = IO.require (IO.join ticket) IO.pure + _ = IO.fail "Invalid test spawn reply")) +pump reader = H.bind (H.liftIO (IO.readSome reader 4096)) (reply : case reply of + ((t t), (("eof", t), t)) = H.pure (B.ok t t) + ((t t), (("data", bytes), t)) = H.bind (Stream.write bytes) (written : case written of + ((t t), (t, t)) = pump reader + _ = H.pure written) + (t, (_, t)) = H.pure reply + _ = H.fail "invalid-response" "reader" "read" "Invalid read reply") +streamPublish root source expected = IO.io (IO.scope (owner : IO.require + (IO.openReader owner source) (reader : H.runIOV2 (A.publishStreamV2 root expected (pump reader))))) diff --git a/test/io-v2/browser.tri b/test/io-v2/browser.tri @@ -0,0 +1,64 @@ +!import io_v2 as IO +!import io_v2_text as Text +!import handlers as H +!export direct +!export handled +!export roundTrip +!export cancellation +!export main +!export binary +!export invalidUTF8 +!export invalidScalar +!export publicHandled +!export publicRoundTrip +!export publicCancellation +!export publicBinary +!export returnValue + +-- Bare computations exercise the private bridge; explicit envelopes exercise +-- the public RuntimeSession and worker/terminal routing. +direct = do IO.require + writer <- IO.stdout + Text.writeText writer "λ😀é\r\n" + IO.pure 42 +handled = H.runIOV2 (H.liftIO direct) +roundTrip = do IO.require + reader <- IO.stdin + writer <- IO.stdout + chars <- Text.readText reader 4096 + Text.writeText writer chars + IO.pure 42 +cancellation = IO.scope (owner : do IO.require + reader <- IO.stdin + writer <- IO.stdout + spawned <- IO.spawn 1 1 [] (_ : do IO.bind + IO.readSome reader 8 + IO.fail "abandoned continuation resumed") + case spawned of + [task ticket] = do IO.require + Text.writeText writer "ready" + IO.cancel task "stop reader" + outcome <- IO.join ticket + case outcome of + ["cancelled" _ _] = IO.pure 42 + _ = IO.fail "wrong child outcome" + _ = IO.fail "invalid spawn result") +main = IO.io direct + +invalidUTF8 = IO.bind (IO.decodeUTF8 0 [237 160]) (reply : case reply of + (t, (["invalid-data" "utf8/decode" "invalid UTF-8 sequence at byte 0" "none"], t)) = IO.pure 42 + _ = IO.fail "invalid UTF8 prefix was accepted or misclassified") +invalidScalar = IO.bind (IO.encodeUTF8 [55296]) (reply : case reply of + (t, (["invalid-argument" "utf8/encode" "" "none"], t)) = IO.pure 42 + _ = IO.fail "surrogate was accepted or misclassified") + +binary = do IO.require + writer <- IO.stdout + IO.writeAll writer [92 120 102 102 255 240 159] + IO.pure 42 + +publicHandled = IO.io handled +publicRoundTrip = IO.io roundTrip +publicCancellation = IO.io cancellation +publicBinary = IO.io binary +returnValue value = IO.io (IO.pure value) diff --git a/test/io-v2/core-controls.tri b/test/io-v2/core-controls.tri @@ -0,0 +1,58 @@ +!import base as B +!import io_v2 as IO +!export cancelChild +!export currentScopes +!export badFlags +!export badTransfer +!export discardedTicket +!export selfCycle +!export selfCycleWrong +!export pureDomainError +!export rootFailure +!export rootCancelled +!export malformedBind +!export unknownWithPoison +!export legacyEnvelope + +spin seed = IO.bind (IO.pure seed) (_ : spin seed) +diverge seed = diverge seed +joinSpawn reply = case reply of + ((t t), ([task ticket], t)) = IO.require (IO.join ticket) IO.pure + _ = IO.fail "invalid spawn reply" +cancelChild = IO.io <| IO.require (IO.spawn 1 1 [] (_ : spin t)) (spawned : case spawned of + [task ticket] = do IO.bind + _ <- IO.require (IO.cancel task "stop") IO.pure + IO.require (IO.join ticket) IO.pure + _ = IO.fail "invalid spawn reply") +currentScopes = IO.io <| do IO.bind + before <- IO.currentScope + inside <- IO.scope (_ : IO.currentScope) + after <- IO.currentScope + case after of + ^before = case inside of + ^before = IO.pure 0 + _ = IO.pure 1 + _ = IO.pure 0 +badFlags seed = IO.io (IO.spawn 2 1 [] (diverge seed)) +badTransfer seed = IO.io (IO.spawn 1 1 [t] (diverge seed)) +discardedTicket = IO.io <| IO.require (IO.spawn 1 1 [] (_ : IO.pure 5)) (spawned : case spawned of + [task ticket] = IO.bind (IO.discard ticket) (_ : IO.join ticket) + _ = IO.fail "invalid spawn reply") +selfCheck expected = IO.bind IO.currentScope (scope : case scope of + [_ identity key] = IO.scope (_ : IO.bind (IO.join ["io.v2.join" identity key]) (reply : case reply of + (t, ([category _ _ _], t)) = case category of + ^expected = IO.pure t + _ = IO.fail "wrong cycle category" + _ = IO.fail "self join succeeded")) + _ = IO.fail "invalid scope") +-- Parent never claims this ticket. Normal-exit joining must wait for the child's +-- assertion; the wrong-expectation control must fail, not merely lose its result. +selfRun expected = IO.io <| IO.bind (IO.spawn 0 1 [] (_ : selfCheck expected)) (_ : IO.pure 42) +selfCycle = selfRun "join-cycle" +selfCycleWrong = selfRun "not-the-category" +pureDomainError = IO.io (IO.pure (B.err "ordinary" t)) +rootFailure seed = IO.io (IO.fail (diverge seed)) +rootCancelled seed = IO.io (IO.cancelled (diverge seed)) +malformedBind = IO.io (B.pair 1 t) +unknownWithPoison seed = IO.io (IO.request "future" 3 "call" (diverge seed)) +legacyEnvelope seed = B.pair "tricuIO" (B.pair 1 (diverge seed)) diff --git a/test/io-v2/core-retention.tri b/test/io-v2/core-retention.tri @@ -0,0 +1,26 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export joined +!export discarded + +-- Each child creates a different, forced 4096-cell payload. A constant or +-- unevaluated payload would conceal an archive of completed task results. +make 0 _ = [] +make count value = B.pair value (make (N.predecessor count) value) +count [] acc = acc +count [_ | rest] acc = count rest (N.successor acc) +count _ _ = 0 +checked payload = case N.equal (count payload 0) 4096 of + (t t) = IO.pure payload + _ = IO.fail "incomplete fresh payload" +step mode seed = IO.scope (_ : IO.require + (IO.spawn 0 1 [] (_ : checked (make 4096 seed))) (spawned : case spawned of + [task ticket] = case mode of + 0 = IO.require (IO.join ticket) (_ : IO.pure t) + _ = IO.require (IO.discard ticket) (_ : IO.pure t) + _ = IO.fail "invalid spawn reply")) +loop _ 0 = IO.pure t +loop mode left = IO.bind (step mode left) (_ : loop mode (N.predecessor left)) +joined iterations = IO.io (loop 0 iterations) +discarded iterations = IO.io (loop 1 iterations) diff --git a/test/io-v2/indexed-transport.tri b/test/io-v2/indexed-transport.tri @@ -0,0 +1,14 @@ +!import io_v2 as IO +!export verifyTail +!export wrongKind + +verifyTail path limit tree = IO.io <| IO.scope (owner : do IO.onOk + reader <- IO.openReader owner path + IO.verifyIndexedTree reader limit tree + IO.readSome reader 1) + +-- A bad resource kind is rejected before demanding the value to encode. +diverge seed = diverge seed +wrongKind seed = IO.io <| IO.scope (owner : do IO.onOk + digest <- IO.openDigest owner "sha256" + IO.verifyIndexedTree digest 65536 (diverge seed)) diff --git a/test/io-v2/native-cli.tri b/test/io-v2/native-cli.tri @@ -0,0 +1,54 @@ +!import base as B +!import io_v2 as IO +!export compile +!export pure +!export write +!export unsupported +!export inspect +!export capabilities +!export groups +!export failed +!export cancelled +!export divergent +!export legacy +!export future + +pure = IO.io (IO.pure 42) +write path = IO.io <| IO.scope (owner : IO.require (IO.openWriter owner path) (writer : + IO.require (IO.writeSome writer [0 255 65]) IO.pure)) +diverge seed = diverge seed +unsupported seed = IO.io <| IO.bind (IO.request "tcp" 1 "connect" (diverge seed)) (reply : case reply of + (t, (["unsupported" "tcp/connect" _ "none"], t)) = IO.pure 1 + _ = IO.fail "invalid unsupported reply") +inspect seed = IO.io <| IO.require (IO.inspect (IO.request "file" 1 "open-reader" (diverge seed))) (reply : case reply of + ["facility" "file" 1 "open-reader" "available"] = IO.pure 1 + _ = IO.fail "invalid inspection") +find key rows = case rows of + [[name value] | rest] = case name of + ^key = value + _ = find key rest + _ = t +capabilities = IO.io <| IO.require IO.capabilities (rows : case [(find "backend" rows) (find "authority" rows) (find "io-version" rows)] of + ["zig-native" "os-process" 2] = IO.pure 1 + _ = IO.fail "invalid capabilities") +failed seed = IO.io (IO.fail (diverge seed)) +cancelled seed = IO.io (IO.cancelled (diverge seed)) +divergent seed = IO.io (diverge seed) +legacy = B.pair "tricuIO" (B.pair 1 (B.pair 0 23)) +future seed = B.pair "tricuIO" (B.pair 3 (diverge seed)) + +compile worker workspace root identity name = IO.io <| IO.require (worker workspace root identity name) + (built : IO.pure (B.fst built)) + +groups = IO.io <| IO.require IO.capabilities (rows : case find "groups" rows of + [["runtime" 1 ["capabilities" "inspect"]] + ["resource" 1 ["close"]] + ["stream" 1 ["read" "write"]] + ["file" 1 ["open-reader" "open-writer" "open-appender" "open-update" "seek" "position" "size" "resize" "sync"]] + ["path" 1 ["stat" "lstat" "remove-file" "rename"]] + ["directory" 1 ["create" "remove" "sync"]] + ["stage" 1 ["open" "seal" "publish"]] + ["digest" 1 ["open" "update" "finish"]] + ["indexed-tree" 1 ["read" "hash" "write" "verify"]] + ["utf8" 1 ["encode" "decode"]]] = IO.pure 1 + _ = IO.fail "incorrect advertised groups") diff --git a/test/io-v2/protocol.tri b/test/io-v2/protocol.tri @@ -0,0 +1,24 @@ +!import base as B +!import io_v2 as IO +!import handlers as H +!export capabilities +!export handledCapabilities +!export inspectWriter +!export inspectLazyBind +!export inspectLazyScope +!export inspectFuture +!export unsupported +!export invalidHeader +!export retired + +capabilities = IO.io IO.capabilities +handledCapabilities = IO.io <| H.runIOV2 (H.liftIO IO.capabilities) +inspectWriter path = IO.io <| IO.bind IO.currentScope + (owner : IO.inspect (IO.openWriter owner path)) +diverge x = diverge x +inspectLazyBind seed = IO.io <| IO.inspect (B.pair 1 (diverge seed)) +inspectLazyScope seed = IO.io <| IO.inspect (IO.scope (diverge seed)) +inspectFuture seed = IO.io <| IO.inspect (IO.request "future-tcp" 1 "connect" (diverge seed)) +unsupported seed = IO.io <| IO.request "future-tcp" 1 "connect" (diverge seed) +invalidHeader = IO.io <| IO.request "file" 0 "open-reader" t +retired = IO.io (B.pair 21 t) diff --git a/test/io-v2/registry-log-failure.tri b/test/io-v2/registry-log-failure.tri @@ -0,0 +1,10 @@ +!import base as B +!import io_v2 as IO +!import handlers as H +!import arboricx_streaming_v2 as Server +!export main + +-- Exercise the production request path with an ordinary failed logging sink. +failedLog _ = H.pure (B.err "private logger failure" t) +main = IO.io (Server.serve "./store" 1048576 failedLog + (IO.ipv4 [0 0 0 0] 8080) 16 (_ : IO.pure t)) diff --git a/test/io-v2/tasks.tri b/test/io-v2/tasks.tri @@ -0,0 +1,107 @@ +!import http as Http +!import native_math as N +!import base as B +!import io_v2 as IO +!import handlers as H +!export churnJoined +!export churnDiscarded +!export partialWrites +!export failedPartialWrite +!export joined +!export consumed +!export supervised +!export cancelled +!export ordinary +!export inherited +!export changed +!export callback +!export labelledFailure +!export labelledStateBoundary +!export lazyLabels +!export stateBoundary + +joinSpawn reply = case reply of + ((t t), ([task ticket], t)) = IO.require (IO.join ticket) IO.pure + _ = IO.fail "Invalid spawn reply" +runChild action = IO.io <| IO.bind (IO.spawn 1 1 [] (_ : action)) joinSpawn +joined = runChild (IO.pure 42) +supervised = runChild (IO.fail "fault") +cancelled = runChild (IO.cancelled "shutdown") +ordinary = runChild (IO.pure (B.err "domain" t)) +consumed = IO.io <| IO.require (IO.spawn 1 1 [] (_ : IO.pure 42)) (spawned : case spawned of + [task ticket] = do IO.bind + first <- IO.join ticket + second <- IO.join ticket + IO.pure [first second] + _ = IO.fail "Invalid spawn reply") + +value n _ _ = H.pure n +child action = H.bind (H.spawnV2 1 1 [] (_ : action)) (reply : H.liftIO (joinSpawn reply)) +readValue = H.request "value" "get" t +inherited = IO.io <| H.runIOV2 (H.handle "value" (value 7) (H.scopeV2 (owner : do H.bind + answer <- child readValue + parent <- readValue + H.pure [answer parent]))) +changed = IO.io <| H.runIOV2 (H.handle "value" (value 7) (do H.bind + answer <- child (H.handle "value" (value 9) readValue) + parent <- readValue + H.pure [answer parent])) +spawner _ _ = child readValue +callback = IO.io <| H.runIOV2 (H.handle "value" (value 7) + (H.handle "spawn" spawner (H.handle "value" (value 9) (do H.bind + answer <- H.request "spawn" "go" t + parent <- readValue + H.pure [answer parent])))) +diverge x = diverge x +lazyLabels seed = IO.io <| H.runIOV2 (H.label (diverge seed) (child (H.pure 7))) +stateBoundary = runChild (H.runIOV2 (H.collect "log" "write" B.anyC t (child (H.pure 7)))) + +-- Each child obtains a fresh host chunk. Constant/shared payloads would conceal +-- a completion archive in an RSS-only test. Scope exit settles discarded work. +churnStep mode reader = IO.scope (_ : IO.require + (IO.spawn 0 1 [] (_ : IO.require (IO.readSome reader 4096) checkedChunk)) + (spawned : case spawned of + [task ticket] = case mode of + 0 = IO.require (IO.join ticket) (_ : IO.pure t) + _ = IO.require (IO.discard ticket) (_ : IO.pure t) + _ = IO.fail "Invalid spawn reply")) +churnLoop 0 _ _ = IO.pure t +churnLoop count mode reader = IO.bind (churnStep mode reader) + (_ : churnLoop (N.predecessor count) mode reader) +churn mode count path = IO.io <| IO.scope (owner : + IO.require (IO.openReader owner path) (reader : churnLoop count mode reader)) +churnJoined count path = churn 0 count path +churnDiscarded count path = churn 1 count path + +checkedChunk ("data", bytes) = case Http.httpBodyLength bytes of + 4096 = IO.pure bytes + _ = IO.fail "Incomplete retention input" +checkedChunk _ = IO.fail "Unexpected retention EOF" + +labelledFailure = IO.io <| H.runIOV2 (H.label "outer" + (child (H.label "inner" (H.fail "problem" "service" "op" "detail")))) +labelledStateBoundary = runChild (H.runIOV2 (H.label "outer" + (H.collect "log" "write" B.anyC t (H.label "inner" (child (H.pure 7)))))) + +-- Test-only interpreter for the actual library convenience, not a reimplementation +-- of its chunk dropping. Each successful mock accepts at most two bytes; fuel +-- exhaustion fails before any further acceptance. Python specifies full traces. +mockWrites fuel action frames accepted = let + resume value = case frames of + [] = [value accepted] + [next | rest] = mockWrites fuel (next value) rest accepted + _ = ["invalid frames"] + in case action of + (0, value) = resume value + (1, (first, next)) = mockWrites fuel first (B.pair next frames) accepted + (10, ("stream", (1, ("write", (writer, bytes))))) = case fuel of + 0 = resume (B.err "write fault" t) + _ = case bytes of + [a b | rest] = mockWrites (N.predecessor fuel) (IO.pure (B.ok 2 t)) frames + (B.append accepted [a b]) + [a] = mockWrites (N.predecessor fuel) (IO.pure (B.ok 1 t)) frames + (B.append accepted [a]) + _ = ["invalid chunk"] + _ = ["invalid action"] +partialWrites bytes = IO.io <| IO.pure (mockWrites 32 (IO.writeAll t bytes) [] []) +failedPartialWrite bytes = IO.io <| IO.pure (mockWrites 1 (IO.writeAll t bytes) [] []) diff --git a/test/io-v2/zig-digest.tri b/test/io-v2/zig-digest.tri @@ -0,0 +1,27 @@ +!import io_v2 as IO +!export hash +!export lifecycle +!export unsupported +!export transferred + +hash bytes = IO.io <| IO.scope (owner : do IO.require + digest <- IO.openDigest owner "sha256" + IO.updateDigest digest bytes + IO.require (IO.finishDigest digest) IO.pure) +lifecycle = IO.io <| IO.scope (owner : do IO.require + digest <- IO.openDigest owner "sha256" + value <- IO.finishDigest digest + IO.bind (IO.finishDigest digest) (again : + IO.bind (IO.updateDigest digest [0]) (update : + IO.pure [value again update]))) +unsupported algorithm = IO.io <| IO.scope (owner : IO.openDigest owner algorithm) +child [digest] = do IO.require + IO.updateDigest digest [97 98 99] + IO.require (IO.finishDigest digest) IO.pure +child _ = IO.fail "invalid transfer" +transferred = IO.io <| IO.scope (owner : do IO.require + digest <- IO.openDigest owner "sha256" + spawned <- IO.spawn 0 1 [digest] child + case spawned of + [task ticket] = IO.require (IO.join ticket) IO.pure + _ = IO.fail "invalid spawn") diff --git a/test/io-v2/zig-files.tri b/test/io-v2/zig-files.tri @@ -0,0 +1,40 @@ +!import base as B +!import io_v2 as IO +!export positioned +!export appended +!export emptyWrite +!export closed +!export readerWrite +!export failedAfterWrite +!export unknownWithPoison + +positioned path = IO.io <| IO.scope (owner : do IO.require + file <- IO.openUpdate owner path + IO.writeSome file [97 98 99] + IO.seekFile file 1 + content <- IO.readSome file 3 + length <- IO.fileSize file + IO.seekFile file 10 + IO.resizeFile file 1 + IO.syncFile file + position <- IO.filePosition file + eof <- IO.readSome file 1 + IO.pure [content length position eof]) +appended path = IO.io <| IO.scope (owner : do IO.require + file <- IO.openAppender owner path + IO.writeSome file [120 121] + IO.syncFile file + length <- IO.fileSize file + IO.bind (IO.seekFile file 0) (bad : IO.pure [length bad])) +emptyWrite path = IO.io <| IO.scope (owner : IO.require (IO.openWriter owner path) (file : IO.writeSome file [])) +closed path = IO.io <| IO.scope (owner : do IO.require + file <- IO.openReader owner path + IO.close file + IO.close file) +readerWrite path = IO.io <| IO.scope (owner : IO.require (IO.openReader owner path) (file : IO.writeSome file [0])) +failedAfterWrite path = IO.io <| IO.scope (owner : do IO.require + file <- IO.openWriter owner path + IO.writeSome file [42] + IO.fail "preserve bytes, release descriptor") +diverge seed = diverge seed +unknownWithPoison seed = IO.io (IO.request "file" 99 "open-reader" (diverge seed)) diff --git a/test/io-v2/zig-paths.tri b/test/io-v2/zig-paths.tri @@ -0,0 +1,28 @@ +!import io_v2 as IO +!export paths +!export observe +!export badDirectory +!export failedRename + +category (t, ([name _ _ _], t)) = name +category _ = "unexpected" +paths directory source target = IO.io <| IO.scope (owner : do IO.require + IO.createDirectory directory + file <- IO.openWriter owner source + IO.writeSome file [97 98 99] + IO.close file + before <- IO.statPath source + dir <- IO.statPath directory + IO.renamePath source target + after <- IO.lstatPath target + IO.syncDirectory directory + IO.bind (IO.statPath source) (missing : do IO.require + IO.removeFile target + IO.removeDirectory directory + IO.pure [before dir after (category missing)])) +observe path = IO.io <| do IO.require + link <- IO.lstatPath path + followed <- IO.statPath path + IO.pure [link followed] +badDirectory path = IO.io <| IO.bind (IO.syncDirectory path) (reply : IO.pure (category reply)) +failedRename source target = IO.io <| IO.bind (IO.renamePath source target) (reply : IO.pure (category reply)) diff --git a/test/io-v2/zig-stages.tri b/test/io-v2/zig-stages.tri @@ -0,0 +1,24 @@ +!import io_v2 as IO +!export publish +!export failureAfterPublication +!export invalidDestination +!export crossDevice + +prepare owner directory = IO.require (IO.openStage owner directory) (stage : do IO.require + IO.writeAll stage [97 98 99] + IO.sealStage stage + IO.pure stage) +publish directory destination replace = IO.io <| IO.scope (owner : IO.bind (prepare owner directory) (stage : case replace of + 0 = IO.publishNew stage destination + _ = IO.publishReplace stage destination)) +failureAfterPublication directory destination = IO.io <| IO.scope (owner : IO.bind (prepare owner directory) (stage : do IO.require + IO.publishReplace stage destination + IO.fail "do not roll back publication")) +invalidDestination directory destination = IO.io <| IO.scope (owner : IO.bind (prepare owner directory) (stage : + IO.bind (IO.publishNew stage destination) (first : + IO.bind (IO.publishNew stage destination) (second : IO.pure [first second])))) +problem (t, ([category _ _ progress], t)) = [category progress] +problem _ = ["unexpected"] +crossDevice directory destination = IO.io <| IO.scope (owner : IO.bind (prepare owner directory) (stage : + IO.bind (IO.publishNew stage destination) (first : + IO.bind (IO.publishNew stage destination) (second : IO.pure [(problem first) (problem second)])))) diff --git a/test/io-v2/zig-utf8.tri b/test/io-v2/zig-utf8.tri @@ -0,0 +1,17 @@ +!import base as B +!import native_math as N +!import io_v2 as IO +!export excessEncode +!export excessDecode +!export excessWrite +!export excessDigest + +zeros 0 tail = tail +zeros n tail = B.pair 0 (zeros (N.predecessor n) tail) +diverge seed = diverge seed +excessEncode seed = IO.io (IO.encodeUTF8 (zeros 1024 (B.pair (diverge seed) (diverge seed)))) +excessDecode seed = IO.io (IO.decodeUTF8 0 (zeros 4096 (B.pair (diverge seed) (diverge seed)))) +excessWrite path seed = IO.io <| IO.scope (owner : IO.require (IO.openWriter owner path) (writer : + IO.writeSome writer (zeros 4096 (B.pair (diverge seed) (diverge seed))))) +excessDigest seed = IO.io <| IO.scope (owner : IO.require (IO.openDigest owner "sha256") (digest : + IO.updateDigest digest (zeros 4096 (B.pair (diverge seed) (diverge seed))))) diff --git a/test/io_dependencies.py b/test/io_dependencies.py @@ -20,18 +20,18 @@ def main(): host = str(args.tricu.resolve()) with tempfile.TemporaryDirectory(prefix='tricu-io-dependencies-') as temporary: workspace = Path(temporary) - for name, relative in [('base', 'lib/base.tri'), ('io', 'lib/io.tri'), + for name, relative in [('base', 'lib/base.tri'), ('io_v2', 'lib/io-v2.tri'), ('native_math', 'lib/math/native.tri')]: shutil.copyfile(ROOT / relative, workspace / (name + '.tri')) source = workspace / 'main.tri' # Exercise actual IO definitions in a workspace lacking patterns. source.write_text('''!import base * -!import io as IO +!import io_v2 as IO !export main main = if (and? (equal? (IO.pure 7) (pair 0 7)) - (and? (equal? (IO.when true (IO.pure 9)) (IO.pure 9)) - (and? (equal? (IO.unless true (IO.pure 9)) (IO.pure t)) - (equal? (IO.putStr "hello") (pair 10 "hello"))))) 42 0 + (and? (equal? (IO.io (IO.pure 7)) (pair "tricuIO" (pair 2 (pair 0 7)))) + (and? (equal? IO.yield (pair 10 (pair "scheduler" (pair 1 (pair "yield" t))))) + (equal? IO.currentScope (pair 4 t))))) 42 0 ''') command = [host, 'eval', str(source), '--store', str(workspace / 'store'), '--format', 'number', '+RTS', '-N1', '-M2048m', '-RTS'] @@ -45,7 +45,7 @@ main = if (and? (equal? (IO.pure 7) (pair 0 7)) reply = subprocess.run(command, cwd=workspace, capture_output=True, text=True, timeout=180) assert reply.returncode != 0, (reply.stdout, reply.stderr) - print('Current IO compiles without patterns; constructor/conditional and missing-dependency controls pass.') + print('Current IO compiles without patterns; constructor/envelope/facility and missing-dependency controls pass.') if __name__ == '__main__': diff --git a/test/io_v2_artifact_service.py b/test/io_v2_artifact_service.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +"""Actual v2 artifact handlers: exact bytes, integrity, lexical routing and limits.""" +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from source_order import Runner, field +from http_headers import number, result, tree_list, pair +from io_v2_objects import text, problem + +ROOT = Path(__file__).resolve().parent.parent + + +def octets(data): + return tree_list([number(v) for v in data]) + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu', type=Path) + p.add_argument('--directory', type=Path, required=True) + a = p.parse_args() + root = a.directory.resolve(); root.mkdir(parents=True, exist_ok=False) + runner = Runner(a.tricu, root) + bundle = root/'service.arboricx' + runner.run('compile', ['compile', '--file', ROOT/'test/io-v2/artifact-service.tri', + '--store', runner.store, '--output', bundle, '--compile-timeout', '600'], seconds=660) + pin = runner.imported(bundle, 'import') + checks = [] + + def arguments(label, values): + args = ['--io'] + for i, value in enumerate(values): + path = root/f'{label}-{i}.tree'; path.write_bytes(value) + args += ['--arg-tree-file', path] + return args + + def check(label, name, values, expected): + actual = runner.invoke(label, pin, name, arguments(label, values)) + assert actual == expected, label + checks.append(label) + + def path(directory, data): + digest = hashlib.sha256(data).hexdigest() + return directory/'objects'/digest[:3]/(digest+'.arboricx') + + left, right = root/'left', root/'right' + for directory in [left, right]: + for suffix in ['new', 'existing']: + check(directory.name+'-'+suffix, 'prepare', [text(str(directory))], result(True, b'\0')) + limit = number(8192) + for index, data in enumerate([b'', b'\0\xff\x80\r\n', bytes(range(256))*16+b'end']): + digest = text(hashlib.sha256(data).hexdigest()) + for suffix in ['new', 'existing']: + check(f'publish-{index}-{suffix}', 'publish', [text(str(left)), limit, octets(data)], result(True, digest)) + assert path(left, data).read_bytes() == data + check(f'load-{index}', 'load', [text(str(left)), limit, digest], result(True, octets(data))) + corrupt = b'X'+data[1:] if data else b'X' + path(left, data).write_bytes(corrupt) + check(f'corrupt-load-{index}', 'load', [text(str(left)), limit, digest], + problem('integrity-failed', 'artifacts/load', 'digest mismatch', 'unknown')) + check(f'corrupt-winner-{index}', 'publish', [text(str(left)), limit, octets(data)], + problem('invalid-data', 'storage/verify', 'different bytes', 'unknown')) + assert path(left, data).read_bytes() == corrupt + path(left, data).write_bytes(data+b'extra') + check(f'trailing-winner-{index}', 'publish', [text(str(left)), limit, octets(data)], + problem('invalid-data', 'storage/verify', 'different bytes', 'unknown')) + path(left, data).write_bytes(data) + data = b'lexical' + check('nearest-route', 'routed', [text(str(left)), text(str(right)), limit, octets(data)], + result(True, text(hashlib.sha256(data).hexdigest()))) + assert path(right, data).read_bytes() == data and not path(left, data).exists() + data = b'captured' + check('captured-route', 'captured', [text(str(left)), text(str(right)), limit, octets(data)], + result(True, text(hashlib.sha256(data).hexdigest()))) + assert path(left, data).read_bytes() == data and not path(right, data).exists() + check('forward-unknown', 'forwarded', [text(str(left))], tree_list([text('other'), number(42)])) + absent = root/'untouched' + diagnostic = tree_list([text('invalid-digest'), text('expected 64 lowercase hexadecimal hash characters')]) + for index, invalid in enumerate(['../outside', 'A'*64, '0'*63, '0'*65, '\0'*64]): + for name in ['load', 'rawLoad']: + check(f'{name}-invalid-{index}', name, [text(str(absent)), limit, text(invalid)], result(False, diagnostic)) + check('budget-before-paths', 'publish', [text(str(absent)), number(0), octets(b'x')], + problem('invalid-bound', 'storage/hash', 'bytes exceed limit', 'none')) + assert not absent.exists() + data = b'captured' + check('read-budget', 'load', [text(str(left)), number(1), text(hashlib.sha256(data).hexdigest())], + problem('invalid-bound', 'storage/read', 'file exceeds byte limit', 'unknown')) + for label, malformed in [('wide', tree_list([number(256)])), ('improper', b'\1\0')]: + args = arguments('raw-'+label, [text(str(absent)), limit, malformed]) + command = [str(a.tricu.resolve()), 'arboricx', 'run', '--store', str(runner.store), + '--module', pin, '--name', 'rawPublish', *args] + reply = subprocess.run(command, capture_output=True, text=True, timeout=30) + assert reply.returncode != 0 and 'IO v2 task failed' in reply.stderr, reply + actual = runner.invoke('view-'+label, pin, 'rejectedView', args[1:]) + wanted = pair(number(103), tree_list([text('invalid-request'), text('artifacts'), text('publish'), + text('expected octet' if label == 'wide' else 'not a list'), tree_list([])])) + assert actual == wanted, label + checks.append('raw-'+label) + assert not absent.exists() + blocked = root/'blocked'; blocked.write_bytes(b'keep') + actual = runner.invoke('blocked-root', pin, 'prepare', arguments('blocked', [text(str(blocked))])) + assert field(actual, 1, 0, 0) == text('not-directory') + assert blocked.read_bytes() == b'keep' + checks.append('blocked-root') + data = b'concurrent winner' + args = arguments('race', [text(str(right)), limit, octets(data)]) + command = [str(a.tricu.resolve()), 'arboricx', 'run', '--store', str(runner.store), + '--module', pin, '--name', 'publish', '--format', 'ternary', *args, + '+RTS', '-N1', '-M2g', '-RTS'] + processes = [subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) for _ in range(2)] + try: + for process in processes: + stdout, stderr = process.communicate(timeout=60) + assert process.returncode == 0, stderr + assert bytes(int(c) for c in stdout.strip().decode()) == result(True, text(hashlib.sha256(data).hexdigest())) + finally: + for process in processes: + if process.poll() is None: + process.kill(); process.wait() + assert path(right, data).read_bytes() == data + checks.append('concurrent-winner') + data = b'must not write with old grants' + args = arguments('grants', [text(str(right)), limit, octets(data)]) + reply = subprocess.run([str(a.tricu.resolve()), 'arboricx', 'run', '--store', str(runner.store), + '--module', pin, '--name', 'publish', *args, '--allow-write', str(right)], + capture_output=True, text=True, timeout=30) + assert reply.returncode != 0 and 'v1 permission grants are not accepted' in reply.stderr + assert not path(right, data).exists() + checks.append('v1-grants-rejected-before-effects') + def response(status, body, mime='text/plain; charset=utf-8'): + phrase = {200:'OK', 201:'Created', 400:'Bad Request', 404:'Not Found', 500:'Internal Server Error'}[status] + return octets((f'HTTP/1.1 {status} {phrase}\r\nContent-Type: {mime}\r\n' + f'Content-Length: {len(body)}\r\nConnection: close\r\n\r\n').encode()+body) + def route(label, method, target, body, expected): + check(label, 'route', [text(str(left)), limit, text(method), text(target), octets(body)], expected) + route('health', 'GET', '/_arboricx/health', b'', response(200, b'OK\n')) + data = b'route binary\0\xff' + digest = hashlib.sha256(data).hexdigest() + route('route-publish', 'POST', '/_arboricx/bundle', data, response(201, digest.encode())) + route('route-get', 'GET', '/_arboricx/bundle/hash/'+digest, b'', + response(200, data, 'application/vnd.arboricx.bundle')) + path(left, data).write_bytes(b'corrupt') + route('route-corrupt-get', 'GET', '/_arboricx/bundle/hash/'+digest, b'', response(404, b'Artifact unavailable\n')) + route('route-corrupt-put', 'POST', '/_arboricx/bundle', data, response(400, b'Upload failed\n')) + assert path(left, data).read_bytes() == b'corrupt' + data = b'published despite logger failure' + check('failed-log-after-publication', 'routeFailedLog', [text(str(left)), limit, octets(data)], + response(500, b'Internal Server Error\n')) + assert path(left, data).read_bytes() == data + route('after-failed-request', 'GET', '/_arboricx/health', b'', response(200, b'OK\n')) + package = b'captured package bytes' + (left/'packages'/'example').mkdir(parents=True) + (left/'packages'/'example'/'1').write_bytes(package) + (right/'packages'/'example').mkdir(parents=True) + (right/'packages'/'example'/'1').write_bytes(b'wrong root') + target = '/_arboricx/package/example/1' + check('captured-package', 'capturedPackage', [text(str(left)), text(str(right)), limit, text(target)], + response(200, package, 'application/vnd.arboricx.package-release')) + route('package-route', 'GET', target, b'', response(200, package, 'application/vnd.arboricx.package-release')) + route('package-invalid', 'GET', '/_arboricx/package/../escape', b'', response(400, b'Invalid package name/version')) + check('stdout-status', 'routeStdout', [text(str(left)), limit], response(200, b'OK\n')) + assert 'registry status=200\n' in (root/'stdout-status.log').read_text() + data = b'published before cancellation' + check('request-cancellation', 'routeCancelledLog', [text(str(left)), limit, octets(data)], + tree_list([text('cancelled'), text('logger stopped'), tree_list([])])) + assert path(left, data).read_bytes() == data + for size in [0, 1, 4095, 4096, 4097, 65536, 1048576]: + data = (bytes(range(256))*((size+255)//256))[:size] + source = root/f'stream-source-{size}'; source.write_bytes(data) + digest = hashlib.sha256(data).hexdigest() + for mode in ['new', 'existing']: + check(f'stream-{size}-{mode}', 'streamPublish', [text(str(right)), text(str(source)), number(size)], + result(True, text(digest))) + assert path(right, data).read_bytes() == data + assert list((right/'tmp').iterdir()) == [] + if size >= 65536: + record = runner.records[-1] + assert record['peak_kib'] < 128*1024, 'stream RSS retention regression' + assert record['rts_process']['maximum_live_bytes'] < 16*1024*1024, 'stream heap retention regression' + data = b'stream corruption test' + source = root/'stream-negative'; source.write_bytes(data) + check('stream-short', 'streamPublish', [text(str(right)), text(str(source)), number(len(data)+1)], + result(False, text('Incomplete artifact stream'))) + check('stream-excess', 'streamPublish', [text(str(right)), text(str(source)), number(len(data)-1)], + result(False, text('Artifact exceeds declared length'))) + assert not path(right, data).exists() + path(right, data).parent.mkdir(exist_ok=True) + path(right, data).write_bytes(b'X'+data[1:]) + check('stream-corrupt-winner', 'streamPublish', [text(str(right)), text(str(source)), number(len(data))], + result(False, text('Existing artifact digest mismatch'))) + assert path(right, data).read_bytes() == b'X'+data[1:] + assert list((right/'tmp').iterdir()) == [] + assert not list(left.rglob('.tricu-*')) and not list(right.rglob('.tricu-*')) + (root/'report.json').write_text(json.dumps(dict(checks=checks, records=runner.records), indent=2)+'\n') + print(f'PASS {len(checks)} v2 artifact service controls; deployment unchanged') + + +if __name__ == '__main__': + main() diff --git a/test/io_v2_callers.py b/test/io_v2_callers.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Real migrated reader/state/file/bundle callers, with independent outputs.""" +import argparse,json,os,subprocess,struct +from pathlib import Path +from source_order import Runner +from http_headers import result,parse_tree_output +from io_v2_text import text +ROOT=Path(__file__).resolve().parent.parent + +def main(): + p=argparse.ArgumentParser(description=__doc__);p.add_argument('tricu',type=Path);p.add_argument('--directory',type=Path,required=True) + a=p.parse_args();d=a.directory.resolve();d.mkdir(parents=True,exist_ok=False);r=Runner(a.tricu,d);checks=[];pins={} + for name in ['safeRead','shout','writeThenRead','environment','state','bundle']: + source=ROOT/'demos/runArboricxBundle.tri' if name=='bundle' else ROOT/f'demos/interactionTrees/{name}.tri' + bundle=d/(name+'.arboricx');r.run(name+'-compile',['compile','--file',source,'--store',r.store,'--output',bundle,'--compile-timeout','600'],seconds=660);pins[name]=r.imported(bundle,name+'-import') + def call(label,module,name,args=(),env=None,good=True,output=False): + command=[str(r.exe),'arboricx','run','--module',pins[module],'--name',name,'--store',str(r.store),'--io','--format','ternary'] + if output:command+=['--no-result'] + for i,arg in enumerate(args): + f=d/f'{label}-{i}.tree';f.write_bytes(text(arg));command+=['--arg-tree-file',str(f)] + proc=subprocess.run(command,capture_output=True,timeout=60,env=env) + (d/(label+'.stdout')).write_bytes(proc.stdout);(d/(label+'.stderr')).write_bytes(proc.stderr) + assert (proc.returncode==0)==good,(label,proc.stderr) + checks.append(label) + return proc.stdout if output or not good else parse_tree_output(proc.stdout.decode()) + for i,value in enumerate(['','\ufeffλ\r\n\0😀','a'*4092+'😀']): + f=d/f'text-{i}.txt';f.write_bytes(value.encode()) + assert call(f'read-{i}','safeRead','run',[str(f)])==text(value) + assert call(f'shout-{i}','shout','run',[str(f)])==text(value+'!!!') + out=d/f'write-{i}.txt' + assert call(f'roundtrip-{i}','writeThenRead','run',[str(out),value])==result(True,text(value)) + assert out.read_bytes()==value.encode() + missing=d/'missing' + assert call('missing-read','safeRead','run',[str(missing)])==text('ERROR: Unable to read file') + assert call('missing-shout','shout','run',[str(missing)])==text('missing!!!') + invalid=d/'invalid';invalid.write_bytes(b'\xff') + assert call('invalid-text','safeRead','run',[str(invalid)])==text('ERROR: Unable to read file') + over=d/'overlong';over.write_bytes(b'a'*1048577) + assert call('read-budget','safeRead','run',[str(over)])==text('ERROR: Unable to read file') + failure=call('write-failed','writeThenRead','run',[str(missing/'output'),'never']) + assert failure[:2]==b'\2\0' and not missing.exists(),failure + env=dict(os.environ);env.pop('TRICU_DEMO_OUTPUT',None) + call('explicit-output-required','writeThenRead','main',env=env,good=False) + out=d/'configured.txt' + assert call('configured-output','writeThenRead','main',env=dict(env,TRICU_DEMO_OUTPUT=str(out)),output=True)==b'hello from tricu\n' + assert out.read_bytes()==b'hello from tricu' + assert call('lexical-configuration','environment','main',env=dict(env,TRICU_DEMO_OUTPUT='unrelated'),output=True)==b'working in env: demo\nlocal returned: inside-done\noutside env: outer\n' + assert call('explicit-state','state','main',output=True)==b'state: idle\nstate: running\nstate: done\n' + assert call('bundle-main','bundle','main',output=True)==b'hi\n' + # Main must check its final write even when --no-result suppresses values. + for name in ['shout','writeThenRead','environment','state','bundle']: + with open('/dev/full','wb') as full: + run=subprocess.run([str(r.exe),'arboricx','run','--module',pins[name],'--name','main','--store',str(r.store),'--io','--no-result'],stdout=full,stderr=subprocess.PIPE,env=dict(env,TRICU_DEMO_OUTPUT=str(out)),timeout=60) + assert run.returncode!=0,(name,run.stderr) + checks.append(name+'-failed-final-output') + original=(ROOT/'test/fixtures/id.arboricx').read_bytes() + padding=d/'padding.arboricx';padding.write_bytes(original+b'x') + assert call('bundle-permitted-padding','bundle','run',[str(padding),'hi'],output=True)==b'hi\n' + offset=struct.unpack_from('>Q',original,80)[0] + bad=bytearray(original[:offset]+b'x'+original[offset:]) + struct.pack_into('>Q',bad,56,struct.unpack_from('>Q',original,56)[0]+1) + struct.pack_into('>Q',bad,80,offset+1) + for label,data in [('truncated',original[:-1]),('trailing-manifest',bad),('bad-magic',b'X'+original[1:])]: + f=d/(label+'.arboricx');f.write_bytes(data) + assert call('bundle-'+label,'bundle','run',[str(f),'never'],good=False,output=True)==b'' + (d/'report.json').write_text(json.dumps(dict(passed=checks,records=r.records),indent=2)+'\n');print(f'PASS {len(checks)} migrated caller controls') +if __name__=='__main__':main() diff --git a/test/io_v2_clock.py b/test/io_v2_clock.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Actual source timing, scoped waits/cancellation, and migrated async demos.""" +import argparse +import json +from pathlib import Path +import subprocess +from source_order import Runner +from http_headers import number, result, tree_list +from io_v2_objects import text +from io_v2_directory import parse_tree, natural + +ROOT=Path(__file__).resolve().parent.parent + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu',type=Path);p.add_argument('--directory',type=Path,required=True) + a=p.parse_args();root=a.directory.resolve();root.mkdir(parents=True,exist_ok=False) + runner=Runner(a.tricu,root);checks=[] + def compile(label,path): + bundle=root/(label+'.arboricx') + runner.run(label+'-compile',['compile','--file',path,'--store',runner.store,'--output',bundle,'--compile-timeout','600'],seconds=660) + return runner.imported(bundle,label+'-import') + pin=compile('clock',ROOT/'demos/io-v2/clock.tri') + def invoke(label,name,value=None): + args=['--io'] + if value is not None: + path=root/(label+'.tree');path.write_bytes(value);args+=['--arg-tree-file',path] + return runner.invoke(label,pin,name,args) + assert invoke('yield','yielded')==result(True,b'\0');checks.append('yield-unit') + assert invoke('cancelled','cancelledWait')==tree_list([text('cancelled'),text('timer-stop'),b'\0']);checks.append('cancel-max-duration') + for milliseconds in [0,1,20]: + raw=invoke(f'elapsed-{milliseconds}','elapsed',number(milliseconds)) + # Decode the binary kernel tree with the existing reader; compare to an + # independent host duration bound, never a self-derived expected value. + elapsed=natural(parse_tree(raw)) + assert elapsed>=milliseconds*1000000,(milliseconds,elapsed) + checks.append(f'elapsed-{milliseconds}') + assert natural(parse_tree(invoke('scope-wait','scopeWait')))>=20000000 + checks.append('normal-scope-waits') + problem=tree_list([text('invalid-argument'),text('clock/sleep'),b'\0',text('none')]) + for i,value in enumerate([number(9223372036854775808),b'\1\0',b'\2\0\0']): + assert invoke(f'invalid-{i}','invalidSleep',value)==result(False,problem) + checks.append(f'invalid-{i}') + for label,path,wanted in [ + ('async',ROOT/'demos/interactionTrees.tri',b'Parent first!\n2000ms done sleeping!\n5000ms done sleeping!\n'), + ('yield-demo',ROOT/'demos/interactionTrees/yield.tri',b'A says 1\nA says 2\nA says 3\nB says 1\nB says 2\nB says 3\nFinished: doneA doneB\n')]: + demo=compile(label,path) + command=[str(runner.exe),'arboricx','run','--module',demo,'--name','main','--store',str(runner.store),'--io','--no-result'] + run=subprocess.run(command,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=30) + (root/(label+'.stdout')).write_bytes(run.stdout);(root/(label+'.stderr')).write_bytes(run.stderr) + assert run.returncode==0,(label,run.stderr) + assert run.stdout==wanted,(label,run.stdout) + checks.append(label) + (root/'report.json').write_text(json.dumps(dict(passed=checks,records=runner.records),indent=2)+'\n') + print(f'PASS {len(checks)} clock/caller controls') +if __name__=='__main__':main() diff --git a/test/io_v2_compiler_store.py b/test/io_v2_compiler_store.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 +"""Run actual source_object callers with an isolated v2 IO source projection. +No compiler generations, frozen-input edits or default workspace replacement. +""" +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from source_order import Runner +from process_rss import run_guarded +from stable_bundle import byte_tree +from native_indexed_store import indexed_bytes, digest +from executable_object import INDEXED +from io_v2_indexed_write import dag +from io_v2_objects import text, problem +from http_headers import number, result, tree_list + +ROOT = Path(__file__).resolve().parent.parent +PROBE = '''!import source_object as O +!import compiler_io as IO +!import io_v2_storage as S +!export putRaw +!export getRaw +!export putTree +!export getTree +!export copyTree +!export replace +!export read +!export hash +!export publish +!export parent +!export exists +putRaw root domain bytes = IO.io (O.sourceObjectStore root domain bytes) +getRaw root domain digest = IO.io (O.sourceObjectGet root domain digest) +putTree root tree = IO.io (O.sourceObjectPutTree root tree) +getTree root digest = IO.io (O.sourceObjectGetTree root digest) +copyTree source target digest = IO.io <| IO.onOk_ (O.sourceObjectGetTree source digest) + (tree : O.sourceObjectPutTree target tree) +replace path bytes = IO.io (IO.atomicWriteBytes path bytes) +read path limit = IO.io (S.readBytes path limit) +hash limit bytes = IO.io (S.hashBytes limit bytes) +publish directory path mode limit bytes = IO.io (S.publishBytes directory path mode limit bytes) +parent path = S.parentPath path +exists path = IO.io (IO.fileExists path) +''' + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu',type=Path) + parser.add_argument('--directory',type=Path,required=True) + parser.add_argument('--compiler-smoke',action='store_true',help='also build a candidate stable sourceBuildGraph and compile a real dependency graph; not a generation pair') + parser.add_argument('--zig-probe',type=Path,help='execute source-object IO controls on the private Zig host; pure helpers stay on Haskell') + args=parser.parse_args() + root=args.directory.resolve(); root.mkdir(parents=True,exist_ok=False) + projection=root/'projection'; projection.mkdir() + (projection/'inputs').mkdir() + rows=['tricu.workspace.v1'] + for line in (ROOT/'tricu.workspace').read_text().splitlines()[1:]: + name,path=line.split('\t') + selected=ROOT/path + relative='inputs/'+name+'.tri' + (projection/relative).write_bytes(selected.read_bytes()) + rows.append(name+'\t'+relative) + (projection/'tricu.workspace').write_text('\n'.join(rows)+'\n') + source=projection/'probe.tri'; source.write_text(PROBE) + runner=Runner(args.tricu,root) + bundle=root/'probe.arboricx' + runner.run('compile',['compile','--file',source,'--store',runner.store,'--output',bundle]) + pin=runner.imported(bundle,'import') + checks=[] + candidate=None + zig_records=[] + def invoke(label,name,values,expected,io=True): + argv=['--io'] if io else [] + zig_args=[] + for i,value in enumerate(values): + path=root/f'{label}-{i}.tree'; path.write_bytes(value) + argv+=['--arg-tree-file',path] + zig_args.append('tree-file:'+str(path)) + if args.zig_probe and io: + command=[str(args.zig_probe.resolve()),str(bundle),name,*zig_args] + out,peak=run_guarded(command,memory_mib=512,seconds=180) + (root/(label+'-zig.log')).write_text(out) + assert bytes(map(int,out.strip()))==expected,label + zig_records.append(dict(label=label,peak_kib=peak,command=command)) + else: + assert runner.invoke(label,pin,name,argv)==expected,label + checks.append(label) + def clean(path): + assert not list(path.rglob('.tricu-*')),path + for i,payload in enumerate([b'',bytes(range(256)),b'a'*4096+b'last\0\xff']): + store=root/f'raw-{i}' + expected=hashlib.sha256(b'test.raw\0'+payload).hexdigest() + path=store/'objects'/expected[:3]/expected + for status in ['new','existing']: + invoke(f'raw-{i}-{status}','putRaw',[text(str(store)),text('test.raw'),byte_tree(payload)],result(True,text(expected))) + assert path.read_bytes()==payload + clean(store) + invoke(f'raw-get-{i}','getRaw',[text(str(store)),text('test.raw'),text(expected)],result(True,byte_tree(payload))) + path.write_bytes(payload+b'excess') + invoke(f'raw-corrupt-{i}','putRaw',[text(str(store)),text('test.raw'),byte_tree(payload)], + problem('invalid-data','storage/verify','different bytes','unknown')) + assert path.read_bytes()==payload+b'excess'; clean(store) + invoke(f'raw-corrupt-get-{i}','getRaw',[text(str(store)),text('test.raw'),text(expected)], + result(False,text('CAS digest mismatch'))) + for i,tree in enumerate([b'\0',number(42)]): + store=root/f'tree-{i}' + payload=indexed_bytes(tree); expected=digest(INDEXED,payload) + path=store/'objects'/expected[:3]/expected + for status in ['new','existing']: + invoke(f'tree-{i}-{status}','putTree',[text(str(store)),tree],result(True,text(expected))) + assert path.read_bytes()==payload; clean(store) + invoke(f'tree-get-{i}','getTree',[text(str(store)),text(expected)],result(True,tree)) + path.write_bytes(payload+b'excess') + invoke(f'tree-excess-{i}','putTree',[text(str(store)),tree], + problem('invalid-data','storage/verify','trailing bytes','unknown')) + assert path.read_bytes()==payload+b'excess'; clean(store) + invoke(f'tree-corrupt-get-{i}','getTree',[text(str(store)),text(expected)], + problem('integrity-failed','indexed-tree/read','CAS digest mismatch','unknown')) + shared=dag(200,True); expected=digest(INDEXED,shared) + store=root/'shared'; shard=store/'objects'/expected[:3]; shard.mkdir(parents=True) + (shard/expected).write_bytes(shared) + target=root/'shared-copy' + invoke('shared-copy','copyTree',[text(str(store)),text(str(target)),text(expected)],result(True,text(expected))) + assert (target/'objects'/expected[:3]/expected).read_bytes()==shared; clean(target) + alias=root/'alias' + for i,payload in enumerate([b'old',b'new\0\xff',b'']): + invoke(f'replace-{i}','replace',[text(str(alias)),byte_tree(payload)],result(True,b'\1\0')) + assert alias.read_bytes()==payload + for i,path in enumerate(['name','/name','/','a/b','a//b','../name','λ/name','name/','/name///','//name','///']): + expected_parent=['.','/','/','a','a/','..','λ','.','/','//','///'][i] + invoke(f'parent-{i}','parent',[text(path)],text(expected_parent),False) + data=root/'bytes'; data.write_bytes(b'ab\0\xff') + invoke('read-exact','read',[text(str(data)),number(4)],result(True,byte_tree(data.read_bytes()))) + invoke('read-limit','read',[text(str(data)),number(3)],problem('invalid-bound','storage/read','file exceeds byte limit','unknown')) + for i,payload in enumerate([b'',b'a'*4096+b'last']): + invoke(f'hash-{i}','hash',[number(len(payload)),byte_tree(payload)],result(True,text(hashlib.sha256(payload).hexdigest()))) + for label,path,wanted in [('file',data,True),('directory',root,False),('missing',root/'absent',False)]: + invoke('exists-'+label,'exists',[text(str(path))],result(True,b'\1\0' if wanted else b'\0')) + invoke('exists-invalid','exists',[text('bad\0path')],problem('invalid-argument','path/stat','','none')) + for mode in [0,1]: + destination=root/f'invalid-octet-{mode}' + invoke(f'invalid-octet-{mode}','publish',[text(str(root)),text(str(destination)),number(mode),number(4096),tree_list([number(256)])], + problem('invalid-argument','digest/update','','none')) + assert not destination.exists() + # Legitimate concurrent mutable aliases are last-writer-wins. Neither caller + # should spuriously verify another writer's replacement against its own bytes. + alias_arg=root/'race-path.tree'; alias_arg.write_bytes(text(str(alias))) + jobs=[]; payloads=[b'A'*4096+b'first',b'B'*4096+b'second'] + for i,payload in enumerate(payloads): + argument=root/f'race-{i}.tree'; argument.write_bytes(byte_tree(payload)) + command=[str(runner.exe),'arboricx','run','--module',pin,'--name','replace','--store',str(runner.store), + '--io','--format','ternary','--arg-tree-file',str(alias_arg),'--arg-tree-file',str(argument), + '+RTS','-N1','-M512m','-RTS'] + if args.zig_probe: + command=[str(args.zig_probe.resolve()),str(bundle),'replace','tree-file:'+str(alias_arg),'tree-file:'+str(argument)] + jobs.append(subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.PIPE)) + try: + for process in jobs: + out,err=process.communicate(timeout=60) + assert process.returncode==0,(out,err) + assert out.strip()==''.join(map(str,result(True,b'\1\0'))).encode(),out + finally: + for process in jobs: + if process.poll() is None: process.kill() + process.wait() + assert alias.read_bytes() in payloads + checks.append('concurrent-mutable-alias-replacement') + missing=root/'must-not-exist' + invoke('preflight-limit','publish',[text(str(root)),text(str(missing)),number(0),number(1),byte_tree(b'ab')], + problem('invalid-bound','storage/hash','bytes exceed limit','none')) + assert not missing.exists(); clean(root) + if args.compiler_smoke: + compiler_source=projection/'candidate.tri' + compiler_source.write_text('!import source_build as Build\n!import compiler_io as IO\n!export sourceBuildGraph\n!export main\nsourceBuildGraph = Build.sourceBuildGraphStable\nmain workspace root identity name = IO.io (Build.sourceBuildGraphStable workspace root identity name)\n') + compiler_bundle=root/'candidate.arboricx' + runner.run('compile-candidate',['compile','--file',compiler_source,'--store',runner.store, + '--output',compiler_bundle,'--compile-timeout','600'],seconds=660) + compiler=runner.imported(compiler_bundle,'import-candidate') + candidate=dict(manifest=compiler,io_version=2,bundle_sha256=hashlib.sha256(compiler_bundle.read_bytes()).hexdigest()) + app=root/'app'; app.mkdir() + (app/'dependency.tri').write_text('!export pick\n-- λ 😀 combining: é\npick [x | _] = x\npick _ = 0\n') + (app/'main.tri').write_text('!import dependency as D\n!export main\n!export accent\nmain = D.pick [42 7]\naccent = "é"\n') + application=root/'application' + compiled=root/'application.arboricx' + for label in ['cold','warm']: + runner.run('candidate-'+label,['compile','--file',app/'main.tri','--store',application, + '--output',compiled,'--toolchain',compiler,'--tool-store',runner.store, + '--toolchain-io-version','2','--compile-timeout','300'],seconds=330) + program=runner.imported(compiled,'import-'+label) + assert runner.invoke('run-'+label,program,'main',[])==number(42) + assert runner.invoke('accent-'+label,program,'accent',[])==tree_list([number(101),number(769)]) + checks.append('actual-v2-compiler-'+label) + retained_bundle=root/'retained-application.arboricx' + runner.run('retained-compile',['compile','--file',app/'main.tri','--store',root/'retained-application', + '--output',retained_bundle,'--compile-timeout','300'],seconds=330) + assert compiled.read_bytes()==retained_bundle.read_bytes() + checks.append('complete-application-bundle-identity') + # Exactly received UTF-8 provenance, not re-encoded/normalized text. + # Receipts bind these digests even when source bytes are not retained as + # separate objects by the current source build policy. + all_objects=[p.read_bytes() for p in (application/'objects').glob('*/*') if p.is_file()] + for file in [app/'main.tri',app/'dependency.tri']: + expected=hashlib.sha256(b'arboricx.source.v1\0'+file.read_bytes()).hexdigest().encode() + assert any(expected in payload for payload in all_objects),file + clean(application) + if args.zig_probe: + from source_order import field, names + zig_application=root/'zig-application' + zig_application.mkdir() + for label in ['cold','warm']: + command=[str(args.zig_probe.resolve()),str(compiler_bundle),'main', + 'text:'+str(app),'text:'+str(zig_application),'text:'+compiler,'text:main'] + output,peak=run_guarded(command,memory_mib=1024,seconds=600) + (root/('zig-compiler-'+label+'.log')).write_text(output) + answer=bytes(map(int,output.strip())) + assert field(answer,0)==b'\1\0',('zig-compiler',label,answer) + pair_value=field(answer,1,0) + manifest,receipt=names(tree_list([field(pair_value,0),field(pair_value,1)])) + receipt_path=zig_application/'objects'/receipt[:3]/receipt + assert hashlib.sha256(b'arboricx.build-receipt.v1\0'+receipt_path.read_bytes()).hexdigest()==receipt + # Load and authenticate the native-produced store through the + # independent Haskell host; execute both actual module exports. + old_store=runner.store + runner.store=zig_application + try: + assert runner.invoke('zig-run-'+label,manifest,'main',[])==number(42) + assert runner.invoke('zig-accent-'+label,manifest,'accent',[])==tree_list([number(101),number(769)]) + finally: + runner.store=old_store + zig_records.append(dict(label='compiler-'+label,peak_kib=peak,manifest=manifest,receipt=receipt,command=command)) + checks.append('private-zig-compiler-'+label) + zig_bundle=root/'zig-application.arboricx' + runner.run('zig-export',['export','--module',manifest,'--store',zig_application,'--output',zig_bundle]) + assert zig_bundle.read_bytes()==compiled.read_bytes() + checks.append('private-zig-complete-bundle-identity') + clean(zig_application) + original=(app/'main.tri').read_bytes() + accepted_output=compiled.read_bytes() + for label,bad,diagnostic in [('bom',b'\xef\xbb\xbf'+original,'initial source BOM is not permitted'), + ('utf8',b'\xff','utf8/decode'), + ('crlf',original.replace(b'\n',b'\r\n'),'Portable compilation failed: [40, "invalid import reference or qualifier",')]: + (app/'main.tri').write_bytes(bad) + command=[str(runner.exe),'arboricx','compile','--file',str(app/'main.tri'),'--store',str(application), + '--output',str(compiled),'--toolchain',compiler,'--tool-store',str(runner.store), + '--toolchain-io-version','2','--compile-timeout','60','+RTS','-N1','-M512m','-RTS'] + rejected=subprocess.run(command,capture_output=True,text=True,timeout=90) + assert rejected.returncode!=0,(label,rejected.stdout) + assert diagnostic in rejected.stdout+rejected.stderr,(label,rejected.stderr) + assert compiled.read_bytes()==accepted_output + (root/f'rejected-{label}.log').write_text(rejected.stdout+rejected.stderr) + if label=='crlf': + # Preserve the existing language rejection, rather than hide it + # by normalizing line endings in the new transport. + reference=[str(runner.exe),'arboricx','compile','--file',str(app/'main.tri'), + '--store',str(root/'crlf-reference'),'--output',str(root/'crlf.arboricx'), + '+RTS','-N1','-M512m','-RTS'] + legacy=subprocess.run(reference,capture_output=True,text=True,timeout=90) + assert legacy.returncode!=0 + assert legacy.stdout+legacy.stderr==rejected.stdout+rejected.stderr + if args.zig_probe: + # Compare ordinary failure payloads against the same compiled + # worker on Haskell, not host-formatted stderr or a fallback. + raw_args=[] + for i,value in enumerate([str(app),str(zig_application),compiler,'main']): + arg=root/f'negative-{label}-{i}.tree' + arg.write_bytes(text(value)) + raw_args+=['--arg-tree-file',arg] + expected_failure=runner.invoke('worker-reject-'+label,compiler,'main',['--io',*raw_args]) + assert field(expected_failure,0)==b'\0' + command=[str(args.zig_probe.resolve()),str(compiler_bundle),'main', + 'text:'+str(app),'text:'+str(zig_application),'text:'+compiler,'text:main'] + output,peak=run_guarded(command,memory_mib=1024,seconds=180) + (root/('zig-rejected-'+label+'.log')).write_text(output) + assert bytes(map(int,output.strip()))==expected_failure,label + assert zig_bundle.read_bytes()==accepted_output + clean(zig_application) + zig_records.append(dict(label='compiler-rejected-'+label,peak_kib=peak,command=command)) + checks.append('private-zig-compiler-rejects-'+label) + checks.append('actual-v2-compiler-rejects-'+label) + (app/'main.tri').write_bytes(original) + clean(application) + files=['lib/source/object.tri','lib/source/io-v2.tri','lib/io-v2/storage.tri','lib/io-v2.tri','lib/io-v2/text.tri','test/io_v2_compiler_store.py','bootstrap/build-stable.arboricx'] + if args.zig_probe: files += ['ext/zig/src/io_v2_file_host.zig','ext/zig/src/indexed.zig','ext/zig/src/io_v2_snapshot.zig'] + (root/'report.json').write_text(json.dumps(dict(checks=checks,records=runner.records,candidate=candidate,zig_records=zig_records, + zig_probe_sha256=hashlib.sha256(args.zig_probe.read_bytes()).hexdigest() if args.zig_probe else None, + projected_inputs={p.relative_to(projection).as_posix():hashlib.sha256(p.read_bytes()).hexdigest() for p in projection.rglob('*.tri')}, + source_hashes={p:hashlib.sha256((ROOT/p).read_bytes()).hexdigest() for p in files}),indent=2)+'\n') + print(f'{len(checks)} actual compiler-store and storage controls passed') + +if __name__=='__main__': main() diff --git a/test/io_v2_context.py b/test/io_v2_context.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Actual read-only cwd/environment observations, independent of source handlers.""" +import argparse +import json +import os +from pathlib import Path +import subprocess +from source_order import Runner +from http_headers import result, tree_list, parse_tree_output +from io_v2_text import text, problem +ROOT=Path(__file__).resolve().parent.parent + +def main(): + p=argparse.ArgumentParser(description=__doc__);p.add_argument('tricu',type=Path);p.add_argument('--directory',type=Path,required=True) + a=p.parse_args();d=a.directory.resolve();d.mkdir(parents=True,exist_ok=False);r=Runner(a.tricu,d);bundle=d/'context.arboricx' + r.run('compile',['compile','--file',ROOT/'demos/io-v2/context.tri','--store',r.store,'--output',bundle]);pin=r.imported(bundle,'import');checks=[] + key='TRICU_CONTEXT_TEST_VALUE' + def run(label,name,argument,env,wanted): + cmd=[str(r.exe),'arboricx','run','--module',pin,'--name',name,'--store',str(r.store),'--io','--format','ternary'] + if argument is not None: + arg=d/(label+'.arg');arg.write_bytes(text(argument));cmd+=['--arg-tree-file',str(arg)] + proc=subprocess.run(cmd,cwd=d,env=env,capture_output=True,timeout=20) + (d/(label+'.stdout')).write_bytes(proc.stdout);(d/(label+'.stderr')).write_bytes(proc.stderr) + assert proc.returncode==0,(label,proc.stderr) + assert parse_tree_output(proc.stdout.decode())==wanted,label + checks.append(label) + env=dict(os.environ);env.pop(key,None) + run('cwd','directory',None,env,result(True,text(str(d)))) + run('missing','variable',key,env,result(True,tree_list([text('missing')]))) + for i,value in enumerate(['','λ\n\ufeff','x'*4096]): + run(f'value-{i}','variable',key,dict(env,**{key:value}),result(True,tree_list([text('value'),text(value)]))) + run('oversized','variable',key,dict(env,**{key:'x'*4097}),result(False,problem('invalid-bound','process/environment-variable','host context text exceeds scalar limit'))) + for i,name in enumerate(['','A=B','A\0B']): + run(f'invalid-{i}','variable',name,env,result(False,problem('invalid-argument','process/environment-variable',''))) + (d/'report.json').write_text(json.dumps(dict(passed=checks,records=r.records),indent=2)+'\n') + print(f'PASS {len(checks)} host-context controls') +if __name__=='__main__':main() diff --git a/test/io_v2_demo_servers.py b/test/io_v2_demo_servers.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Actual migrated TCP/HTTP/registry demo lifetimes and independent wire bytes.""" +import argparse,json,socket,subprocess,threading,hashlib,selectors +from pathlib import Path +from source_order import Runner +from http_headers import number,tree_list +from io_v2_text import text +ROOT=Path(__file__).resolve().parent.parent + +def main(): + p=argparse.ArgumentParser(description=__doc__);p.add_argument('tricu',type=Path);p.add_argument('--directory',type=Path,required=True) + a=p.parse_args();d=a.directory.resolve();d.mkdir(parents=True,exist_ok=False);r=Runner(a.tricu,d);checks=[];pins={} + def build(name): + bundle=d/(name+'.arboricx');r.run(name+'-compile',['compile','--file',ROOT/f'demos/interactionTrees/{name}.tri','--store',r.store,'--output',bundle,'--compile-timeout','600'],seconds=660) + pins[name]=r.imported(bundle,name+'-import');return pins[name] + def command(name,pin,values): + command=[str(r.exe),'arboricx','run','--module',pin,'--name','server','--store',str(r.store),'--io','--no-result'] + for i,value in enumerate(values): + f=d/f'{name}-{i}.arg';f.write_bytes(value);command+=['--arg-tree-file',str(f)] + return command + def start(name,pin,values): + proc=subprocess.Popen(command(name,pin,values),stdout=subprocess.PIPE,stderr=subprocess.PIPE) + with selectors.DefaultSelector() as sel: + sel.register(proc.stdout,selectors.EVENT_READ);assert sel.select(15),'no listen announcement' + port=int(proc.stdout.readline());return proc,port + def close(proc,port): + out,err=proc.communicate(timeout=20);assert proc.returncode==0,(out,err) + try: socket.create_connection(('127.0.0.1',port),timeout=1).close() + except OSError: return + raise AssertionError('listener outlived owning scope') + def endpoint(port=0):return tree_list([text('ipv4'),tree_list([number(x) for x in [127,0,0,1]]),number(port)]) + def request(port,data): + with socket.create_connection(('127.0.0.1',port),timeout=20) as s: + s.sendall(data);s.shutdown(socket.SHUT_WR);parts=[] + while True: + b=s.recv(4096) + if not b:return b''.join(parts) + parts.append(b) + proc,port=start('echo',build('echoServer'),[endpoint(),number(2)]) + try: + for data in [b'',bytes(range(256))*1024]: + with socket.create_connection(('127.0.0.1',port),timeout=20) as s: + failures=[] + def send(): + try:s.sendall(data);s.shutdown(socket.SHUT_WR) + except Exception as e:failures.append(e) + thread=threading.Thread(target=send);thread.start();out=[] + while True: + chunk=s.recv(4096) + if not chunk:break + out.append(chunk) + thread.join(5);assert not thread.is_alive() and not failures + assert b''.join(out)==data + close(proc,port);checks.append('echo-binary-and-scope-close') + finally: + if proc.poll() is None:proc.kill();proc.wait() + cases=[(b'GET /hello HTTP/1.1\r\nHost: test\r\n\r\n',200,b'Hello from /hello\n'),(b'HEAD /hello HTTP/1.1\r\nHost: test\r\n\r\n',200,b''),(b'POST /hello HTTP/1.1\r\nHost: test\r\nContent-Length: 0\r\n\r\n',405,b'Method not allowed\n'),(b'bad\r\n\r\n',400,b'Bad Request\n')] + proc,port=start('http',build('httpServer'),[endpoint(),number(len(cases))]) + try: + for payload,status,body in cases: + reply=request(port,payload);head,actual=reply.split(b'\r\n\r\n',1) + assert head.startswith(f'HTTP/1.1 {status} '.encode()) and actual==body,(head,actual) + length=len(b'Hello from /hello\n') if payload.startswith(b'HEAD') else len(body) + assert f'Content-Length: {length}\r\n'.encode() in head+b'\r\n' + close(proc,port);checks.append('http-methods-framing-head-and-scope-close') + finally: + if proc.poll() is None:proc.kill();proc.wait() + root=d/'registry';data=b'\0\xffregistry-demo';digest=hashlib.sha256(data).hexdigest() + proc,port=start('registry',build('arboricxServer'),[text(str(root)),endpoint(),number(3)]) + try: + assert request(port,b'GET /_arboricx/health HTTP/1.1\r\nHost: test\r\n\r\n').endswith(b'OK\n') + response=request(port,b'POST /_arboricx/bundle HTTP/1.1\r\nHost: test\r\nContent-Length: '+str(len(data)).encode()+b'\r\n\r\n'+data) + assert response.startswith(b'HTTP/1.1 201 ') and response.endswith(digest.encode()),response + assert request(port,f'GET /_arboricx/bundle/hash/{digest} HTTP/1.1\r\nHost: test\r\n\r\n'.encode()).endswith(b'\r\n\r\n'+data) + close(proc,port);checks.append('registry-binary-upload-download-and-scope-close') + finally: + if proc.poll() is None:proc.kill();proc.wait() + for name,values in [('echoServer',[endpoint(),number(0)]),('httpServer',[endpoint(),number(0)]),('arboricxServer',[text(str(d/'failed-ready-store')),endpoint(),number(0)])]: + with open('/dev/full','wb') as full: + failed=subprocess.run(command(name+'-failed-ready',pins[name],values),stdout=full,stderr=subprocess.PIPE,timeout=20) + assert failed.returncode!=0,(name,failed.stderr) + checks.append(name+'-announcement-failure') + (d/'report.json').write_text(json.dumps(dict(passed=checks,records=r.records),indent=2)+'\n');print('PASS migrated server demo wire/lifecycle controls') +if __name__=='__main__':main() diff --git a/test/io_v2_directory.py b/test/io_v2_directory.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +"""Complete independent directory inventory and path-operation oracles.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +from source_order import Runner +from http_headers import number, pair, result, tree_list +from io_v2_positioned import text +ROOT = Path(__file__).resolve().parent.parent + +def parse_tree(data): + # Iterative independent decoder; tolerate large inventories without Python + # recursion limits. This parses structure, not the native result's text view. + frames = [] + completed = None + for tag in data: + assert tag in (0, 1, 2), tag + if tag: + frames.append([tag, []]) + continue + value = (0,) + while frames: + frame = frames[-1] + frame[1].append(value) + if len(frame[1]) != frame[0]: + break + frames.pop() + value = (frame[0], *frame[1]) + else: + assert completed is None + completed = value + assert not frames and completed is not None + return completed + +def items(value): + output = [] + while value != (0,): + assert value[0] == 2 + output.append(value[1]) + value = value[2] + return output + +def natural(value): + n, place = 0, 1 + while value != (0,): + assert value[0] == 2 and value[1] in ((0,), (1,(0,))) + n += place * (value[1] != (0,)) + place *= 2 + value = value[2] + return n + +def string(value): + return ''.join(chr(natural(c)) for c in items(value)) + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + source = ROOT/'demos/io-v2/directory.tri' + bundle = directory/'directory.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + checks = [] + def invoke(label, name, *paths): + argv = ['--io'] + for index, value in enumerate(paths): + path = directory/f'{label}-{index}.tree' + path.write_bytes(text(str(value))) + argv += ['--arg-tree-file', path] + return runner.invoke(label, pin, name, argv) + unit = result(True, b'\0') + root = directory/'store-λ' + assert invoke('initialize', 'initialize', root) == unit + assert invoke('initialize-again', 'initialize', root) == unit + assert sorted(p.name for p in root.iterdir()) == ['cache'] + checks += ['initialize', 'initialize-again'] + (root/'file').write_bytes(b'a\0\xff') + (root/'link').symlink_to('file') + (root/'dangling').symlink_to('absent') + for name, expected in [('file',('file',3)),('cache',('directory',)),('link',('symlink',))]: + actual = parse_tree(invoke('metadata-'+name,'metadata',root/name)) + followed, unfollowed = items(actual) + wanted = tree_list([text(expected[0])] + ([number(expected[1])] if len(expected)>1 else [])) + assert unfollowed == parse_tree(result(True,wanted)) + if name != 'link': + assert followed == unfollowed + else: + assert followed == parse_tree(result(True,tree_list([text('file'),number(3)]))) + checks.append('metadata-'+name) + actual = items(parse_tree(invoke('inventory','inventory',root))) + observed = {} + for row in actual: + name, status = items(row) + key = string(name) + assert key not in observed + parts = items(status) + observed[key] = [string(parts[0])] + [natural(p) for p in parts[1:]] + assert observed == {'cache':['directory'],'file':['file',3],'link':['symlink'],'dangling':['symlink']} + checks.append('inventory') + (root/'replacement').write_bytes(b'new\x80') + assert invoke('rename','rename',root/'replacement',root/'file') == unit + assert (root/'file').read_bytes() == b'new\x80' and not (root/'replacement').exists() + assert invoke('unlink','removeFile',root/'link') == unit + assert (root/'file').read_bytes() == b'new\x80' and not (root/'link').is_symlink() + assert invoke('rmdir','removeDirectory',root/'cache') == unit + checks += ['rename','unlink','rmdir'] + reply = items(parse_tree(invoke('nonempty','removeDirectory',root))) + assert reply[0] == (0,) + problem = items(reply[1]) + assert string(problem[1]) == 'directory/remove' and string(problem[3]) == 'unknown' + assert (root/'file').read_bytes() == b'new\x80' + conflict = directory/'not-a-directory' + conflict.write_bytes(b'keep') + expected = result(False,tree_list([text(s) for s in ['not-directory','directory/ensure','','none']])) + assert invoke('ensure-file','initialize',conflict) == expected + assert conflict.read_bytes() == b'keep' + checks += ['nonempty','ensure-file'] + alias = directory/'directory-alias' + alias.symlink_to(root, target_is_directory=True) + assert invoke('ensure-symlink','initialize',alias) == unit + assert (root/'cache').is_dir() + checks.append('ensure-symlink') + followed, unfollowed = items(parse_tree(invoke('dangling','metadata',root/'dangling'))) + assert unfollowed == parse_tree(result(True,tree_list([text('symlink')]))) + failed = items(followed) + assert failed[0] == (0,) + problem = items(failed[1]) + assert [string(problem[i]) for i in [0,1,3]] == ['not-found','path/stat','none'] + checks.append('dangling') + # Real host-invalid name: must fail, never silently replace characters/skip it. + invalid = directory/'invalid' + invalid.mkdir() + os.symlink(b'absent',os.fsencode(invalid)+b'/\xff') + try: + invoke('invalid-encoding','count',invalid) + except Exception as error: + assert 'IO v2 task failed' in str(getattr(error,'output',error)), error + else: + raise AssertionError('invalid name was silently accepted') + checks.append('invalid-encoding') + for count in [0,16,256,4096,16384]: + path = directory/f'count-{count}' + path.mkdir() + for index in range(count): + (path/f'{index:06}-entry-λ').touch() + assert invoke(f'count-{count}','count',path) == number(count) + checks.append(f'count-{count}') + observations = [r for r in runner.records if r['label'].startswith('count-')] + for row in observations: + assert row['peak_kib'] <= 128*1024, row + assert row['rts_process']['maximum_live_bytes'] <= 2*1024*1024, row + files = [source,Path(__file__).resolve(),ROOT/'lib/io-v2.tri',ROOT/'src/IOV2/Filesystem.hs', + ROOT/'src/IOV2/Resources.hs',ROOT/'src/IOV2/Driver.hs',ROOT/'src/IOV2/Protocol.hs', + ROOT/'test/source_order.py',ROOT/'test/rts_metrics.py',ROOT/'bootstrap/build-stable.arboricx'] + (directory/'report.json').write_text(json.dumps(dict(checks=checks,observations=observations, + sources={str(p.relative_to(ROOT)):hashlib.sha256(p.read_bytes()).hexdigest() for p in files}),indent=2)+'\n') + print(f'PASS: {len(checks)} directory/path controls') +if __name__ == '__main__': + main() diff --git a/test/io_v2_files.py b/test/io_v2_files.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""Compile/run the experimental v2 copier through the retained v1 compiler. + +Nix-built host required. No grants are passed to the v2 application. Compares +complete output files and result trees for direct and handler-backed compositions. +""" +import argparse +import hashlib +import json +import re +from pathlib import Path + +from source_order import Runner, field +from stable_bundle import byte_tree +from http_headers import result, tree_list + +ROOT = Path(__file__).resolve().parent.parent + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + args.directory.mkdir(parents=True, exist_ok=False) + directory = args.directory.resolve() + runner = Runner(args.tricu, directory) + bundle = directory/'copy.arboricx' + runner.run('compile', ['compile', '--file', ROOT/'demos/io-v2/copy.tri', + '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + checks = [] + for name in ('copy', 'handledCopy', 'borrowedCopy', 'transferredCopy', 'joinedOnExitCopy', 'handledChildCopy'): + for size in (0, 1, 4095, 4096, 4097, 65536, 1048576): + label = f'{name}-{size}' + source, target = directory/(label+'.in'), directory/(label+'.out') + body = (bytes(range(256))*((size+255)//256))[:size] + source.write_bytes(body) + argv = ['--io'] + for index, path in enumerate((source, target)): + argument = directory/f'{label}-{index}.tree' + argument.write_bytes(byte_tree(str(path).encode())) + argv += ['--arg-tree-file', argument] + actual = runner.invoke(label, pin, name, argv) + expected = result(True, b'\0') + if name == 'handledCopy': + expected = result(True, expected) + assert actual == expected, label + assert target.read_bytes() == body, label + checks.append({'name': label, 'sha256': hashlib.sha256(body).hexdigest()}) + for name in ('copy', 'handledCopy'): + # Independent failure controls: no destination on a missing source, and + # a failed writer acquisition after a successful reader acquisition. + for case in ('missing-source', 'missing-parent'): + source = directory/(name+'-failure.in') + source.write_bytes(b'not copied') + target = directory/(name+'-failure.out') + if case == 'missing-source': + source = directory/'NO-SUCH-SOURCE' + else: + target = directory/'NO-SUCH-DIRECTORY'/'out' + label = name+'-'+case + argv = ['--io'] + for index, path in enumerate((source, target)): + argument = directory/f'{label}-{index}.tree' + argument.write_bytes(byte_tree(str(path).encode())) + argv += ['--arg-tree-file', argument] + actual = runner.invoke(label, pin, name, argv) + assert field(actual, 0) == b'\0', label + diagnostic = field(actual, 1, 0) + assert field(diagnostic, 0) == byte_tree(b'not-found'), label + assert field(diagnostic, 1, 0) == byte_tree(b'file/open-reader' if case == 'missing-source' else b'file/open-writer'), label + assert not target.exists(), label + checks.append({'name': label}) + for size in (0, 1, 4096, 4097, 65536): + label = f'cancelledCopy-{size}' + source, target = directory/(label+'.in'), directory/(label+'.out') + body = (bytes(range(256))*((size+255)//256))[:size] + source.write_bytes(body) + argv = ['--io'] + for index, path in enumerate((source, target)): + argument = directory/f'{label}-{index}.tree' + argument.write_bytes(byte_tree(str(path).encode())) + argv += ['--arg-tree-file', argument] + actual = runner.invoke(label, pin, 'cancelledCopy', argv) + assert actual == tree_list([byte_tree(b'cancelled'), byte_tree(b'requested'), b'\0']), label + assert target.read_bytes() == body[:4096], label + checks.append({'name': label, 'sha256': hashlib.sha256(target.read_bytes()).hexdigest()}) + # Regression guards for this selected copier, not general resource SLAs. + observations = [row for row in runner.records if row['label'].endswith(('-65536', '-1048576'))] + for row in observations: + assert row['peak_kib'] is not None and row['peak_kib'] <= 128*1024, row + match = re.search(r'([0-9,]+) bytes maximum residency', (directory/(row['label']+'.log')).read_text()) + assert match, row + row['maximum_live_bytes'] = int(match[1].replace(',', '')) + assert row['maximum_live_bytes'] <= 16*1024*1024, row + report = {'checks': checks, 'sources': {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() + for p in [ROOT/'src/IOV2/Resources.hs', ROOT/'src/IOV2/Driver.hs', ROOT/'src/IOV2/Protocol.hs', + ROOT/'test/source_order.py', ROOT/'test/rts_metrics.py', ROOT/'src/IOV2/Tasks.hs', ROOT/'lib/io-v2.tri', + ROOT/'demos/io-v2/copy.tri', ROOT/'lib/handlers.tri']}} + report['observations'] = observations + (directory/'report.json').write_text(json.dumps(report, indent=2)+'\n') + print(f'PASS: {len(checks)} binary-copy and failure checks, including handler composition') + + +if __name__ == '__main__': + main() diff --git a/test/io_v2_generation.py b/test/io_v2_generation.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""Prepare an explicit v2 map-builder kit and exercise a scoped generation. + +The current compiler prepares v2 actions. The real generation runner +then stages all mapped sources and builds native_math over v2. This is deliberately +NOT a full compiler generation pair or deployment/retention acceptance. +""" +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +import sys +from source_order import Runner +from bootstrap_generations import source_map +from bootstrap_inputs import load_kit +from generation_io import produced_adapter, versions + +ROOT = Path(__file__).resolve().parent.parent + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu', type=Path) + p.add_argument('--kit', type=Path, help='validate an explicitly prepared v2 kit instead of preparing a current candidate') + p.add_argument('--directory', type=Path, required=True) + p.add_argument('--zig-library',type=Path,help='also run the explicit v2 executor and compare complete inventories') + args = p.parse_args() + root = args.directory.resolve(); root.mkdir(parents=True, exist_ok=False) + mapping = ROOT/'bootstrap/compiler-source-map.io-v2.v1' + runner = Runner(args.tricu, root) + if args.kit: + kit = args.kit.resolve() + raw, _, _ = load_kit(kit, 'stable') + metadata = json.loads(raw) + from bootstrap_inputs import kit_io_version + if kit_io_version(metadata) != 2: + raise ValueError('this probe requires an explicit v2 kit') + else: + projection = root/'projection'; projection.mkdir() + (projection/'inputs').mkdir() + _, entries, _ = source_map(mapping, ROOT, 'source_map') + workspace = ['tricu.workspace.v1'] + for alias, (_, relative, _) in entries.items(): + target = 'inputs/'+alias+'.tri' + (projection/target).write_bytes((ROOT/relative).read_bytes()) + workspace.append(alias+'\t'+target) + (projection/'tricu.workspace').write_text('\n'.join(workspace)+'\n') + worker = projection/'worker.tri' + worker.write_text('!import source_map as M\n!export sourceMapBuildStable\nsourceMapBuildStable = M.sourceMapBuildStable\n') + adapter = projection/'adapter.tri' + adapter.write_text('!import io_v2 as IO\n!export main\nmain build source overlay store compiler mapping root = IO.io (build source overlay store compiler mapping root)\n') + kit = root/'kit'; kit.mkdir() + for label, source, destination in [('worker',worker,'rebuilt-compiler.arboricx'),('adapter',adapter,'io-adapter.arboricx')]: + runner.run('compile-'+label,['compile','--file',source,'--store',runner.store, + '--output',kit/destination,'--compile-timeout','600'],seconds=660) + metadata = dict(schema='tricu.bootstrap-kit.v2', backend='stable', io_version=2, artifacts={}) + for role, filename, export in [('compiler','rebuilt-compiler.arboricx','sourceMapBuildStable'),('adapter','io-adapter.arboricx','main')]: + metadata['artifacts'][role]=dict(file=filename,export=export,sha256=hashlib.sha256((kit/filename).read_bytes()).hexdigest()) + (kit/'bootstrap-kit.json').write_text(json.dumps(metadata,indent=2)+'\n') + load_kit(kit,'stable') + checks=[] + def generation(label, extra=(), success=True, selected_kit=kit, preparation_failure=False): + output=root/label + cmd=[sys.executable,str(ROOT/'test/bootstrap_generations.py'),str(output), + '--bootstrap-kit',str(selected_kit),'--tricu',str(args.tricu.resolve()), + '--map',str(mapping),'--source-root',str(ROOT),'--root','native_math', + '--backend','stable','--memory-mib','8192','--wall-seconds','600',*extra] + reply=subprocess.run(cmd,capture_output=True,text=True,timeout=660) + (root/(label+'.log')).write_text(reply.stdout+reply.stderr) + assert (reply.returncode==0)==success,(label,reply.stdout[-2000:],reply.stderr) + if success: + status=json.loads((output/'status.json').read_text()) + assert status['complete'] and versions(status)==(2,2) + assert produced_adapter(output,status)==(kit/'io-adapter.arboricx').read_bytes() + invocation=json.loads((output/'invocation.json').read_text()) + assert '--unsafe-io' not in invocation + assert not list(output.rglob('.tricu-*')) + elif preparation_failure: + assert not json.loads((output/'status.json').read_text())['complete'] + assert not (output/'cas').exists(), 'invalid adapter started compilation' + else: + assert not output.exists(), 'selection failure created output' + checks.append(label) + return output + generation('wrong-version',('--execution-io-version','1'),False) + generation('missing-produced-adapter',('--produced-io-version','1'),False) + generation('noncanonical-version',('--execution-io-version','02'),False) + fake_library=root/'not-a-zig-library'; fake_library.write_bytes(b'not loaded') + generation('invalid-zig-runtime',('--execution-host','zig','--zig-library',str(fake_library)),False) + generation('invalid-produced-adapter',('--produced-io-version','1','--produced-adapter',str(fake_library)),False,preparation_failure=True) + output=generation('scoped') + # Independent existing store/receipt audit runs inside the generation runner. + # Compare actual complete native_math manifest with the authenticated packaged v2 kit. + control=root/'packaged-control' + command=[sys.executable,str(ROOT/'test/bootstrap_generations.py'),str(control), + '--bootstrap-kit',str(ROOT/'bootstrap/rebuild'),'--tricu',str(args.tricu.resolve()), + '--map',str(mapping),'--source-root',str(ROOT),'--root','native_math', + '--backend','stable','--memory-mib','8192','--wall-seconds','600', + '--produced-io-version','2','--produced-adapter',str(kit/'io-adapter.arboricx')] + reply=subprocess.run(command,capture_output=True,text=True,timeout=660) + (root/'packaged-control.log').write_text(reply.stdout+reply.stderr) + assert reply.returncode==0,reply.stderr + def manifest(directory): + return (directory/'cas/cache/source-build/native_math').read_text().split('\t')[2] + assert manifest(output)==manifest(control) + assert versions(json.loads((control/'status.json').read_text()))==(2,2) + checks.append('selected-packaged-complete-manifest-equality') + zig_output = None + if args.zig_library: + zig_output = generation('zig-scoped',('--execution-host','zig','--zig-library',str(args.zig_library.resolve()))) + def inventory(directory): + return {p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in (directory/'cas/objects').glob('*/*')} + assert inventory(zig_output)==inventory(output) + assert manifest(zig_output)==manifest(output) + checks.append('haskell-zig-complete-inventory-equality') + from zig_generation_executor import validate_library + try: + validate_library(args.zig_library.resolve(), 1) + except (ValueError, AttributeError): + checks.append('current-zig-rejects-v1-before-preparation') + else: + raise AssertionError('current runtime still exposes v1 execution') + # Captured request tampering must fail before any IO execution, not select + # another interpreter or silently reinterpret absent/Boolean metadata. + request_path=zig_output/'execution-request.json' + original=request_path.read_bytes() + before=inventory(zig_output) + command=json.loads((zig_output/'invocation.json').read_text()) + try: + for label,value in [('wrong',1),('boolean',True),('future',3),('missing',None)]: + request=json.loads(original) + if value is None: request.pop('execution_io_version') + else: request['execution_io_version']=value + request_path.write_text(json.dumps(request)) + reply=subprocess.run(command,capture_output=True,text=True,timeout=30) + assert reply.returncode!=0,(label,reply.stdout) + assert 'IO version' in reply.stderr,(label,reply.stderr) + assert inventory(zig_output)==before + checks.append('zig-request-'+label) + finally: + request_path.write_bytes(original) + (root/'report.json').write_text(json.dumps(dict(checks=checks,records=runner.records, + kit=metadata,scoped_generation=str(output),zig_generation=str(zig_output) if zig_output else None,generation_pair=False),indent=2)+'\n') + print(f'{len(checks)} generation boundary controls passed; no full compiler pair built') + + +if __name__=='__main__': main() diff --git a/test/io_v2_http.py b/test/io_v2_http.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +"""Independent real-socket v2 registry framing, streaming, admission and cleanup.""" +import argparse +import concurrent.futures +from contextlib import contextmanager +import hashlib +import json +from pathlib import Path +import select +import socket +import struct +import subprocess +import threading +import time +from source_order import Runner +from http_headers import number +from io_v2_objects import text +from process_rss import ChildRSS +from rts_metrics import parse_rts_stats + +ROOT = Path(__file__).resolve().parent.parent +PHRASE = {200:'OK',201:'Created',400:'Bad Request',404:'Not Found',413:'Content Too Large',431:'Request Header Fields Too Large',500:'Internal Server Error',505:'HTTP Version Not Supported'} +def expected(status, body, mime='text/plain; charset=utf-8', head=False): + return (f'HTTP/1.1 {status} {PHRASE[status]}\r\nContent-Type: {mime}\r\nContent-Length: {len(body)}\r\nConnection: close\r\n\r\n').encode()+(b'' if head else body) +def header(length): + return f'POST /_arboricx/bundle HTTP/1.1\r\nHost: local\r\nContent-Length: {length}\r\n\r\n'.encode() +def receive(peer): + chunks=[] + while True: + chunk=peer.recv(65536) + if not chunk: return b''.join(chunks) + chunks.append(chunk) +def wait(predicate): + until=time.monotonic()+15 + while time.monotonic()<until: + if predicate(): return + time.sleep(.02) + raise AssertionError('condition timeout') + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu',type=Path); p.add_argument('--directory',type=Path,required=True) + a=p.parse_args(); directory=a.directory.resolve(); directory.mkdir(parents=True,exist_ok=False) + runner=Runner(a.tricu,directory) + bundle=directory/'registry.arboricx' + runner.run('compile',['compile','--file',ROOT/'demos/io-v2/registry.tri','--store',runner.store,'--output',bundle,'--compile-timeout','600'],seconds=660) + pin=runner.imported(bundle,'import'); checks=[]; observations=[] + @contextmanager + def server(label,count,window=4,name='server',root=None): + root=root or directory/(label+'-files') + argv=[str(runner.exe),'arboricx','run','--module',pin,'--name',name,'--store',str(runner.store),'--io','--no-result'] + for i,value in enumerate([text(str(root)),number(count),number(window)]): + path=directory/f'{label}-{i}.tree';path.write_bytes(value);argv+=['--arg-tree-file',str(path)] + argv+=['+RTS','-N2','-M256m','-T','-s','-RTS'] + log=(directory/(label+'.stderr')).open('wb') + proc=subprocess.Popen(argv,stdin=subprocess.PIPE if name=='cancelledServer' else subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=log) + stopped=threading.Event(); row=dict(label=label,command=argv,peak_kib=0) + def monitor(): + try: + rss=ChildRSS(proc) + while not stopped.is_set(): + value=rss.sample() + if value is not None: + row['peak_kib']=max(row['peak_kib'],value) + if value>256*1024: raise AssertionError('RSS guard exceeded') + stopped.wait(.02) + except Exception as error: + row['monitor_error']=repr(error);proc.kill() + thread=threading.Thread(target=monitor,daemon=True);thread.start();started=time.monotonic() + try: + assert select.select([proc.stdout],[],[],45)[0], 'startup timeout' + line=proc.stdout.readline() + assert line.strip().isdigit(), (line,(directory/(label+'.stderr')).read_text()) + port=int(line) + def connect(): + return socket.create_connection(('127.0.0.1',port),timeout=90) + def request(wire,fragment=False): + with connect() as peer: + if fragment: + for start in range(0,len(wire),997):peer.sendall(wire[start:start+997]) + else:peer.sendall(wire) + peer.shutdown(socket.SHUT_WR) + return receive(peer) + def cancel(): + proc.stdin.write(b'x');proc.stdin.flush() + request.cancel=cancel + yield root,connect,request + stdout,_=proc.communicate(timeout=120) + assert proc.returncode==0, (directory/(label+'.stderr')).read_text() + assert b'private logger detail' not in stdout + assert not list(root.rglob('.tricu-stage-*')) + row['rts_process']=parse_rts_stats((directory/(label+'.stderr')).read_text()) + assert row['rts_process']['maximum_live_bytes']<64*1024*1024,row + if label in ['binary','lifecycle','admission']: + assert row['peak_kib']<128*1024,row + assert row['rts_process']['maximum_live_bytes']<16*1024*1024,row + assert 'monitor_error' not in row,row + row['seconds']=time.monotonic()-started;observations.append(row) + finally: + if proc.poll() is None:proc.kill();proc.communicate() + stopped.set();thread.join();log.close() + def object_path(root,data): + digest=hashlib.sha256(data).hexdigest() + return digest,root/'objects'/digest[:3]/(digest+'.arboricx') + def check(label,actual,wanted): + assert actual==wanted,(label,actual[:300],wanted[:300]);checks.append(label) + + cases=[ + ('health',b'GET /_arboricx/health HTTP/1.1\r\nHost: local\r\n\r\n',200,b'OK\n',False), + ('head',b'HEAD /_arboricx/health HTTP/1.0\r\n\r\n',200,b'OK\n',True), + ('duplicate-length',header(0).replace(b'\r\n\r\n',b'\r\nContent-Length: 0\r\n\r\n'),400,b'Bad Request\n',False), + ('transfer-encoding',b'POST /_arboricx/bundle HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n',400,b'Bad Request\n',False), + ('oversize',header(1048577),413,b'Request body too large\n',False), + ('length-junk',header(1048577).replace(b'1048577',b'1048577x'),400,b'Bad Request\n',False), + ('bad-version',header(0).replace(b'HTTP/1.1',b'HTTP/9.0'),505,b'HTTP Version Not Supported\n',False), + ('syntax-before-version',b'POST /_arboricx/bundle HTTP/9.0\r\nbroken\r\n\r\n',400,b'Bad Request\n',False), + ('head-syntax-body',b'HEAD /_arboricx/health HTTP/1.1\r\nbroken\r\n\r\n',400,b'Bad Request\n',False), + ('head-length-error',b'HEAD /_arboricx/health HTTP/1.1\r\nContent-Length: x\r\n\r\n',400,b'Bad Request\n',True), + ('missing-length',b'POST /_arboricx/bundle HTTP/1.1\r\n\r\nignored',201,hashlib.sha256(b'').hexdigest().encode(),False), + ('short-get-body',b'GET /_arboricx/health HTTP/1.1\r\nContent-Length: 5\r\n\r\nabc',400,b'Unexpected end of request body\n',False), + ] + cases += [('header-limit', b'GET /_arboricx/health HTTP/1.1\r\nX-Long: '+b'x'*65536+b'\r\n\r\n',431,b'Request Header Fields Too Large\n',False)] + with server('framing',len(cases)) as (root,connect,request): + for label,wire,status,body,head in cases: + check(label,request(wire),expected(status,body,head=head)) + with server('binary',14) as (root,connect,request): + for size in [0,1,4095,4096,4097,65536,1048576]: + data=(bytes(range(256))*((size+255)//256))[:size] + digest,path=object_path(root,data) + for state in ['new','existing']: + check(f'binary-{size}-{state}',request(header(size)+data,True),expected(201,digest.encode())) + assert path.read_bytes()==data + assert list((root/'tmp').iterdir())==[] + with server('failures',5) as (root,connect,request): + data=b'unpublished-prefix'*200;digest,path=object_path(root,data) + with connect() as peer: + peer.sendall(header(len(data)+1)+data) + wait(lambda:len(list((root/'tmp').iterdir()))==1) + assert not path.exists() + peer.shutdown(socket.SHUT_WR) + check('early-eof',receive(peer),expected(400,b'Upload failed\n')) + assert not path.exists();assert list((root/'tmp').iterdir())==[] + path.parent.mkdir(exist_ok=True);path.write_bytes(b'X'+data[1:]) + check('corrupt-winner',request(header(len(data))+data),expected(400,b'Upload failed\n')) + assert path.read_bytes()==b'X'+data[1:] + check('corrupt-download',request(f'GET /_arboricx/bundle/hash/{digest} HTTP/1.1\r\n\r\n'.encode()),expected(404,b'Artifact unavailable\n')) + # Peer reset is a connection-local failure, not a server shutdown. + with connect() as peer: + peer.sendall(header(1048576)+b'partial') + peer.setsockopt(socket.SOL_SOCKET,socket.SO_LINGER,struct.pack('ii',1,0)) + check('healthy-after-reset',request(cases[0][1]),expected(200,b'OK\n')) + with server('downloads',6) as (root,connect,request): + data=b'authenticated binary\0\xff';digest,path=object_path(root,data) + path.parent.mkdir(exist_ok=True);path.write_bytes(data) + wire=f'GET /_arboricx/bundle/hash/{digest} HTTP/1.1\r\n\r\n'.encode() + check('download-binary',request(wire),expected(200,data,'application/vnd.arboricx.bundle')) + check('download-head',request(wire.replace(b'GET ',b'HEAD ')),expected(200,data,'application/vnd.arboricx.bundle',head=True)) + package=root/'packages'/'example';package.mkdir(parents=True) + (package/'v1').write_bytes(b'package bytes') + check('package-download',request(b'GET /_arboricx/package/example/v1 HTTP/1.1\r\n\r\n'),expected(200,b'package bytes','application/vnd.arboricx.package-release')) + for size in [4096,65536]: + data=bytes(range(256))*(size//256);digest,path=object_path(root,data) + path.parent.mkdir(exist_ok=True);path.write_bytes(data) + check(f'download-{size}',request(f'GET /_arboricx/bundle/hash/{digest} HTTP/1.1\r\n\r\n'.encode()),expected(200,data,'application/vnd.arboricx.bundle')) + # Trailing pipelined bytes never become a second request or uploaded data. + data=b'one request';digest,path=object_path(root,data) + check('surplus-body',request(header(len(data))+data+cases[0][1]),expected(201,digest.encode())) + assert path.read_bytes()==data + # Repeated handoff/join windows also exercise completed-ticket retention. + with server('lifecycle',128,window=4) as (root,connect,request): + for i in range(128): + assert request(cases[0][1])==expected(200,b'OK\n') + checks.append('128-connections-consumed-tickets') + with server('limited',2,name='limited') as (root,connect,request): + check('configured-upload-limit',request(header(5)+b'12345'),expected(413,b'Request body too large\n')) + assert not list((root/'objects').rglob('*.arboricx')) + check('configured-upload-boundary',request(header(4)+b'1234'),expected(201,hashlib.sha256(b'1234').hexdigest().encode())) + with server('logger',1,name='badLogger') as (root,connect,request): + data=b'published before logger failure';digest,path=object_path(root,data) + check('logger-failure',request(header(len(data))+data),expected(500,b'Internal Server Error\n')) + assert path.read_bytes()==data + # Two pending streams occupy exactly two admission slots. A third connection + # may enter the kernel backlog but cannot create another stage until a ticket + # is consumed. FIFO deliberately waits for the oldest, not any completed child. + with server('admission',3,window=2) as (root,connect,request): + with connect() as first,connect() as second,connect() as third: + first.sendall(header(1));second.sendall(header(1));third.sendall(header(1)) + wait(lambda:len(list((root/'tmp').iterdir()))==2) + second.sendall(b'b');second.shutdown(socket.SHUT_WR) + check('second-concurrent',receive(second),expected(201,hashlib.sha256(b'b').hexdigest().encode())) + wait(lambda:len(list((root/'tmp').iterdir()))==1) + third.settimeout(.3) + try: third.recv(1);raise AssertionError('third admitted before oldest completion') + except socket.timeout: pass + assert len(list((root/'tmp').iterdir()))==1 + first.sendall(b'a');first.shutdown(socket.SHUT_WR) + check('oldest-completes',receive(first),expected(201,hashlib.sha256(b'a').hexdigest().encode())) + third.settimeout(90);third.sendall(b'c');third.shutdown(socket.SHUT_WR) + check('queued-admitted',receive(third),expected(201,hashlib.sha256(b'c').hexdigest().encode())) + with server('cancelled',0,name='cancelledServer') as (root,connect,request): + with connect() as first,connect() as second: + first.sendall(header(1048576)+b'a'*4096) + second.sendall(header(1048576)+b'b'*4096) + wait(lambda:len(list((root/'tmp').iterdir()))==2) + request.cancel() + for peer in [first,second]: + try: assert receive(peer)==b'', 'cancellation rendered a response' + except ConnectionResetError: pass + checks.append('server-cancellation-cleans-active-streams') + def startup(label,name,values,flags=()): + argv=[str(runner.exe),'arboricx','run','--module',pin,'--name',name,'--store',str(runner.store),'--io','--no-result',*flags] + for i,value in enumerate(values): + path=directory/f'{label}-{i}.tree';path.write_bytes(value);argv+=['--arg-tree-file',str(path)] + result=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=30) + (directory/(label+'.startup.log')).write_bytes(result.stdout+result.stderr) + assert result.returncode!=0 and not result.stdout,(label,result.stdout,result.stderr) + checks.append(label) + for window in [0,257]: + root=directory/f'invalid-window-{window}' + startup(f'window-{window}','server',[text(str(root)),number(1),number(window)]) + assert not root.exists() + root=directory/'blocked-root';root.write_bytes(b'unchanged') + startup('blocked-root','server',[text(str(root)),number(1),number(1)]) + assert root.read_bytes()==b'unchanged' + root=directory/'grant-root' + startup('reject-v1-grants','server',[text(str(root)),number(1),number(1)],['--unsafe-io']) + assert not root.exists() + with socket.socket() as occupied: + occupied.bind(('127.0.0.1',0));occupied.listen(1) + from io_v2_tcp import endpoint + root=directory/'bind-failure' + startup('bind-failure','listen',[text(str(root)),endpoint(4,occupied.getsockname()[1])]) + assert not list(root.rglob('.tricu-stage-*')) + (directory/'report.json').write_text(json.dumps(dict(passed=checks,records=runner.records,processes=observations),indent=2)+'\n') + print(f'PASS {len(checks)} v2 HTTP controls; deployment unchanged') +if __name__=='__main__':main() diff --git a/test/io_v2_indexed.py b/test/io_v2_indexed.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +"""Independent framed/authenticated indexed-read and bounded-transfer controls.""" +import argparse +import hashlib +import json +from pathlib import Path +import struct +from source_order import Runner +from process_rss import run_guarded +from native_indexed_store import indexed_bytes, digest +from executable_object import INDEXED, indexed_manifest +from http_headers import number, result, tree_list + +ROOT = Path(__file__).resolve().parent.parent + +def text(s): + return tree_list([number(ord(c)) for c in s]) + +def problem(category, detail, progress): + return result(False, tree_list([text(category), text('indexed-tree/read'), text(detail), text(progress)])) + +def padded(size): + # Valid noncritical opaque section grows TRANSPORT, not the decoded DAG. + # This is not a large-DAG memory or compiler-capacity benchmark. + manifest = indexed_manifest(0) + nodes = struct.pack('>QIB', 1, 1, 0) + def section(kind, flags, offset, count): + return struct.pack('>IHHIQQI', kind, 1, flags, 0, offset, count, 0) + offset = 128 + len(manifest) + len(nodes) + return (b'ARBORICX' + struct.pack('>HHIQQ', 1, 0, 3, 0, 32) + + section(1, 1, 128, len(manifest)) + + section(2, 1, 128 + len(manifest), len(nodes)) + + section(99, 0, offset, size) + manifest + nodes + b'x' * size) + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', required=True, type=Path) + parser.add_argument('--zig-probe', type=Path) + args = parser.parse_args() + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + scratch = directory/'spool-λ' + scratch.mkdir() + runner = Runner(args.tricu, directory) + source = ROOT/'demos/io-v2/indexed-read.tri' + bundle = directory/'indexed-read.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + checks = [] + zig_observations = [] + def check(label, payload, size, limit, expected_digest, expected_reply, position): + path = directory/(label+'.bin') + path.write_bytes(payload) + values = [text(str(path)), text(str(scratch)), number(limit), number(size), text(expected_digest)] + argv = ['--io'] + zig_arguments = [] + for i, value in enumerate(values): + argument = directory/f'{label}-{i}.tree' + argument.write_bytes(value) + argv += ['--arg-tree-file', argument] + zig_arguments.append('tree-file:'+str(argument)) + expected = tree_list([expected_reply, number(position)]) + if args.zig_probe: + command = [str(args.zig_probe.resolve()),str(bundle),'probe',*zig_arguments] + output,peak = run_guarded(command,memory_mib=48,seconds=180) + (directory/(label+'-zig.log')).write_text(output) + assert bytes(map(int,output.strip())) == expected,label + zig_observations.append(dict(label=label,peak_kib=peak,command=command)) + else: + assert runner.invoke(label, pin, 'probe', argv) == expected, label + assert path.read_bytes() == payload, label + assert list(scratch.iterdir()) == [], label + checks.append(dict(label=label, input_bytes=len(payload), frame_bytes=size)) + for i, tree in enumerate([b'\0', b'\1\0', b'\2\1\0\0', number(42)]): + payload = indexed_bytes(tree) + check(f'tree-{i}', payload+b'next frame', len(payload), len(payload), digest(INDEXED, payload), result(True, tree), len(payload)) + payload = indexed_bytes(number(7)) + check('wrong-digest', payload, len(payload), len(payload), '0'*64, + problem('integrity-failed', 'CAS digest mismatch', 'unknown'), len(payload)) + check('truncated', payload[:-1], len(payload), len(payload), digest(INDEXED, payload), + problem('invalid-data', 'incomplete indexed frame', 'unknown'), len(payload)-1) + check('limit', payload, len(payload), len(payload)-1, digest(INDEXED, payload), + problem('invalid-bound', 'invalid snapshot size limit', 'none'), 0) + check('bad-digest', payload, len(payload), len(payload), 'A'*64, + problem('invalid-argument', '', 'none'), 0) + check('bad-bound-wire', payload, 2**63, 2**63, digest(INDEXED, payload), + problem('invalid-argument', '', 'none'), 0) + malformed = b'not a bundle' + check('malformed-authenticated', malformed, len(malformed), len(malformed), digest(INDEXED, malformed), + problem('invalid-data', 'invalid indexed frame' if args.zig_probe else 'section extends beyond bundle end', 'unknown'), len(malformed)) + check('malformed-unauthenticated', malformed, len(malformed), len(malformed), '0'*64, + problem('integrity-failed', 'CAS digest mismatch', 'unknown'), len(malformed)) + for size in [4096, 1048576, 16777216, 67108864]: + payload = padded(size) + check(f'transport-{size}', payload, len(payload), len(payload), digest(INDEXED, payload), result(True, b'\0'), len(payload)) + observations = [s for s in runner.records if s['label'].startswith('transport-')] + for sample in observations: + assert sample['peak_kib'] <= 48*1024, sample + assert sample['rts_process']['maximum_live_bytes'] <= 16*1024*1024, sample + files = ['lib/io-v2.tri', 'demos/io-v2/indexed-read.tri', 'test/io_v2_indexed.py', + 'test/source_order.py', 'test/rts_metrics.py', 'test/native_indexed_store.py', + 'test/executable_object.py', 'test/stable_bundle.py', 'src/ContentStore/Snapshot.hs', + 'src/ContentStore/Indexed.hs', 'src/Wire.hs', 'src/IOV2/Codec.hs', + 'src/IOV2/Resources.hs', 'src/IOV2/Driver.hs', 'src/IOV2/Protocol.hs', + 'bootstrap/build-stable.arboricx'] + if args.zig_probe: files += ['ext/zig/src/indexed.zig','ext/zig/src/io_v2_snapshot.zig','ext/zig/src/io_v2_file_host.zig'] + (directory/'report.json').write_text(json.dumps(dict(checks=checks, observations=observations, zig_observations=zig_observations, + zig_probe_sha256=hashlib.sha256(args.zig_probe.read_bytes()).hexdigest() if args.zig_probe else None, + measurement_notes='Opaque extension grows transport, not DAG. RSS is sampled: tiny runs can exit before representative samples; use the 64-MiB control for the transfer RSS guard. No concurrency or large-DAG capacity claim.', + source_hashes={p:hashlib.sha256((ROOT/p).read_bytes()).hexdigest() for p in files}), indent=2)+'\n') + print(f'{len(checks)} indexed-read controls passed') + +if __name__ == '__main__': + main() diff --git a/test/io_v2_indexed_write.py b/test/io_v2_indexed_write.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +"""Independent canonical bytes, winner verification and shared-DAG round trips.""" +import argparse +import hashlib +import json +from pathlib import Path +import struct +from source_order import Runner +from process_rss import run_guarded +from native_indexed_store import indexed_bytes, digest +from executable_object import INDEXED, indexed_manifest +from stable_bundle import assembled +from http_headers import number, result, tree_list + +ROOT = Path(__file__).resolve().parent.parent + +def text(s): + return tree_list([number(ord(c)) for c in s]) + +def info(payload): + return tree_list([number(len(payload)), text(digest(INDEXED, payload))]) + +def problem(category, operation, detail, progress): + return result(False, tree_list([text(category), text(operation), text(detail), text(progress)])) + +def dag(depth, fork): + records = [struct.pack('>IB', 1, 0)] + for i in range(1, depth+1): + payload = struct.pack('>BII', 2, i-1, i-1) if fork else struct.pack('>BI', 1, i-1) + records.append(struct.pack('>I', len(payload))+payload) + return assembled(indexed_manifest(depth), struct.pack('>Q', len(records))+b''.join(records)) + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + parser.add_argument('--zig-writer-probe', type=Path, help='private writer-only subset: no authenticated read/round-trip acceptance') + parser.add_argument('--zig-probe', type=Path, help='private complete indexed transport controls including authenticated reads') + args = parser.parse_args() + if args.zig_probe and args.zig_writer_probe: parser.error('select only one private probe mode') + private_probe = args.zig_probe or args.zig_writer_probe + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + source = ROOT/'demos/io-v2/indexed-write.tri' + bundle = directory/'writer.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + checks = [] + zig_observations = [] + def invoke(label, name, values, expected): + argv = ['--io'] + zig_arguments = [] + for i, value in enumerate(values): + path = directory/f'{label}-{i}.tree' + path.write_bytes(value) + argv += ['--arg-tree-file', path] + zig_arguments.append('tree-file:'+str(path)) + if private_probe: + command = [str(private_probe.resolve()),str(bundle),name,*zig_arguments] + output,peak = run_guarded(command,memory_mib=256,seconds=180) + (directory/(label+'-zig.log')).write_text(output) + assert bytes(map(int,output.strip())) == expected,label + zig_observations.append(dict(label=label,peak_kib=peak,command=command)) + else: + assert runner.invoke(label, pin, name, argv) == expected, label + checks.append(label) + for i, tree in enumerate([b'\0', b'\1\0', b'\2\1\0\0', number(42)]): + payload = indexed_bytes(tree) + expected = digest(INDEXED, payload) + invoke(f'hash-{i}', 'hash', [number(len(payload)), tree], result(True, info(payload))) + path = directory/f'write-{i}.bin' + invoke(f'write-{i}', 'write', [text(str(path)), number(len(payload)), text(expected), tree], result(True, info(payload))) + assert path.read_bytes() == payload + root = directory/f'publish-{i}' + root.mkdir() + for status in ['published', 'exists']: + invoke(f'publish-{i}-{status}', 'publish', [text(str(root)), number(len(payload)), tree], + result(True, tree_list([text(status),number(len(payload)),text(expected)]))) + assert sorted(p.name for p in root.iterdir()) == [expected] + assert (root/expected).read_bytes() == payload + (root/expected).write_bytes(b'X'+payload[1:]) + invoke(f'corrupt-{i}', 'publish', [text(str(root)), number(len(payload)), tree], + problem('integrity-failed','indexed-tree/verify','indexed frame differs from canonical tree','unknown')) + assert (root/expected).read_bytes() == b'X'+payload[1:] + assert sorted(p.name for p in root.iterdir()) == [expected] + (root/expected).write_bytes(payload+b'extra') + invoke(f'excess-{i}', 'publish', [text(str(root)), number(len(payload)), tree], + problem('invalid-data','indexed-file/verify','trailing bytes','unknown')) + assert (root/expected).read_bytes() == payload+b'extra' + path = directory/'preserve.bin' + path.write_bytes(b'untouched') + tree = b'\0' + payload = indexed_bytes(tree) + for label, limit, expected, answer in [ + ('wrong-hash',65536,'0'*64,problem('integrity-failed','indexed-tree/write','CAS digest mismatch','none')), + ('limit',0,digest(INDEXED,payload),problem('invalid-bound','indexed-tree/write','encoded tree exceeds limit','none')), + ('invalid-hash',65536,'A'*64,problem('invalid-argument','indexed-tree/write','','none'))]: + invoke(label, 'write', [text(str(path)),number(limit),text(expected),tree], answer) + assert path.read_bytes() == b'untouched' + # Raw input uses linear-depth stems, not an expanded exponentially shared + # tree. Expectations come from the independent wire assembler, not Zig. + for depth in [1000,10000]: + payload = dag(depth,False) + tree = b'\1'*depth+b'\0' + invoke(f'deep-hash-{depth}','hash',[number(len(payload)),tree],result(True,info(payload))) + output = directory/f'deep-{depth}.bin' + invoke(f'deep-write-{depth}','write',[text(str(output)),number(len(payload)),text(digest(INDEXED,payload)),tree],result(True,info(payload))) + assert output.read_bytes() == payload + original_bundle,original_pin = bundle,pin + bundle = directory/'transport.arboricx' + runner.run('compile-transport',['compile','--file',ROOT/'test/io-v2/indexed-transport.tri','--store',runner.store,'--output',bundle]) + pin = runner.imported(bundle,'import-transport') + payload = indexed_bytes(b'\0') + framed = directory/'framed.bin' + framed.write_bytes(payload+b'X') + from http_headers import pair + invoke('frame-tail','verifyTail',[text(str(framed)),number(len(payload)),b'\0'],result(True,pair(text('data'),tree_list([number(88)])))) + framed.write_bytes(payload[:-1]) + invoke('frame-truncated','verifyTail',[text(str(framed)),number(len(payload)),b'\0'],problem('integrity-failed','indexed-tree/verify','indexed frame differs from canonical tree','unknown')) + invoke('wrong-kind-inert-tree','wrongKind',[number(0)],problem('wrong-kind','indexed-tree/verify','','none')) + bundle,pin = original_bundle,original_pin + scratch = directory/'spool' + scratch.mkdir() + for depth, fork in ([] if args.zig_writer_probe else [(200,True),(1000,False),(10000,False),(100000,False)]): + payload = dag(depth,fork) + input_path = directory/f'dag-{depth}-{fork}.bin' + output = directory/f'dag-{depth}-{fork}-output.bin' + input_path.write_bytes(payload) + invoke(f'roundtrip-{depth}-{fork}', 'roundTrip', [text(str(input_path)),text(str(output)),text(str(scratch)), + number(len(payload)),number(len(payload)),text(digest(INDEXED,payload))], result(True,info(payload))) + assert output.read_bytes() == input_path.read_bytes() == payload + assert list(scratch.iterdir()) == [] + observations = [r for r in runner.records if r['label'].startswith('roundtrip-')] + for row in observations: + # DAG/table memory is expected to grow; these are regression guards, not + # constant-memory claims or a universal safe node-count budget. + assert row['peak_kib'] <= 256*1024, row + assert row['rts_process']['maximum_live_bytes'] <= 128*1024*1024, row + paths = ['test/io-v2/indexed-transport.tri','test/io_v2_indexed_write.py','test/native_indexed_store.py','test/executable_object.py', + 'test/stable_bundle.py','test/source_order.py','test/rts_metrics.py','lib/io-v2.tri', + 'demos/io-v2/indexed-write.tri','src/IOV2/Codec.hs','src/IOV2/Resources.hs','src/Wire.hs', + 'bootstrap/build-stable.arboricx'] + if private_probe: paths += ['ext/zig/src/indexed.zig','ext/zig/src/io_v2_file_host.zig'] + (directory/'report.json').write_text(json.dumps(dict(checks=checks, observations=observations, zig_observations=zig_observations, + scope="private-zig-writer-only" if args.zig_writer_probe else "private-zig-complete" if args.zig_probe else "haskell-complete", + zig_probe_sha256=hashlib.sha256(private_probe.read_bytes()).hexdigest() if private_probe else None, + source_hashes={p:hashlib.sha256((ROOT/p).read_bytes()).hexdigest() for p in paths}),indent=2)+'\n') + print(f'{len(checks)} independent indexed-writer checks passed'+(' (private Zig writer-only; authenticated read NOT tested)' if args.zig_writer_probe else '')) + +if __name__ == '__main__': + main() diff --git a/test/io_v2_lines.py b/test/io_v2_lines.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +"""Independent LF/UTF-8/limit oracles, fragmented pipes and interactive prompts.""" +import argparse +import json +import subprocess +import selectors +import time +from pathlib import Path +from source_order import Runner +from http_headers import number, result, tree_list, parse_tree_output +from io_v2_text import text, problem +ROOT=Path(__file__).resolve().parent.parent + +def event(s,ending): return tree_list([text('line'),text(s),text(ending)]) +def lines(data): + parts=data.split(b'\n') + return [event(p.decode('utf8'),'lf') for p in parts[:-1]] + ([event(parts[-1].decode('utf8'),'eof')] if parts[-1] else []) + +def main(): + p=argparse.ArgumentParser(description=__doc__);p.add_argument('tricu',type=Path);p.add_argument('--directory',type=Path,required=True) + a=p.parse_args();d=a.directory.resolve();d.mkdir(parents=True,exist_ok=False);runner=Runner(a.tricu,d);checks=[] + def compile(label,path): + bundle=d/(label+'.arboricx');runner.run(label+'-compile',['compile','--file',path,'--store',runner.store,'--output',bundle,'--compile-timeout','600'],seconds=660) + return runner.imported(bundle,label+'-import') + pin=compile('lines',ROOT/'demos/io-v2/lines.tri') + def invoke(label,name,values): + args=['--io'] + for i,value in enumerate(values): + f=d/f'{label}-{i}.tree';f.write_bytes(value);args+=['--arg-tree-file',f] + return runner.invoke(label,pin,name,args) + def collect(label,data,bound,expected=None): + path=d/(label+'.input');path.write_bytes(data) + actual=invoke(label,'collect',[text(str(path)),number(bound)]) + assert actual==(expected if expected is not None else result(True,tree_list(lines(data)))),label + checks.append(label) + for i,data in enumerate([b'',b'\n',b'\n\n',b'a',b'a\n',b'a\nb',b'a\n\n',b'a\r\nb\rc\n\0', '\ufeffλ😀e\u0301\n'.encode(),b'a'*4092+'😀'.encode()+b'\nnext\n',b'a'*4093+b'\n',b'a'*8190+b'\nend']): + collect(f'valid-{i}',data,max(map(len,data.split(b'\n')))) + for carry in [1,2,3]: + data=b'a'*(4093-carry)+'😀'.encode()+b'\nnext\n' + collect(f'carry-{carry}',data,4100) + collect('zero-empty-lines',b'\n\n',0) + over=problem('invalid-bound','text/read-line','line exceeds byte limit') + collect('overlong',b'ab\n',1,result(False,tree_list([b'\0',tree_list([text('read-line'),number(2),over])]))) + invalid=problem('invalid-data','utf8/decode','invalid UTF-8 leading byte at byte 0') + collect('later-invalid',b'ok\n\xff\n',20,result(False,tree_list([tree_list([event('ok','lf')]),tree_list([text('read-line'),number(1),invalid])]))) + partial=problem('invalid-data','utf8/decode','incomplete UTF-8 sequence at byte 0') + collect('incomplete-before-lf',b'\xe2\n',20,result(False,tree_list([b'\0',tree_list([text('read-line'),number(1),partial])]))) + collect('incomplete-at-eof',b'\xe2',20,result(False,tree_list([b'\0',tree_list([text('read-line'),number(1),partial])]))) + # Returning a first line never validates unrequested later buffered text. + path=d/'later-invalid.input' + assert invoke('first-only','first',[text(str(path)),number(20)])==result(True,event('ok','lf'));checks.append('first-only') + stale=problem('stale-handle','stream/read','') + assert invoke('closed-cursor','closed',[text(str(path)),number(20)])==result(False,tree_list([text('read-line'),number(0),stale])) + badlimit=problem('invalid-bound','text/limit','invalid text byte limit') + assert invoke('invalid-bound-before-read','closed',[text(str(path)),number(9223372036854775808)])==result(False,badlimit) + checks+=['closed-cursor','invalid-bound-before-read'] + def command(module,name,values=(),no_result=False): + cmd=[str(runner.exe),'arboricx','run','--module',module,'--name',name,'--store',str(runner.store),'--io','--format','ternary'] + for i,value in enumerate(values): + f=d/f'pipe-{name}-{i}.tree';f.write_bytes(value);cmd+=['--arg-tree-file',str(f)] + if no_result:cmd+=['--no-result'] + return cmd + data='A😀λ\n\ufeff\r\nlast'.encode() + for split in range(len(data)+1): + proc=subprocess.Popen(command(pin,'stdinLines',[number(100)]),stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + try: + proc.stdin.write(data[:split]);proc.stdin.flush();time.sleep(.02) + proc.stdin.write(data[split:]);proc.stdin.close();proc.stdin=None + out,err=proc.communicate(timeout=20) + assert proc.returncode==0,(split,err) + assert parse_tree_output(out.decode())==result(True,tree_list(lines(data))),split + finally: + if proc.poll() is None:proc.kill();proc.wait() + checks.append('every-pipe-split') + proc=subprocess.Popen(command(pin,'cancelled'),stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + try: + # Do not close stdin: cancellation, rather than EOF, must release the read. + proc.wait(timeout=10);out=proc.stdout.read();err=proc.stderr.read() + assert proc.returncode==0,err + assert parse_tree_output(out.decode())==tree_list([text('cancelled'),text('line-stop'),b'\0']) + finally: + if proc.poll() is None:proc.kill();proc.wait() + proc.stdin.close();proc.stdout.close();proc.stderr.close() + checks.append('cancel-blocked-input') + demo=compile('getLineAsync',ROOT/'demos/interactionTrees/getLineAsync.tri') + prompt=b'Please enter your first name: \n(this printed before you typed anything)\n(this second line also printed before you typed anything)\n' + for label,payload,wanted in [('name','λ😀\n'.encode(),'Hello, λ😀!\n'.encode()),('empty',b'\n',b'Hello, !\n'),('eof',b'',b'End of input.\n')]: + proc=subprocess.Popen(command(demo,'main',no_result=True),stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + try: + collected=b'';deadline=time.monotonic()+10 + with selectors.DefaultSelector() as sel: + sel.register(proc.stdout,selectors.EVENT_READ) + while len(collected)<len(prompt): + assert time.monotonic()<deadline,'prompt blocked on input' + if sel.select(.1): collected+=__import__('os').read(proc.stdout.fileno(),4096) + assert collected==prompt,collected + out,err=proc.communicate(payload,timeout=20) + assert proc.returncode==0 and out==wanted,(label,out,err) + finally: + if proc.poll() is None:proc.kill();proc.wait() + checks.append('interactive-'+label) + for label,wanted in [('greet',b'Hello, tricu\n'),('forkAwait',b'working: job1\nworking: job2\nGot job1-result and job2-result\n')]: + demo=compile(label,ROOT/f'demos/interactionTrees/{label}.tri') + run=subprocess.run(command(demo,'main',no_result=True),capture_output=True,timeout=30) + assert run.returncode==0 and run.stdout==wanted,(label,run.stdout,run.stderr) + checks.append(label) + with open('/dev/full','wb') as full: + failed=subprocess.run(command(demo,'main',no_result=True),stdout=full,stderr=subprocess.PIPE,timeout=20) + assert failed.returncode!=0,(label,failed.stderr) + checks.append(label+'-failed-output') + for count in [1024,16384]: + path=d/f'churn-{count}.input';path.write_bytes(b'x\n'*count) + assert invoke(f'churn-{count}','count',[text(str(path)),number(1)])==number(count) + row=runner.records[-1] + assert row['peak_kib']<128*1024 and row['rts_process']['maximum_live_bytes']<16*1024*1024,row + checks.append(f'churn-{count}') + (d/'report.json').write_text(json.dumps(dict(passed=checks,records=runner.records),indent=2)+'\n') + print(f'PASS {len(checks)} line/interactive controls (including every pipe split)') +if __name__=='__main__':main() diff --git a/test/io_v2_native_cli.py b/test/io_v2_native_cli.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""Actual production Zig boundary; private probes are not execution substitutes.""" +import argparse,json,hashlib,subprocess +from pathlib import Path +from source_order import Runner +from bundle_dag import merge_bundles +ROOT=Path(__file__).resolve().parent.parent + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu',type=Path);p.add_argument('zig',type=Path);p.add_argument('--directory',type=Path,required=True) + p.add_argument('--compiler-bundle',type=Path,help='also run an explicit v2 sourceBuildGraph worker through the production CLI') + args=p.parse_args();d=args.directory.resolve();d.mkdir(parents=True,exist_ok=False) + runner=Runner(args.tricu,d);bundle=d/'controls.arboricx' + runner.run('compile',['compile','--file',ROOT/'test/io-v2/native-cli.tri','--store',runner.store,'--output',bundle]) + checks=[] + def run(name,values=(),flags=(),expected=None,error=None): + selected=d/(name+'.arboricx') + merge_bundles(selected,[(bundle,[(name,'main')])],prune_unreachable=True) + command=[str(args.zig.resolve()),'--io','--type','number',*flags,str(selected),*values] + out=subprocess.run(command,capture_output=True,text=True,timeout=30) + (d/(str(len(checks))+'-'+name+'.log')).write_text(out.stdout+out.stderr) + if error: + assert out.returncode!=0 and error in out.stderr,(command,out.stdout,out.stderr) + else: + assert out.returncode==0 and out.stdout==str(expected)+'\n',(command,out.stdout,out.stderr) + checks.append(dict(name=name,command=command,returncode=out.returncode)) + for name,expected in [('pure',42),('unsupported',1),('inspect',1),('capabilities',1),('groups',1)]:run(name,['0'] if name in ['unsupported','inspect'] else [],expected=expected) + run('legacy',error='UnsupportedIOVersion') + run('legacy',flags=['--unsafe-io'],error='RetiredIOGrants') + path=d/'output-λ😀.bin';run('write',['text:'+str(path)],expected=3) + assert path.read_bytes()==bytes([0,255,65]) + denied=d/'must-not-exist' + run('write',['text:'+str(denied)],['--unsafe-io'],error='RetiredIOGrants') + assert not denied.exists() + run('divergent',['0'],['--unsafe-io'],error='RetiredIOGrants') + run('future',['0'],error='UnsupportedIOVersion') + run('failed',['0'],error='IOV2Failed');run('cancelled',['0'],error='IOV2Cancelled') + if args.compiler_bundle: + worker=d/'worker.arboricx' + merge_bundles(worker,[(args.compiler_bundle,[('sourceBuildGraph','main')])],prune_unreachable=True) + selected=d/'compile.arboricx' + merge_bundles(selected,[(bundle,[('compile','main')])],prune_unreachable=True) + workspace=d/'workspace-λ';workspace.mkdir() + (workspace/'dependency.tri').write_text('!export pick\npick [x | _] = x\npick _ = 0\n') + (workspace/'main.tri').write_text('!import dependency as D\n!export main\n!export accent\nmain = D.pick [42 7]\naccent = "é"\n') + store=d/'compiled';store.mkdir() + identity=hashlib.sha256(args.compiler_bundle.read_bytes()).hexdigest() + for label in ['cold','warm']: + command=[str(args.zig.resolve()),'--io','--type','string',str(selected),str(worker), + 'text:'+str(workspace),'text:'+str(store),'text:'+identity,'text:main'] + out=subprocess.run(command,capture_output=True,text=True,timeout=600) + (d/('compiler-'+label+'.log')).write_text(out.stdout+out.stderr) + manifest=out.stdout.strip() + assert out.returncode==0 and len(manifest)==64 and all(c in '0123456789abcdef' for c in manifest),(out.stdout,out.stderr) + from http_headers import number,tree_list + old_store=runner.store;runner.store=store + try: + assert runner.invoke('compiler-main-'+label,manifest,'main',[])==number(42) + assert runner.invoke('compiler-accent-'+label,manifest,'accent',[])==tree_list([number(101),number(769)]) + finally: runner.store=old_store + checks.append(dict(name='compiler-'+label,command=command,manifest=manifest,compiler_sha256=identity)) + assert not list(store.rglob('.tricu-*')) + (d/'report.json').write_text(json.dumps(dict(checks=checks,zig_sha256=hashlib.sha256(args.zig.read_bytes()).hexdigest()),indent=2)+'\n') + print(f'PASS {len(checks)} production Zig v2 and retired-v1 CLI controls') +if __name__=='__main__':main() diff --git a/test/io_v2_objects.py b/test/io_v2_objects.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Independent source-owned CAS bytes, exact winners and shared-tree transfer.""" +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +from source_order import Runner +from native_indexed_store import indexed_bytes, digest +from executable_object import INDEXED +from io_v2_indexed_write import dag +from http_headers import number, result, tree_list + +ROOT = Path(__file__).resolve().parent.parent + +def text(s): + return tree_list([number(ord(c)) for c in s]) + +def problem(category, op, detail, progress): + return result(False, tree_list([text(category),text(op),text(detail),text(progress)])) + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + source = ROOT/'demos/io-v2/objects.tri' + bundle = directory/'objects.arboricx' + runner.run('compile',['compile','--file',source,'--store',runner.store,'--output',bundle]) + module = runner.imported(bundle,'import') + checks = [] + def options(label, values): + argv = ['--io'] + for i,value in enumerate(values): + path = directory/f'{label}-{i}.tree' + path.write_bytes(value) + argv += ['--arg-tree-file',path] + return argv + def invoke(label,name,values,expected): + assert runner.invoke(label,module,name,options(label,values)) == expected,label + checks.append(label) + def clean(root): + assert not list(root.rglob('.tricu-*')),root + for i,tree in enumerate([b'\0',b'\1\0',number(42)]): + root = directory/f'root-{i}' + root.mkdir() + payload = indexed_bytes(tree) + pin = digest(INDEXED,payload) + path = root/'objects'/pin[:3]/pin + for label in ['new','existing']: + invoke(f'{label}-{i}','put',[text(str(root)),number(len(payload)),tree],result(True,text(pin))) + assert path.read_bytes() == payload + assert sorted(p.relative_to(root).as_posix() for p in root.rglob('*') if p.is_file()) == ['objects/'+pin[:3]+'/'+pin] + clean(root) + invoke(f'get-{i}','get',[text(str(root)),number(len(payload)),text(pin)],result(True,tree)) + path.write_bytes(b'X'+payload[1:]) + invoke(f'corrupt-put-{i}','put',[text(str(root)),number(len(payload)),tree], + problem('integrity-failed','indexed-tree/verify','indexed frame differs from canonical tree','unknown')) + invoke(f'corrupt-get-{i}','get',[text(str(root)),number(len(payload)),text(pin)], + problem('integrity-failed','indexed-tree/read','CAS digest mismatch','unknown')) + assert path.read_bytes() == b'X'+payload[1:] + clean(root) + path.write_bytes(payload+b'extra') + invoke(f'excess-{i}','put',[text(str(root)),number(len(payload)),tree], + problem('invalid-data','objects/verify','trailing bytes','unknown')) + assert path.read_bytes() == payload+b'extra' + clean(root) + absent = directory/'must-not-be-created' + for i,invalid in enumerate(['../outside','A'*64,'0'*63,'0'*65,'λ'*64,'\0'*64]): + invoke(f'invalid-{i}','get',[text(str(absent)),number(65536),text(invalid)], + problem('invalid-argument','objects/get','invalid CAS digest','none')) + for i, invalid in enumerate([b'\1\0', tree_list([number(48)[:-1]+b'\2\0\0']*64)]): + invoke(f'malformed-digest-{i}','get',[text(str(absent)),number(65536),invalid], + problem('invalid-argument','objects/get','invalid CAS digest','none')) + assert not absent.exists() + invoke('absent-root','putCategory',[text(str(absent)),number(65536),b'\0'],text('not-found')) + blocked = directory/'blocked'; blocked.mkdir(); (blocked/'objects').write_bytes(b'keep') + invoke('blocked','putCategory',[text(str(blocked)),number(65536),b'\0'],text('not-directory')) + assert (blocked/'objects').read_bytes() == b'keep' + limited = directory/'limited'; limited.mkdir() + invoke('limit','putCategory',[text(str(limited)),number(0),b'\0'],text('invalid-bound')) + assert list(limited.iterdir()) == [] + source_root = directory/'source'; target = directory/'target' + source_root.mkdir(); target.mkdir() + payload = dag(200,True) + pin = digest(INDEXED,payload) + path = source_root/'objects'/pin[:3]/pin + path.parent.mkdir(parents=True); path.write_bytes(payload) + invoke('shared-copy','copy',[text(str(source_root)),text(str(target)),number(len(payload)),text(pin)],result(True,text(pin))) + assert (target/'objects'/pin[:3]/pin).read_bytes() == payload + clean(source_root); clean(target) + # Two independent processes publish to one new namespace. Keep complete + # output/byte checks; this is a correctness race, not a capacity benchmark. + race = directory/'race'; race.mkdir() + tree = number(19); payload = indexed_bytes(tree); pin = digest(INDEXED,payload) + argv = options('race',[text(str(race)),number(len(payload)),tree]) + command = [str(args.tricu.resolve()),'arboricx','run','--module',module,'--name','put','--store',str(runner.store),'--format','ternary',*map(str,argv),'+RTS','-N1','-M256m','-RTS'] + workers = [subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.PIPE) for _ in range(2)] + try: + for worker in workers: + out,err = worker.communicate(timeout=120) + assert worker.returncode == 0,(out,err) + raw = re.search(rb'(?m)^([012]+)$',out)[1] + assert bytes(int(c)-48 for c in raw) == result(True,text(pin)),out + finally: + for worker in workers: + if worker.poll() is None: worker.kill() + worker.wait() + assert (race/'objects'/pin[:3]/pin).read_bytes() == payload + clean(race); checks.append('concurrent-publish') + files = ['lib/io-v2/objects.tri','lib/io-v2.tri','demos/io-v2/objects.tri','test/io_v2_objects.py', + 'test/native_indexed_store.py','test/io_v2_indexed_write.py','test/executable_object.py', + 'test/source_order.py','test/rts_metrics.py','src/IOV2/Resources.hs','src/IOV2/Descriptor.hs', + 'src/IOV2/Filesystem.hs','src/IOV2/Codec.hs','bootstrap/build-stable.arboricx'] + (directory/'report.json').write_text(json.dumps(dict(checks=checks,observations=runner.records, + source_hashes={p:hashlib.sha256((ROOT/p).read_bytes()).hexdigest() for p in files}),indent=2)+'\n') + print(f'{len(checks)} source CAS controls passed') + +if __name__ == '__main__': main() diff --git a/test/io_v2_positioned.py b/test/io_v2_positioned.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""Independent complete byte/result oracles for positioned file compositions.""" +import argparse +import hashlib +import json +from pathlib import Path +from source_order import Runner +from http_headers import number, pair, result, tree_list +from stable_bundle import byte_tree +ROOT = Path(__file__).resolve().parent.parent + +def text(value): + return tree_list([number(ord(c)) for c in value]) + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + source = ROOT/'demos/io-v2/positioned.tri' + bundle = directory/'positioned.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + checks = [] + + def invoke(label, name, values): + argv = ['--io'] + for index, value in enumerate(values): + path = directory/f'{label}-{index}.tree' + path.write_bytes(value) + argv += ['--arg-tree-file', path] + return runner.invoke(label, pin, name, argv) + + for index, (original, offset, patch, length, exists) in enumerate([ + (b'abcdefgh', 2, bytes([0,255,13,10]), 8, True), + (b'', 5, b'X', 12, False), + (b'0123456789', 3, b'ABCDE', 4, True), + (b'unchanged prefix', 0, b'', 16, True), + (b'discarded', 2, b'xy', 0, True), + (bytes(range(256))*4096, 524288, bytes(range(128)), 1048585, True), + ]): + path = directory/f'patch-{index}-λ.bin' + if exists: + path.write_bytes(original) + wanted = bytearray(original) + if patch: + end = offset+len(patch) + if end > len(wanted): + wanted.extend(bytes(end-len(wanted))) + wanted[offset:end] = patch + if length > len(wanted): + wanted.extend(bytes(length-len(wanted))) + del wanted[length:] + prefix = pair(text('data'), byte_tree(wanted[:4096])) if wanted else pair(text('eof'), b'\0') + expected = tree_list([number(n) for n in + [len(original), offset, offset+len(patch), offset+len(patch), length]]+[prefix]) + actual = invoke(f'patch-{index}', 'patch', [text(str(path)), number(offset), byte_tree(patch), number(length)]) + assert actual == expected, index + assert path.read_bytes() == wanted, index + checks.append(dict(label=f'patch-{index}', sha256=hashlib.sha256(wanted).hexdigest())) + for index, original in enumerate([None, b'', b'prior\x00\xff', bytes(range(256))*4096]): + path = directory/f'append-{index}.bin' + if original is not None: + path.write_bytes(original) + body = bytes(range(256))*16 + before = original or b'' + actual = invoke(f'append-{index}', 'append', [text(str(path)), byte_tree(body)]) + assert actual == tree_list([number(len(before)),number(len(before)+len(body))]), index + assert path.read_bytes() == before+body, index + checks.append(dict(label=f'append-{index}', sha256=hashlib.sha256(before+body).hexdigest())) + # Exercise the wire's wide offset without materializing a multi-GiB value: + # write one sparse byte, shrink, and compare the entire resulting file. + path = directory/'wide.bin' + actual = invoke('wide-offset', 'patch', [text(str(path)),number(4294967296),byte_tree(b'X'),number(1)]) + assert actual == tree_list([number(n) for n in [0,4294967296,4294967297,4294967297,1]]+ + [pair(text('data'),byte_tree(b'\0'))]) + assert path.read_bytes() == b'\0' + checks.append(dict(label='wide-offset')) + path = directory/'guard.bin' + path.write_bytes(b'keep me') + def problem(category, operation, detail=''): + return result(False, tree_list([text(v) for v in [category, operation, detail, 'none']])) + expected = tree_list([problem('unsupported','file/'+op,'operation unavailable in this file mode') + for op in ['seek','position','resize']]+[problem('wrong-kind','stream/read')]) + assert invoke('append-guards', 'appendGuards', [text(str(path))]) == expected + assert path.read_bytes() == b'keep me' + checks.append(dict(label='append-guards')) + for index, value in enumerate([number(9223372036854775808), pair(b'\0',b'\0'), b'\1\0']): + assert invoke(f'offset-{index}', 'badOffset', [text(str(path)),value]) == problem('invalid-argument','file/seek') + assert path.read_bytes() == b'keep me' + checks.append(dict(label=f'offset-{index}')) + observations = [row for row in runner.records if row['label'].startswith(('patch-','append-'))] + for row in observations: + assert row['peak_kib'] <= 128*1024, row + assert row['rts_process']['maximum_live_bytes'] <= 16*1024*1024, row + paths = [source, Path(__file__).resolve(), ROOT/'lib/io-v2.tri', ROOT/'src/IOV2/Resources.hs', + ROOT/'src/IOV2/Driver.hs', ROOT/'src/IOV2/Protocol.hs', ROOT/'test/source_order.py', + ROOT/'test/rts_metrics.py', ROOT/'bootstrap/build-stable.arboricx'] + (directory/'report.json').write_text(json.dumps(dict(checks=checks, observations=observations, + sources={str(p.relative_to(ROOT)):hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}), indent=2)+'\n') + print(f'PASS: {len(checks)} independent positioned-file checks') + +if __name__ == '__main__': + main() diff --git a/test/io_v2_process.py b/test/io_v2_process.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +"""Independent actual-command, binary pipeline and normal-scope-exit controls.""" +import argparse +import json +import shutil +from pathlib import Path +from source_order import Runner +from http_headers import number, tree_list, result +from io_v2_objects import text + +ROOT = Path(__file__).resolve().parent.parent + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu',type=Path); p.add_argument('--directory',type=Path,required=True) + a=p.parse_args(); directory=a.directory.resolve(); directory.mkdir(parents=True,exist_ok=False) + runner=Runner(a.tricu,directory); bundle=directory/'process.arboricx'; checks=[] + runner.run('compile',['compile','--file',ROOT/'demos/io-v2/process.tri','--store',runner.store,'--output',bundle,'--compile-timeout','600'],seconds=660) + pin=runner.imported(bundle,'import') + inherit=tree_list([text('inherit')]) + def invoke(label,name,args): + path=directory/(label+'.tree');path.write_bytes(tree_list(args)) + return runner.invoke(label,pin,name,['--io','--arg-tree-file',path]) + cat=shutil.which('cat');shell=shutil.which('sh');assert cat and shell + source=directory/'input';output=directory/'output';errors=directory/'errors' + def pipeline(label,exe,argv,cwd,env,wanted,err=b'',code=0): + actual=invoke(label,'pipeline',[text(exe),tree_list([text(x) for x in argv]),cwd,env,number(20),text(str(source)),text(str(output)),text(str(errors))]) + assert actual==tree_list([text('exit'),number(code)]),(label,actual.hex()) + assert output.read_bytes()==wanted,label + assert errors.read_bytes()==err,label + checks.append(label) + for size in [0,1,4095,4096,4097,65536,1048576]: + data=bytes(i%256 for i in range(size));source.write_bytes(data) + pipeline(f'binary-{size}',cat,[],inherit,inherit,data) + tee=shutil.which('tee');assert tee + data=bytes(range(256))*4096;source.write_bytes(data) + pipeline('concurrent-stdout-stderr',tee,['/dev/stderr'],inherit,inherit,data,data) + source.write_bytes(b'') + pipeline('literal-argv-env-cwd',shell,['-c','printf "%s|%s|%s" "$1" "$VALUE" "$PWD"; printf diagnostic >&2; exit 7','test','$(not-a-command); *'],tree_list([text('at'),text(str(directory))]),tree_list([text('replace'),tree_list([tree_list([text('VALUE'),text('literal')])])]),('$(not-a-command); *|literal|'+str(directory)).encode(),b'diagnostic',7) + marker=directory/'joined' + actual=invoke('normal-exit','normalExit',[text(shell),tree_list([text('-c'),text('i=0; while [ "$i" -lt 10000 ]; do i=$((i+1)); done; printf finished > "$1"; exit 3'),text('test'),text(str(marker))])]) + assert actual==number(42) and marker.read_bytes()==b'finished' + checks.append('normal-exit-reaps-without-implying-zero-status') + for label,exe,args,cwd,env,grace in [ + ('empty-executable','',[],inherit,inherit,0), + ('duplicate-environment',cat,[],inherit,tree_list([text('replace'),tree_list([tree_list([text('X'),text('a')]),tree_list([text('X'),text('b')])])]),0), + ('excess-argv',cat,['a']*257,inherit,inherit,0), + ('excess-text',cat,['a'*4097],inherit,inherit,0), + ('excess-total',cat,['a'*4096]*16,inherit,inherit,0), + ('excess-grace',cat,[],inherit,inherit,9223372036854775808)]: + actual=invoke(label,'raw',[text(exe),tree_list([text(x) for x in args]),cwd,env,number(grace)]) + assert actual==result(False,tree_list([text('invalid-argument'),text('process/spawn'),b'\0',text('none')])),label + checks.append(label) + for row in runner.records: + if row.get('label') in ('binary-1048576','concurrent-stdout-stderr'): + # Runtime process observations, not subprocess heap or capacity promises. + assert row['rts_process']['maximum_live_bytes'] < 16*1024*1024,row + assert row['peak_kib'] < 128*1024,row + (directory/'report.json').write_text(json.dumps(dict(passed=checks,records=runner.records),indent=2)+'\n') + print(f'PASS {len(checks)} process source controls') +if __name__=='__main__':main() diff --git a/test/io_v2_protocol.py b/test/io_v2_protocol.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""Independent source oracles for v2 discovery, inspection and unsupported calls.""" +import argparse +import hashlib +import json +from pathlib import Path +from source_order import Runner +from stable_bundle import byte_tree +from http_headers import tree_list, number, result + +ROOT = Path(__file__).resolve().parent.parent + +def text(value): + return byte_tree(value.encode()) + +def strings(values): + return tree_list([text(v) for v in values]) + +def expected_capabilities(): + def row(key, value): + return tree_list([text(key), value]) + groups = [('runtime', ['capabilities', 'inspect']), ('standard', ['input', 'output', 'error']), ('file', ['open-reader', 'open-writer', 'open-appender', 'open-update', 'seek', 'position', 'size', 'resize', 'sync']), + ('path', ['stat', 'lstat', 'remove-file', 'rename']), ('directory', ['create', 'remove', 'open', 'next', 'sync']), ('tcp', ['listen', 'connect', 'accept', 'local-address', 'peer-address', 'shutdown-write']), ('resolver', ['resolve']), ('indexed-tree', ['read', 'hash', 'write', 'verify']), ('utf8', ['encode', 'decode']), ('stream', ['read', 'write']), ('resource', ['close']), + ('stage', ['open', 'seal', 'publish']), ('digest', ['open', 'update', 'finish']), ('clock', ['monotonic', 'sleep']), ('scheduler', ['yield']), ('process', ['spawn', 'working-directory', 'environment-variable'])] + return tree_list([ + row('schema', number(1)), row('io-version', number(2)), + row('backend', text('haskell-native')), row('authority', text('os-process')), + row('core', strings(['pure', 'bind', 'fail', 'scope', 'current-scope', 'spawn', 'join', 'discard', 'cancel', 'cancelled'])), + row('groups', tree_list([tree_list([text(g), number(1), strings(ops)]) for g, ops in groups])), + row('limits', tree_list([row('chunk-bytes', number(4096)), row('transfer-handles', number(256)), row('file-offset-max', number(9223372036854775807)), row('directory-name-scalars', number(4096)), row('tcp-backlog', number(4096)), row('resolver-host-characters', number(253)), row('resolver-addresses', number(64)), row('indexed-sections', number(64)), row('indexed-manifest-bytes', number(65536)), row('utf8-encode-scalars', number(1024)), row('sleep-milliseconds-max', number(9223372036854775807)), row('process-arguments', number(256)), row('process-environment-entries', number(256)), row('process-text-scalars', number(4096)), row('process-total-scalars', number(65536)), row('process-grace-milliseconds-max', number(9223372036854775807))])), + row('tcp-address-families', strings(['ipv4', 'ipv6'])), + row('digest-algorithms', strings(['sha256'])), + row('restrictions', strings(['action-boundary-cancellation', 'no-hard-deadlines', + 'stage-same-filesystem', 'stage-no-copy-fallback', 'stage-private-namespace', 'publish-new-requires-hard-links', 'no-crash-recovery', 'borrowed-standard-streams', 'standard-stream-leases-per-run', 'file-single-cursor-lease', 'append-no-position-or-resize', 'file-host-handle-locking', 'directory-unsorted-non-snapshot', 'filesystem-host-encoding', 'directory-failure-poisons-cursor', 'tcp-numeric-addresses', 'tcp-ipv6-only-listeners', 'tcp-direction-leases', 'tcp-failure-poisons-direction', 'resolver-host-order-and-buffering', 'resolver-may-delay-cancellation', 'indexed-authenticated-snapshot', 'indexed-caller-frame-and-disk-limit', 'indexed-dag-resident', 'indexed-writer-preflight', 'indexed-budget-not-heap-quota', 'sync-host-filesystem-guarantees', 'utf8-strict-scalars', 'clock-host-monotonic-word64', 'sleep-may-run-late', 'yield-no-ordering-guarantee', 'process-direct-child-only', 'process-normal-exit-joins', 'process-explicit-pipe-draining']))]) + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + bundle = directory/'protocol.arboricx' + source = ROOT/'test/io-v2/protocol.tri' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + victim = directory/'must-remain' + victim.write_bytes(b'not truncated\x00\xff') + path = directory/'path.tree' + path.write_bytes(byte_tree(str(victim).encode())) + seed = directory/'seed.tree' + seed.write_bytes(number(7)) + cases = { + 'capabilities': (result(True, expected_capabilities()), []), + 'handledCapabilities': (result(True, expected_capabilities()), []), + 'inspectWriter': (result(True, tree_list([text('facility'), text('file'), number(1), + text('open-writer'), text('available')])), [path]), + 'inspectLazyBind': (result(True, strings(['core', 'bind'])), [seed]), + 'inspectLazyScope': (result(True, strings(['core', 'scope'])), [seed]), + 'inspectFuture': (result(True, tree_list([text('facility'), text('future-tcp'), number(1), + text('connect'), text('unsupported')])), [seed]), + 'unsupported': (result(False, strings(['unsupported', 'future-tcp/connect', 'group revision 1', 'none'])), [seed])} + for name, (expected, inputs) in cases.items(): + argv = ['--io'] + for arg in inputs: + argv += ['--arg-tree-file', arg] + assert runner.invoke(name, pin, name, argv) == expected, name + assert victim.read_bytes() == b'not truncated\x00\xff' + for name in ['invalidHeader', 'retired']: + try: + runner.invoke(name, pin, name, ['--io']) + except Exception as error: + assert 'IO v2 task failed' in str(getattr(error, 'output', error)), error + else: + raise AssertionError('malformed protocol executed: ' + name) + observations = runner.records + for row in observations: + stats = row['rts_process'] + assert all(k in stats for k in ['copied_bytes', 'mutator_cpu_seconds', 'gc_cpu_seconds', + 'productivity_cpu_percent']), row + paths = [source, ROOT/'lib/io-v2.tri', ROOT/'src/IOV2/Protocol.hs', ROOT/'src/IOV2/Driver.hs', + ROOT/'test/source_order.py', ROOT/'test/rts_metrics.py', Path(__file__).resolve()] + report = {'checks': list(cases)+['invalidHeader', 'retired'], 'observations': observations, + 'sources': {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}} + (directory/'report.json').write_text(json.dumps(report, indent=2)+'\n') + print('PASS: 9 independent capability/inspection/protocol checks') + +if __name__ == '__main__': + main() diff --git a/test/io_v2_provenance.py b/test/io_v2_provenance.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""Real v2 recipe capture, producer removal and fresh explicitly trusted replay. +Supply a retained-built candidate sourceBuildGraph bundle; never change defaults. +""" +import argparse +import json +import os +from pathlib import Path +import re +import shutil +import subprocess +import time +from provenance import digest, put, object_path, snapshot, RECEIPT, SOURCE +from source_order import Runner +from http_headers import number + + +def main(): + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu',type=Path) + parser.add_argument('--compiler-bundle',type=Path,required=True) + parser.add_argument('--directory',type=Path,required=True) + args=parser.parse_args() + root=args.directory.resolve(); root.mkdir(parents=True,exist_ok=False) + producer=root/'producer'; producer.mkdir() + runner=Runner(args.tricu,producer) + compiler=runner.imported(args.compiler_bundle.resolve(),'compiler') + workspace=producer/'workspace'; workspace.mkdir() + (workspace/'tricu.workspace').write_text('tricu.workspace.v1\n') + (workspace/'dependency.tri').write_text('!export first\nfirst [x | _] = x\nfirst _ = 0\n') + (workspace/'main.tri').write_text('!import dependency as D\n!export main\n-- raw Unicode: λ 😀 é\nmain = D.first [42 7]\n') + runner.run('compile',['compile','--file',workspace/'main.tri','--toolchain',compiler, + '--toolchain-io-version','2','--tool-store',runner.store,'--store',runner.store, + '--output',producer/'program.arboricx']) + records=[]; checks=[] + def run(label,command,success=True): + started=time.monotonic() + result=subprocess.run([str(args.tricu.resolve()),*map(str,command),'+RTS','-N1','-M8192m','-T','-s','-RTS'], + cwd=root,env=dict(os.environ,tricu_datadir=str(root/'NO-PACKAGED-COMPILER')), + capture_output=True,text=True,timeout=180) + records.append(dict(label=label,command=list(map(str,command)),seconds=time.monotonic()-started, + code=result.returncode,stdout=result.stdout,stderr=result.stderr)) + (root/(label+'.log')).write_text(result.stdout+result.stderr) + assert (result.returncode==0)==success,(label,result.stdout,result.stderr) + return result.stdout+result.stderr + pack=root/'pack' + output=run('capture',['provenance','capture','--file',workspace/'main.tri','--store',runner.store, + '--tool-store',runner.store,'--toolchain',compiler,'--toolchain-io-version','2','--output',pack]) + pin=re.search(r'Retained build inputs: ([0-9a-f]{64})',output)[1] + data=(pack/'build-inputs').read_bytes() + assert pin==digest('arboricx.build-inputs.v2',data) + assert b'compiler-io\t2\n' in data + compilation_records=runner.records + shutil.rmtree(producer) + before=snapshot(pack) + assert 'Compiler IO version 2' in run('verify',['provenance','verify','--pack',pack,'--pin',pin]) + for label,trust,version in [('unauthorized', '0'*64,'2'),('wrong-version',compiler,'1')]: + run(label,['provenance','rebuild','--pack',pack,'--pin',pin,'--trust-compiler',trust, + '--trust-io-version',version,'--output',root/label],False) + assert not (root/label).exists() + replay=root/'replayed' + response=run('rebuild',['provenance','rebuild','--pack',pack,'--pin',pin,'--trust-compiler',compiler, + '--trust-io-version','2','--output',replay]) + assert 'Reproduced native module and receipt exactly' in response + assert snapshot(pack)==before + checks.append('offline-exact-module-and-receipt') + module=(replay/'application/cache/source-build/main').read_text().split('\t')[2] + # Evaluate the replay's actual stored executable independently, not a copied + # packaged output or an expected receipt from the producer. + reader=Runner(args.tricu,root/'execution'); reader.root.mkdir() + reader.store=replay/'application' + assert reader.invoke('value',module,'main',[])==number(42) + checks.append('independent-replayed-value') + # Retain valid but false producer claims and a matching poisoned cache. Verify + # authenticates bytes only; fresh replay must detect the executable mismatch. + forged=root/'forged'; shutil.copytree(pack,forged) + store=forged/'store' + rows=data.decode().splitlines() + old_receipt=next(row.split('\t')[1] for row in rows if row.startswith('receipt\t')) + receipt_rows=object_path(store,old_receipt).read_text().splitlines() + src=put(store,SOURCE,b'!export main\nmain = 99\n') + receipt_bytes=('\n'.join([RECEIPT,'source\t'+src,*receipt_rows[2:5]])+'\n').encode() + receipt=put(store,RECEIPT,receipt_bytes) + descriptor=('\n'.join([rows[0],'entry\tmain','receipt\t'+receipt,rows[3],rows[4], + 'unit\tmain\t'+receipt])+'\n').encode() + (forged/'build-inputs').write_bytes(descriptor) + forged_pin=digest('arboricx.build-inputs.v2',descriptor) + key_bytes=('\n'.join(['arboricx.source-build-key.v1','source\t'+src,*receipt_rows[2:4]])+'\n').encode() + key=digest('arboricx.source-build-key.v1',key_bytes) + cache=store/'cache/source-build/main'; cache.parent.mkdir(parents=True) + cache.write_text('\t'.join(['arboricx.source-build-cache.v1',key,receipt_rows[4].split('\t')[1],receipt])) + run('verify-false-claim',['provenance','verify','--pack',forged,'--pin',forged_pin]) + response=run('reject-false-claim',['provenance','rebuild','--pack',forged,'--pin',forged_pin, + '--trust-compiler',compiler,'--trust-io-version','2','--output',root/'mismatch'],False) + assert 'Rebuild mismatch' in response and 'tree different' in response + checks.append('authenticated-false-claim-and-cache-poison-rejected') + (root/'report.json').write_text(json.dumps(dict(checks=checks,compiler=compiler,io_version=2, + producer_removed=True,pack_unchanged=True,records=records,compilation=compilation_records),indent=2)+'\n') + print('PASS v2 offline replay, explicit authority, exact receipts, independent output and false-claim/cache controls') + + +if __name__=='__main__': main() diff --git a/test/io_v2_publication.py b/test/io_v2_publication.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Source-owned exact length/digest policy over scoped staged writers and hashes.""" +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +from source_order import Runner, field +from stable_bundle import byte_tree +from http_headers import number, result, tree_list + +ROOT = Path(__file__).resolve().parent.parent + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + args.directory.mkdir(parents=True, exist_ok=False) + directory = args.directory.resolve() + runner = Runner(args.tricu, directory) + bundle = directory/'publication.arboricx' + runner.run('compile', ['compile', '--file', ROOT/'demos/io-v2/publish.tri', + '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + checks = [] + def invoke(label, name, values): + argv = ['--io'] + for index, value in enumerate(values): + arg = directory/f'{label}-{index}.tree' + arg.write_bytes(number(value) if isinstance(value, int) else tree_list([number(ord(c)) for c in str(value)])) + argv += ['--arg-tree-file', arg] + return runner.invoke(label, pin, name, argv) + callback = directory/'callback'; callback.mkdir() + assert invoke('rejected-destination', 'rejectedDestination', [callback]) == result(False, byte_tree(b'Destination rejected')) + failure = tree_list([byte_tree(b'invalid-response'), byte_tree(b'artifact-stream'), + byte_tree(b'destination'), byte_tree(b'Expected destination Result'), b'\0']) + assert invoke('malformed-destination', 'malformedDestination', [callback]) == tree_list([byte_tree(b'failed'), failure, b'\0']) + assert list(callback.iterdir()) == [] + checks += ['rejected-destination', 'malformed-destination'] + for size in (0, 1, 4095, 4096, 4097, 65536, 1048576): + body = (bytes(range(256))*((size+255)//256))[:size] + source = directory/f'input-{size}' + source.write_bytes(body) + target = directory/f'objects-{size}' + target.mkdir() + digest = hashlib.sha256(body).hexdigest() + for state in ('new', 'existing'): + label = f'{state}-{size}' + actual = invoke(label, 'publish', [source, target, size]) + assert actual == result(True, byte_tree(digest.encode())), label + assert (target/digest).read_bytes() == body, label + assert (target/digest).stat().st_mode & 0o077 == 0, label + assert sorted(p.name for p in target.iterdir()) == [digest], label + checks.append(label) + source, target = directory/'source-λ-树', directory/'objects-λ-树' + source.write_bytes(b'\0\xffbinary') + target.mkdir() + digest = hashlib.sha256(source.read_bytes()).hexdigest() + assert invoke('unicode-paths', 'publish', [source, target, 8]) == result(True, byte_tree(digest.encode())) + assert (target/digest).read_bytes() == source.read_bytes() + assert sorted(p.name for p in target.iterdir()) == [digest] + checks.append('unicode-paths') + source = directory/'negative-input' + source.write_bytes(b'abc') + digest = hashlib.sha256(b'abc').hexdigest() + for label, count, winner, message in ( + ('short', 4, None, 'Incomplete artifact stream'), + ('excess', 2, None, 'Artifact exceeds declared length'), + ('bad-winner', 3, b'xyz', 'Existing artifact digest mismatch'), + ('short-winner', 3, b'ab', 'Existing artifact length mismatch'), + ('long-winner', 3, b'abcd', 'Existing artifact length mismatch')): + target = directory/label + target.mkdir() + if winner is not None: + (target/digest).write_bytes(winner) + actual = invoke(label, 'publish', [source, target, count]) + assert actual == result(False, byte_tree(message.encode())), label + assert sorted(p.name for p in target.iterdir()) == ([] if winner is None else [digest]), label + if winner is not None: + assert (target/digest).read_bytes() == winner, label + checks.append(label) + for name, message in ( + ('earlySuccess', 'Incomplete artifact stream'), + ('ignoresWriteFailure', 'Artifact exceeds declared length'), + ('emptyChunk', 'Empty artifact chunk'), + ('overrideWrite', 'Incomplete artifact stream'), + ('failedAfterFull', 'Producer failed')): + target = directory/name + target.mkdir() + actual = invoke(name, name, [target]) + assert actual == result(False, byte_tree(message.encode())), name + assert list(target.iterdir()) == [], name + checks.append(name) + target = directory/'invalidChunk' + target.mkdir() + actual = invoke('invalidChunk', 'invalidChunk', [target]) + assert field(actual, 0) == b'\0' + assert field(actual, 1, 0, 0) == byte_tree(b'write-all') + assert field(actual, 1, 0, 1, 0) == number(0) + assert field(actual, 1, 0, 1, 1, 0, 0) == byte_tree(b'invalid-argument') + assert list(target.iterdir()) == [] + checks.append('invalidChunk') + for name, expected in [('cancelled', 'IO v2 task cancelled')]: + target = directory/name + target.mkdir() + try: + invoke(name, name, [target]) + except subprocess.CalledProcessError as error: + assert expected in error.output, error.output + else: + raise AssertionError(name+' resumed normally') + assert list(target.iterdir()) == [] + assert not (directory/'escape').exists() + checks.append(name) + target = directory/'badDigest' + target.mkdir() + expected = tree_list([byte_tree(b'failed'), tree_list([ + byte_tree(b'invalid-response'), byte_tree(b'artifact-stream'), byte_tree(b'digest'), + byte_tree(b'Invalid digest reply'), b'\0']), b'\0']) + assert invoke('badDigest', 'badDigest', [target]) == expected + assert list(target.iterdir()) == [] + assert not (directory/'escape').exists() + checks.append('badDigest') + for name in ('nestedProducer', 'forkedProducer'): + target = directory/name + target.mkdir() + expected = tree_list([byte_tree(b'failed'), tree_list([ + byte_tree(b'producer-boundary'), byte_tree(b'artifact-stream'), byte_tree(b'publish'), + byte_tree(b'Producer must be sequential'), + tree_list([byte_tree(b'outer'), byte_tree(b'inner')])]), b'\0']) + assert invoke(name, name, [target]) == expected, name + assert list(target.iterdir()) == [], name + checks.append(name) + observations = [dict(row) for row in runner.records if row['label'].endswith(('-65536', '-1048576'))] + for row in observations: + log = (directory/(row['label']+'.log')).read_text() + matched = re.search(r'([0-9,]+) bytes maximum residency', log) + assert matched, row + row['maximum_live_bytes'] = int(matched[1].replace(',', '')) + assert row['maximum_live_bytes'] <= 16*1024*1024, row + assert row['peak_kib'] <= 128*1024, row + paths = [Path(__file__), ROOT/'src/IOV2/Resources.hs', ROOT/'src/IOV2/Staging.hs', + ROOT/'src/IOV2/Digest.hs', ROOT/'src/IOV2/Driver.hs', ROOT/'src/IOV2/Protocol.hs', + ROOT/'test/source_order.py', ROOT/'test/rts_metrics.py', ROOT/'lib/io-v2.tri', + ROOT/'lib/io-v2/artifacts.tri', ROOT/'lib/handlers.tri', ROOT/'demos/io-v2/publish.tri'] + report = {'checks': checks, 'observations': observations, 'sources': { + str(path.resolve().relative_to(ROOT)): hashlib.sha256(path.read_bytes()).hexdigest() for path in paths}} + (directory/'report.json').write_text(json.dumps(report, indent=2)+'\n') + print(f'PASS: {len(checks)} publication and independent policy checks') + +if __name__ == '__main__': + main() diff --git a/test/io_v2_streams.py b/test/io_v2_streams.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""Real binary pipes/redirects, ownership errors, and bounded native residency.""" +import argparse +import concurrent.futures +import hashlib +import json +import os +from pathlib import Path +import subprocess +import time +from source_order import Runner +from process_rss import ChildRSS +from rts_metrics import parse_rts_stats +from http_headers import result, tree_list +from stable_bundle import byte_tree + +ROOT = Path(__file__).resolve().parent.parent + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, directory) + source = ROOT/'demos/io-v2/streams.tri' + bundle = directory/'streams.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + checks, observations = [], [] + + def command(name, stats=True): + return [str(runner.exe), 'arboricx', 'run', '--module', pin, '--name', name, + '--store', str(runner.store), '--io', '--no-result', + '+RTS', '-N1', '-M128m', *(['-T', '-s'] if stats else []), '-RTS'] + + def invoke(label, name, body, redirected=False): + argv = command(name) + started = time.monotonic() + input_path, output_path = directory/(label+'.in'), directory/(label+'.out') + input_path.write_bytes(body) + with input_path.open('rb') as src, output_path.open('wb') as dst: + proc = subprocess.Popen(argv, stdin=src if redirected else subprocess.PIPE, + stdout=dst if redirected else subprocess.PIPE, stderr=subprocess.PIPE) + peak = None + try: + rss = ChildRSS(proc) + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(proc.communicate, None if redirected else body, timeout=120) + while not future.done(): + current = rss.sample() + if current is not None: + peak = max(peak or 0, current) + if peak > 128*1024: + proc.kill() + raise AssertionError('RSS bound exceeded') + time.sleep(.01) + stdout, stderr = future.result() + finally: + if proc.poll() is None: + proc.kill(); proc.communicate() + assert proc.returncode == 0, stderr + if redirected: + stdout = output_path.read_bytes() + else: + output_path.write_bytes(stdout) + assert stdout == body, label + text = stderr.decode() + (directory/(label+'.log')).write_text(text) + stats = parse_rts_stats(text) + assert stats['maximum_live_bytes'] <= 16*1024*1024, stats + assert peak is not None, 'RSS unavailable' + observations.append(dict(label=label, command=argv, seconds=time.monotonic()-started, + peak_kib=peak, rts_process=stats, sha256=hashlib.sha256(stdout).hexdigest())) + checks.append(label) + + for name in ['main', 'handled', 'child']: + for size in [0, 1, 4095, 4096, 4097, 65536, 1048576]: + body = (bytes(range(256))*((size+255)//256))[:size] + invoke(f'{name}-{size}', name, body) + invoke('redirected', 'main', bytes(range(256))*257, True) + for name, operation in [('closeDenied', 'resource/close'), ('transferDenied', 'core/spawn')]: + expected = result(False, tree_list([byte_tree(x.encode()) for x in + ['borrowed-resource', operation, '', 'none']])) + assert runner.invoke(name, pin, name, ['--io']) == expected, name + checks.append(name) + run = subprocess.run(command('diagnostic', False), input=b'', capture_output=True, timeout=20) + assert (run.returncode, run.stdout, run.stderr) == (0, b'', bytes([0,255,13,10])), run + checks.append('binary-stderr') + reader, writer = os.pipe() + os.close(reader) + try: + proc = subprocess.Popen(command('broken', False), stdin=subprocess.DEVNULL, + stdout=writer, stderr=subprocess.PIPE) + finally: + os.close(writer) + _, diagnostic = proc.communicate(timeout=20) + assert (proc.returncode, diagnostic) == (0, b'failed'), (proc.returncode, diagnostic) + checks.append('broken-pipe-ordinary-result') + paths = [source, Path(__file__).resolve(), ROOT/'lib/io-v2.tri', + ROOT/'src/IOV2/Resources.hs', ROOT/'src/IOV2/Driver.hs', ROOT/'src/IOV2/Protocol.hs', + ROOT/'test/rts_metrics.py', ROOT/'bootstrap/build-stable.arboricx'] + (directory/'report.json').write_text(json.dumps(dict(checks=checks, observations=observations, + sources={str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}), indent=2)+'\n') + print(f'PASS: {len(checks)} binary stream, lifecycle and pipe checks') + +if __name__ == '__main__': + main() diff --git a/test/io_v2_tasks.py b/test/io_v2_tasks.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""Independent source-level task/handler oracles, using a Nix-built native host.""" +import argparse +import hashlib +import json +import re +from pathlib import Path +from source_order import Runner +from stable_bundle import byte_tree +from http_headers import tree_list, number, result + +ROOT = Path(__file__).resolve().parent.parent + +def text(s): + return byte_tree(s.encode()) +def completed(value): + return tree_list([text('completed'), value]) +def failed(message): + return tree_list([text('failed'), text(message), b'\0']) + +def core_cases(): + return { + 'joined': completed(number(42)), + 'supervised': failed('fault'), + 'cancelled': tree_list([text('cancelled'), text('shutdown'), b'\0']), + 'ordinary': completed(result(False, text('domain'))), + 'consumed': tree_list([result(True, completed(number(42))), + result(False, tree_list([text('stale-task'), text('core/join'), b'\0', text('none')]))]), + 'inherited': tree_list([completed(number(7)), number(7)]), + 'changed': tree_list([completed(number(9)), number(7)]), + 'callback': tree_list([completed(number(7)), number(9)]), + 'lazyLabels': completed(number(7)), + 'labelledFailure': tree_list([text('failed'), tree_list([text('problem'), text('service'), + text('op'), text('detail'), tree_list([text('outer'), text('inner')])]), b'\0']), + 'labelledStateBoundary': tree_list([text('failed'), tree_list([text('state-boundary'), + text('log'), text('write'), text('Install child-local collection explicitly'), + tree_list([text('outer'), text('inner')])]), b'\0']), + 'stateBoundary': tree_list([text('failed'), tree_list([text('state-boundary'), + text('log'), text('write'), text('Install child-local collection explicitly'), b'\0']), b'\0']), + } + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + args.directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu, args.directory.resolve()) + source = ROOT/'test/io-v2/tasks.tri' + bundle = args.directory.resolve()/'tasks.arboricx' + runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle]) + pin = runner.imported(bundle, 'import') + cases = core_cases() + for name, expected in cases.items(): + argv = ['--io'] + if name == 'lazyLabels': + arg = args.directory.resolve()/'unit.tree' + arg.write_bytes(b'\0') + argv += ['--arg-tree-file', arg] + actual = runner.invoke(name, pin, name, argv) + assert actual == expected, (name, actual.hex(), expected.hex()) + partial_checks = [] + for name in ('partialWrites', 'failedPartialWrite'): + for body in (b'', b'\xff', bytes([0, 255, 1, 128, 2, 254, 3])): + argument = args.directory.resolve()/f'{name}-{len(body)}.tree' + argument.write_bytes(byte_tree(body)) + label = f'{name}-{len(body)}' + actual = runner.invoke(label, pin, name, ['--io', '--arg-tree-file', argument]) + fails = name == 'failedPartialWrite' and len(body) > 2 + expected = tree_list([result(False, tree_list([text('write-all'), number(2), text('write fault')])) if fails else result(True, b'\0'), + byte_tree(body[:2] if fails else body)]) + assert actual == expected, label + partial_checks.append(label) + retention = [] + for name in ('churnJoined', 'churnDiscarded'): + for count in (16, 256, 1024): + payload = args.directory.resolve()/f'{name}-{count}.bin' + payload.write_bytes(bytes(range(256))*(count*16)) + count_arg = args.directory.resolve()/f'count-{count}.tree' + count_arg.write_bytes(number(count)) + path_arg = args.directory.resolve()/f'{name}-{count}.path' + path_arg.write_bytes(byte_tree(str(payload).encode())) + label = f'{name}-{count}' + actual = runner.invoke(label, pin, name, ['--io', '--arg-tree-file', count_arg, + '--arg-tree-file', path_arg]) + assert actual == b'\0', label + row = dict(runner.records[-1]) + log = (args.directory/(label+'.log')).read_text() + match = re.search(r'([0-9,]+) bytes maximum residency', log) + assert match, label + row['maximum_live_bytes'] = int(match[1].replace(',', '')) + assert row['maximum_live_bytes'] <= 16*1024*1024, row + assert row['peak_kib'] <= 128*1024, row + retention.append(row) + paths = [source, ROOT/'lib/handlers.tri', ROOT/'lib/io-v2.tri', + ROOT/'src/IOV2/Tasks.hs', ROOT/'src/IOV2/Driver.hs', ROOT/'src/IOV2/Protocol.hs', + ROOT/'test/source_order.py', ROOT/'test/rts_metrics.py', ROOT/'src/IOV2/Resources.hs', + ROOT/'lib/http.tri', ROOT/'lib/math/native.tri', ROOT/'lib/base.tri', + ROOT/'bootstrap/build-stable.arboricx'] + (args.directory/'report.json').write_text(json.dumps({'checks': list(cases)+partial_checks, + 'sources': {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}, + 'retention': retention, 'observations': runner.records}, indent=2)+'\n') + print(f'PASS: {len(cases)+len(partial_checks)} task/handler source oracles and {len(retention)} fresh-chunk retention workloads') + +if __name__ == '__main__': + main() diff --git a/test/io_v2_tcp.py b/test/io_v2_tcp.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python3 +"""Real socket source workflows: full binary echo, directional pipelines and handoff.""" +import argparse +import concurrent.futures +import hashlib +import json +from pathlib import Path +import socket +import subprocess +import struct +import threading +import time +from source_order import Runner +from process_rss import ChildRSS +from rts_metrics import parse_rts_stats +from http_headers import number, result, tree_list +from io_v2_positioned import text +from io_v2_directory import parse_tree, items, string +ROOT = Path(__file__).resolve().parent.parent + +def endpoint(family, port): + if family == 4: + return tree_list([text('ipv4'),tree_list([number(x) for x in [127,0,0,1]]),number(port)]) + return tree_list([text('ipv6'),tree_list([number(x) for x in [0,0,0,0,0,0,0,1]]),number(0),number(port)]) + +def exchange(family, port, body, prefix): + with socket.socket(socket.AF_INET if family == 4 else socket.AF_INET6) as peer: + peer.settimeout(90) + peer.connect(('127.0.0.1' if family == 4 else '::1',port)) + def send(): + for start in range(0,len(body),997): + peer.sendall(body[start:start+997]) + peer.shutdown(socket.SHUT_WR) + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + writer = pool.submit(send) + chunks = [] + while True: + chunk = peer.recv(4096) + if not chunk: + break + chunks.append(chunk) + writer.result(timeout=90) + actual = b''.join(chunks) + assert actual == prefix+body, (len(actual),len(prefix+body)) + return hashlib.sha256(actual).hexdigest() + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu,directory) + source = ROOT/'demos/io-v2/tcp.tri' + bundle = directory/'tcp.arboricx' + runner.run('compile',['compile','--file',source,'--store',runner.store,'--output',bundle]) + pin = runner.imported(bundle,'import') + checks, observations = [], [] + def command(label, name, values): + argv = [str(runner.exe),'arboricx','run','--module',pin,'--name',name,'--store',str(runner.store),'--io','--no-result'] + for index,value in enumerate(values): + path = directory/f'{label}-{index}.tree' + path.write_bytes(value) + argv += ['--arg-tree-file',str(path)] + return argv+['+RTS','-N1','-M128m','-T','-s','-RTS'] + def launch(label,name,values): + argv = command(label,name,values) + proc = subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + data = dict(label=label, command=argv, started=time.monotonic(), peak_kib=0) + stopped = threading.Event() + def sample(): + try: + rss = ChildRSS(proc) + while not stopped.is_set(): + current = rss.sample() + if current is not None: + data['peak_kib'] = max(data['peak_kib'],current) + if current > 128*1024: + raise AssertionError('RSS bound exceeded') + stopped.wait(.01) + except Exception as error: + data['monitor_error'] = repr(error) + try: + proc.kill() + except ProcessLookupError: + pass + thread = threading.Thread(target=sample,daemon=True) + thread.start() + return proc,data,stopped,thread + def finish(run): + proc,data,stopped,thread = run + try: + stdout,stderr = proc.communicate(timeout=120) + stopped.set(); thread.join() + assert 'monitor_error' not in data, data + assert proc.returncode == 0, stderr + assert not stdout, stdout + stats = parse_rts_stats(stderr.decode()) + assert stats['maximum_live_bytes'] <= 16*1024*1024, stats + assert 0 < data['peak_kib'] <= 128*1024, data + (directory/(data['label']+'.log')).write_bytes(stderr) + data['seconds'] = time.monotonic()-data.pop('started') + data['rts_process'] = stats + observations.append(data) + finally: + stopped.set(); thread.join() + if proc.poll() is None: + proc.kill(); proc.communicate() + def abort(run): + proc,_,stopped,thread = run + stopped.set(); thread.join() + if proc.poll() is None: + proc.kill(); proc.communicate() + def server(label, name, family, bodies, parallel=False): + run = launch(label,name,[endpoint(family,0),number(len(bodies))]) + proc = run[0] + try: + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + # A timed readiness channel, never a probe connection consuming + # one of the finite server's accepted-connection slots. + ready = pool.submit(proc.stdout.readline) + try: + line = ready.result(timeout=20) + if not line: + _, diagnostic = proc.communicate(timeout=5) + (directory/(label+'.startup-error.log')).write_bytes(diagnostic) + raise AssertionError(diagnostic) + port = int(line) + except BaseException: + abort(run) + raise + prefix = bytes([66,0,255,10]) if name == 'handledServer' else b'' + if parallel: + with concurrent.futures.ThreadPoolExecutor(max_workers=len(bodies)) as pool: + sums = list(pool.map(lambda body: exchange(family,port,body,prefix),bodies)) + else: + sums = [exchange(family,port,body,prefix) for body in bodies] + finish(run) + checks.append(dict(label=label,sha256=sums)) + finally: + abort(run) + body = lambda size: (bytes(range(256))*((size+255)//256))[:size] + for name in ['server','handledServer']: + server(name+'-boundaries',name,4,[body(n) for n in [0,1,4095,4096,4097,65536,1048576]]) + server(name+'-ipv6',name,6,[body(4097)]) + server(name+'-concurrent',name,4,[bytes([i])+body(65535) for i in range(4)],True) + server(name+'-churn',name,4,[bytes([i])*64 for i in range(128)]) + for name in ['handledServer','supervisedHandledServer']: + label = name+'-reset' + run = launch(label,name,[endpoint(4,0),number(2)]) + proc = run[0] + try: + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + ready = pool.submit(proc.stdout.readline) + try: + port = int(ready.result(timeout=20)) + except BaseException: + abort(run) + raise + with socket.create_connection(('127.0.0.1',port),timeout=20) as peer: + banner = b'' + while len(banner) < 4: + chunk = peer.recv(4-len(banner)) + assert chunk, banner + banner += chunk + assert banner == bytes([66,0,255,10]) + # Banner proves accept/handoff completed before aborting the peer. + peer.setsockopt(socket.SOL_SOCKET,socket.SO_LINGER,struct.pack('ii',1,0)) + if name == 'supervisedHandledServer': + exchange(4,port,body(4097),bytes([66,0,255,10])) + finish(run) + else: + stdout,stderr = proc.communicate(timeout=20) + assert proc.returncode != 0 and b'IO v2 task failed' in stderr, stderr + assert stdout == b'' + (directory/(label+'.log')).write_bytes(stderr) + checks.append(dict(label=label)) + finally: + abort(run) + # Native source client reads/writes its socket concurrently. A real peer + # echoes with small buffers, forcing meaningful bidirectional backpressure. + for family in [4,6]: + for size in ([0,4097,1048576] if family == 4 else [4097]): + label = f'client-{family}-{size}' + incoming, outgoing = directory/(label+'.in'), directory/(label+'.out') + incoming.write_bytes(body(size)) + with socket.socket(socket.AF_INET if family == 4 else socket.AF_INET6) as listener: + listener.bind(('127.0.0.1' if family == 4 else '::1',0)); listener.listen(1); listener.settimeout(20) + port = listener.getsockname()[1] + def echo(): + peer,_ = listener.accept() + with peer: + peer.settimeout(90) + while True: + chunk = peer.recv(997) + if not chunk: + break + peer.sendall(chunk) + peer.shutdown(socket.SHUT_WR) + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + worker = pool.submit(echo) + run = launch(label,'client',[endpoint(family,port),text(str(incoming)),text(str(outgoing))]) + try: + finish(run) + worker.result(timeout=90) + finally: + abort(run) + assert outgoing.read_bytes() == incoming.read_bytes(), label + checks.append(dict(label=label,sha256=hashlib.sha256(outgoing.read_bytes()).hexdigest())) + # Explicit host resolution plus source fallback, using only local services. + for host in ['127.0.0.1','localhost']: + label = 'named-client-'+host + incoming, outgoing = directory/(label+'.in'), directory/(label+'.out') + incoming.write_bytes(body(65536)) + with socket.socket() as listener: + listener.bind(('127.0.0.1',0)); listener.listen(1); listener.settimeout(20) + port = listener.getsockname()[1] + def echo_named(): + peer,_ = listener.accept() + with peer: + peer.settimeout(30) + while True: + chunk = peer.recv(997) + if not chunk: + break + peer.sendall(chunk) + peer.shutdown(socket.SHUT_WR) + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + peer = pool.submit(echo_named) + run = launch(label,'namedClient',[text(host),text('any'),number(port),text(str(incoming)),text(str(outgoing))]) + try: + finish(run) + peer.result(timeout=30) + finally: + abort(run) + assert outgoing.read_bytes() == incoming.read_bytes() + checks.append(dict(label=label,sha256=hashlib.sha256(outgoing.read_bytes()).hexdigest())) + def invoke_values(label,name,values): + argv = ['--io'] + for index,value in enumerate(values): + path = directory/f'{label}-{index}.tree'; path.write_bytes(value) + argv += ['--arg-tree-file',path] + return runner.invoke(label,pin,name,argv) + actual = parse_tree(invoke_values('resolve-numeric','resolve',[text('127.0.0.1'),text('ipv4'),number(1234),number(64)])) + wrapped = items(actual) + assert wrapped[0] == (1,(0,)) + addresses,truncated = items(wrapped[1]) + assert items(addresses) and all(x == parse_tree(endpoint(4,1234)) for x in items(addresses)) + assert truncated == (0,) + checks.append(dict(label='resolve-numeric')) + for label,host,limit,category,detail in [ + ('resolve-host','bad/name',1,'invalid-argument','expected bounded ASCII host name'), + ('resolve-limit','localhost',0,'invalid-bound',''), + ]: + expected_error = result(False,tree_list([text(x) for x in [category,'resolver/resolve',detail,'none']])) + assert invoke_values(label,'resolve',[text(host),text('any'),number(80),number(limit)]) == expected_error + checks.append(dict(label=label)) + with socket.socket() as listener: + listener.bind(('127.0.0.1',0)); listener.listen(1); listener.settimeout(20) + selected = listener.getsockname()[1] + # Invalid first candidate, valid second, malformed third. Source policy + # retries ordinary failure but must stop before inspecting the third. + reply = items(parse_tree(invoke_values('candidate-fallback','candidates', + [tree_list([endpoint(4,0),endpoint(4,selected),b'\1\0'])]))) + assert reply[0] == (1,(0,)) + token = items(reply[1]) + assert len(token) == 3 and string(token[0]) == 'io.v2.resource' + peer,_ = listener.accept() + with peer: + peer.settimeout(20) + assert peer.recv(1) == b'' # returned token did not extend its scope + checks.append(dict(label='candidate-fallback')) + empty_error = result(False,tree_list([text(x) for x in ['not-found','tcp/connect-first','No endpoint candidates','none']])) + assert invoke_values('empty-candidates','candidates',[b'\0']) == empty_error + checks.append(dict(label='empty-candidates')) + expected = result(False,tree_list([text(x) for x in ['invalid-argument','tcp/connect','','none']])) + assert runner.invoke('invalid-address',pin,'invalidAddress',['--io']) == expected + checks.append(dict(label='invalid-address')) + with socket.socket() as unused: + unused.bind(('127.0.0.1',0)) + port = unused.getsockname()[1] + path = directory/'refused.tree'; path.write_bytes(endpoint(4,port)) + reply = items(parse_tree(runner.invoke('refused',pin,'connectResult',['--io','--arg-tree-file',path]))) + assert reply[0] == (0,) + problem = items(reply[1]) + assert [string(problem[i]) for i in [0,1,3]] == ['connection-refused','tcp/connect','unknown'] + checks.append(dict(label='refused')) + paths = [source,Path(__file__).resolve(),ROOT/'lib/io-v2.tri',ROOT/'lib/handlers.tri', + ROOT/'src/IOV2/Network.hs',ROOT/'src/IOV2/Resolver.hs',ROOT/'src/IOV2/Resources.hs',ROOT/'src/IOV2/Driver.hs', + ROOT/'src/IOV2/Protocol.hs',ROOT/'test/rts_metrics.py',ROOT/'test/process_rss.py',ROOT/'bootstrap/build-stable.arboricx'] + (directory/'report.json').write_text(json.dumps(dict(checks=checks,observations=observations, + sources={str(p.relative_to(ROOT)):hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}),indent=2)+'\n') + print(f'PASS: {len(checks)} actual TCP workflow groups') +if __name__ == '__main__': + main() diff --git a/test/io_v2_text.py b/test/io_v2_text.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +"""Independent UTF-8 boundaries, exact source provenance and text pipelines.""" +import argparse +import hashlib +import json +from pathlib import Path +from source_order import Runner +from http_headers import number, result, pair, tree_list +from stable_bundle import byte_tree +from process_rss import run_guarded + +ROOT = Path(__file__).resolve().parent.parent + +def text(s): + return tree_list([number(ord(c)) for c in s]) + +def problem(category, op, detail, progress='none'): + return tree_list([text(category),text(op),text(detail),text(progress)]) + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('--directory', type=Path, required=True) + parser.add_argument('--zig-probe', type=Path, help='also check the private Zig host against the same independent oracles') + args = parser.parse_args() + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + runner = Runner(args.tricu,directory) + source = ROOT/'demos/io-v2/text.tri' + bundle = directory/'text.arboricx' + runner.run('compile',['compile','--file',source,'--store',runner.store,'--output',bundle]) + pin = runner.imported(bundle,'import') + checks = [] + zig_observations = [] + def invoke(label,name,values,expected): + argv = ['--io'] + zig_arguments = [] + for i,value in enumerate(values): + path = directory/f'{label}-{i}.tree' + path.write_bytes(value) + argv += ['--arg-tree-file',path] + zig_arguments.append('tree-file:'+str(path)) + assert runner.invoke(label,pin,name,argv) == expected,label + if args.zig_probe: + command = [str(args.zig_probe.resolve()),str(bundle),name,*zig_arguments] + output,peak = run_guarded(command,memory_mib=256,seconds=180) + (directory/(label+'-zig.log')).write_text(output) + assert bytes(map(int,output.strip())) == expected,('zig',label) + zig_observations.append(dict(label=label,peak_kib=peak,command=command)) + checks.append(label) + for i,value in enumerate(['','ascii\r\n\0','λ😀e\u0301é','\ufeff','\uffff\U0010ffff','😀'*1025,'a'*1024+'😀'+'b'*1024]): + payload = value.encode('utf-8') + invoke(f'encode-{i}','encode',[number(len(payload)),text(value)],result(True,byte_tree(payload))) + output = directory/f'write-{i}.bin' + invoke(f'write-{i}','write',[text(str(output)),text(value)],result(True,b'\0')) + assert output.read_bytes() == payload + for i,payload in enumerate([b'',b'a\r\n\0', 'λ😀e\u0301é'.encode(), + b'a'*4092+'😀'.encode(), b'a'*4091+'😀x'.encode(), + ('a\ufeffe\u0301\r\n'*100).encode()]): + path = directory/f'source-{i}.bin' + path.write_bytes(payload) + invoke(f'source-{i}','source',[text(str(path)),number(len(payload))], + result(True,pair(byte_tree(payload),text(payload.decode('utf-8'))))) + assert path.read_bytes() == payload + payload = 'A😀λ'.encode() + # Every split through the multibyte suffix, with independent expected carry. + for i in range(len(payload)+1): + prefix = payload[:i] + import codecs + decoder = codecs.getincrementaldecoder('utf-8')('strict') + decoded = decoder.decode(prefix, final=False) + carry = decoder.getstate()[0] + invoke(f'prefix-{i}','decode',[number(0),byte_tree(prefix)], + result(True,tree_list([text(decoded),byte_tree(carry)]))) + for i,(payload,cause) in enumerate([ + (b'\xef\xbb\xbfabc',problem('invalid-data','text/read-source','initial source BOM is not permitted')), + (b'good\xff',problem('invalid-data','utf8/decode','invalid UTF-8 leading byte at byte 4')), + (b'\xe2\x82',problem('invalid-data','utf8/decode','incomplete UTF-8 sequence at byte 0')), + (b'\xed\xa0\x80',problem('invalid-data','utf8/decode','invalid UTF-8 sequence at byte 0'))]): + path = directory/f'invalid-{i}.bin'; path.write_bytes(payload) + invoke(f'invalid-source-{i}','source',[text(str(path)),number(len(payload))], + result(False,tree_list([text('read-source'),number(len(payload)),cause]))) + path = directory/'excess.bin'; path.write_bytes(b'x') + invoke('source-limit','source',[text(str(path)),number(0)],result(False,tree_list([ + text('read-source'),number(1),problem('invalid-bound','text/read-source','source exceeds byte limit')]))) + invoke('encode-limit','encode',[number(1),text('λ')],result(False, + problem('invalid-bound','text/encode','encoded text exceeds byte limit'))) + invoke('surrogate','encode',[number(4096),tree_list([number(0xd800)])],result(False, + problem('invalid-argument','utf8/encode',''))) + partial = directory/'partial.bin' + invalid_text = tree_list([number(97)]*1024+[number(0xd800)]) + invoke('write-invalid-tail','write',[text(str(partial)),invalid_text],result(False,tree_list([ + text('write-text'),number(1024),problem('invalid-argument','utf8/encode','')]))) + assert partial.read_bytes() == b'a'*1024 + for size in [4096,65536]: + payload = (b'ab\r\n\0'+ 'λ😀'.encode())*(size//11) + input_path = directory/f'pipeline-{size}.bin'; output = directory/f'pipeline-{size}-out.bin' + input_path.write_bytes(payload) + invoke(f'pipeline-{size}','roundTrip',[text(str(input_path)),text(str(output)),number(len(payload))],result(True,b'\0')) + assert output.read_bytes() == payload + if args.zig_probe: + # Independent raw-protocol boundaries; excess heads AND tails diverge. + # Both backends must reject without demanding them or writing a prefix. + boundary = directory/'boundaries.arboricx' + runner.run('compile-boundaries',['compile','--file',ROOT/'test/io-v2/zig-utf8.tri','--store',runner.store,'--output',boundary]) + original_bundle, original_pin = bundle, pin + bundle, pin = boundary, runner.imported(boundary,'import-boundaries') + for name,operation in [('excessEncode','utf8/encode'),('excessDecode','utf8/decode'),('excessDigest','digest/update')]: + invoke(name,name,[number(0)],result(False,problem('invalid-argument',operation,''))) + path = directory/'excess-write' + invoke('excessWrite','excessWrite',[text(str(path)),number(0)],result(False,problem('invalid-argument','stream/write',''))) + assert path.read_bytes() == b'' + bundle, pin = original_bundle, original_pin + observations = [r for r in runner.records if r['label'].startswith('pipeline-')] + for row in observations: + assert row['peak_kib'] <= 256*1024,row + assert row['rts_process']['maximum_live_bytes'] <= 128*1024*1024,row + files = ['lib/io-v2.tri','lib/io-v2/text.tri','demos/io-v2/text.tri','test/io_v2_text.py', + 'test/source_order.py','test/rts_metrics.py','src/IOV2/UTF8.hs','src/IOV2/Driver.hs', + 'src/IOV2/Protocol.hs','bootstrap/build-stable.arboricx'] + if args.zig_probe: files += ['test/io-v2/zig-utf8.tri','ext/zig/src/io_v2_utf8.zig','ext/zig/src/io_v2_file_host.zig','ext/zig/src/io_v2_core_probe.zig'] + (directory/'report.json').write_text(json.dumps(dict(checks=checks,observations=observations,zig_observations=zig_observations, + zig_probe_sha256=hashlib.sha256(args.zig_probe.read_bytes()).hexdigest() if args.zig_probe else None, + source_hashes={p:hashlib.sha256((ROOT/p).read_bytes()).hexdigest() for p in files}),indent=2)+'\n') + print(f'{len(checks)} independent text/UTF-8 checks passed'+(' on Haskell and private Zig' if args.zig_probe else '')) + +if __name__ == '__main__': main() diff --git a/test/io_v2_zig_core.py b/test/io_v2_zig_core.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""Actual source core/handler parity using the PRIVATE Zig core probe, not native IO.""" +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from source_order import Runner +from io_v2_tasks import core_cases +from process_rss import run_guarded +from http_headers import number, result, tree_list +from stable_bundle import byte_tree +import time + +ROOT = Path(__file__).resolve().parent.parent + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('probe', type=Path) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + d = args.directory.resolve(); d.mkdir(parents=True, exist_ok=False) + r = Runner(args.tricu, d) + probe = args.probe.resolve() + source = ROOT/'test/io-v2/tasks.tri' + bundle = d/'tasks.arboricx' + r.run('compile', ['compile', '--file', source, '--store', r.store, '--output', bundle]) + pin = r.imported(bundle, 'import') + checks = [] + for name, expected in core_cases().items(): + flags = ['--io']; argv = [] + if name == 'lazyLabels': + (d/'zero.tree').write_bytes(b'\0') + flags += ['--arg-tree-file', d/'zero.tree']; argv = ['0'] + assert r.invoke('haskell-'+name, pin, name, flags) == expected, ('haskell', name) + process = subprocess.run([str(probe), str(bundle), name, *argv], capture_output=True, timeout=60) + (d/(name+'.stdout')).write_bytes(process.stdout) + (d/(name+'.stderr')).write_bytes(process.stderr) + assert process.returncode == 0, (name, process.returncode, process.stderr) + assert process.stdout == ''.join(map(str, expected)).encode()+b'\n', name + checks.append(name) + controls_source = ROOT/'test/io-v2/core-controls.tri' + controls_bundle = d/'controls.arboricx' + r.run('compile-controls', ['compile', '--file', controls_source, '--store', r.store, '--output', controls_bundle]) + controls_pin = r.imported(controls_bundle, 'import-controls') + def text(s): return byte_tree(s.encode()) + def problem(category, operation, detail=''): + return result(False, tree_list([text(category), text(operation), text(detail), text('none')])) + controls = { + 'cancelChild': (tree_list([text('cancelled'), text('stop'), b'\0']), False), + 'currentScopes': (number(1), False), + 'badFlags': (problem('invalid-argument', 'core/spawn'), True), + 'badTransfer': (problem('invalid-argument', 'core/spawn'), True), + 'discardedTicket': (problem('stale-task', 'core/join'), False), + 'selfCycle': (number(42), False), + 'pureDomainError': (result(False, text('ordinary')), False), + 'unknownWithPoison': (problem('unsupported', 'future/call', 'group revision 3'), True), + } + for name, (expected, seeded) in controls.items(): + flags = ['--io'] + (['--arg-tree-file', d/'zero.tree'] if seeded else []) + assert r.invoke('haskell-'+name, controls_pin, name, flags) == expected, ('haskell', name) + p = subprocess.run([str(probe), str(controls_bundle), name] + (['0'] if seeded else []), capture_output=True, timeout=60) + (d/(name+'.stderr')).write_bytes(p.stderr) + assert p.returncode == 0 and p.stdout == ''.join(map(str, expected)).encode()+b'\n', (name, p.returncode, p.stderr, p.stdout) + checks.append(name) + for name, seeded in [('selfCycleWrong', False), ('rootFailure', True), ('rootCancelled', True), ('malformedBind', False)]: + flags = ['--io'] + (['--arg-tree-file', d/'zero.tree'] if seeded else []) + try: + r.invoke('haskell-'+name, controls_pin, name, flags) + except subprocess.CalledProcessError as error: + assert 'IO v2 task '+('cancelled' if name == 'rootCancelled' else 'failed') in error.output + else: + raise AssertionError(('Haskell negative succeeded', name)) + p = subprocess.run([str(probe), str(controls_bundle), name] + (['0'] if seeded else []), capture_output=True, timeout=60) + assert p.returncode == 1 and p.stdout == b'' and b'not forced' in p.stderr, (name, p.returncode, p.stderr) + checks.append(name+'-negative') + p = subprocess.run([str(probe), str(controls_bundle), 'legacyEnvelope', '0'], capture_output=True, timeout=10) + assert p.returncode != 0 and p.stdout == b'' and b'UnsupportedVersion' in p.stderr, p.stderr + checks.append('v1-not-retried') + retention_source = ROOT/'test/io-v2/core-retention.tri' + retention_bundle = d/'retention.arboricx' + r.run('compile-retention', ['compile', '--file', retention_source, '--store', r.store, '--output', retention_bundle]) + retention_pin = r.imported(retention_bundle, 'import-retention') + retention = [] + for name in ('joined', 'discarded'): + for count in (16, 128, 512): + label = f'{name}-{count}' + argument = d/(label+'.tree'); argument.write_bytes(number(count)) + assert r.invoke('haskell-retention-'+label, retention_pin, name, ['--io', '--arg-tree-file', argument]) == b'\0' + command = [str(probe), str(retention_bundle), name, str(count)] + start = time.monotonic() + output, peak = run_guarded(command, memory_mib=128, seconds=180) + (d/('zig-retention-'+label+'.log')).write_text(output) + assert output == '0\n', (label, output) + retention.append(dict(name=name, count=count, peak_kib=peak, seconds=time.monotonic()-start, command=command)) + paths = [controls_source, retention_source, source, probe, ROOT/'ext/zig/src/io_v2_core.zig', ROOT/'ext/zig/src/io_v2_tasks.zig', ROOT/'ext/zig/src/io_v2_resources.zig', ROOT/'ext/zig/src/io_v2_protocol.zig', ROOT/'test/io_v2_tasks.py'] + (d/'report.json').write_text(json.dumps({'checks': checks, 'scope': 'private-core-only-no-OS-facilities', 'retention': retention, 'sources': {str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}, 'haskell_records': r.records}, indent=2)+'\n') + print(f'PASS {len(checks)} actual source core/handler controls on Haskell and private Zig core') + +if __name__ == '__main__': main() diff --git a/test/io_v2_zig_digest.py b/test/io_v2_zig_digest.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +"""Actual incremental digest source on private Zig and Haskell; external SHA oracle.""" +import argparse +import hashlib +import json +from pathlib import Path +from source_order import Runner +from http_headers import tree_list, result, number +from process_rss import run_guarded +ROOT = Path(__file__).resolve().parent.parent + +def text(s): return tree_list([number(ord(c)) for c in s]) +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu',type=Path); p.add_argument('probe',type=Path); p.add_argument('--directory',type=Path,required=True) + args=p.parse_args(); d=args.directory.resolve(); d.mkdir(parents=True,exist_ok=False) + r=Runner(args.tricu,d); bundle=d/'digest.arboricx' + r.run('compile',['compile','--file',ROOT/'test/io-v2/zig-digest.tri','--store',r.store,'--output',bundle]); pin=r.imported(bundle,'import') + def problem(op): return result(False,tree_list([text('invalid-state'),text(op),text('digest already finished'),text('none')])) + empty=hashlib.sha256(b'').hexdigest(); abc=hashlib.sha256(b'abc').hexdigest() + cases=[('hash','abc',text(abc)),('hash','ÿ',text(hashlib.sha256(bytes([255])).hexdigest())),('hash','a'*4096,text(hashlib.sha256(b'a'*4096).hexdigest())), + ('lifecycle',None,tree_list([text(empty),problem('digest/finish'),problem('digest/update')])), + ('transferred',None,tree_list([text('completed'),text(abc)]))] + for algorithm in ('sha512','é😀'*100): + cases.append(('unsupported',algorithm,result(False,tree_list([text('unsupported'),text('digest/open'),text(algorithm[:128]),text('none')])))) + checks=[] + for i,(name,argument,expected) in enumerate(cases): + label=f'{i}-{name}'; command=[str(args.probe.resolve()),str(bundle),name]+(['text:'+argument] if argument is not None else []) + output,peak=run_guarded(command,memory_mib=128,seconds=60) + (d/(label+'.log')).write_text(output) + assert bytes(map(int,output.strip()))==expected,('zig',label) + flags=['--io'] + if argument is not None: + arg=d/(label+'.tree');arg.write_bytes(text(argument));flags+=['--arg-tree-file',arg] + assert r.invoke(label,pin,name,flags)==expected,('haskell',label) + checks.append(dict(label=label,peak_kib=peak)) + (d/'report.json').write_text(json.dumps(dict(checks=checks,haskell=r.records,probe_sha256=hashlib.sha256(args.probe.read_bytes()).hexdigest()),indent=2)+'\n') + print(f'PASS {len(checks)} digest source controls on Haskell and private Zig') +if __name__=='__main__':main() diff --git a/test/io_v2_zig_files.py b/test/io_v2_zig_files.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +"""Private Zig regular-file host: actual source, independent bytes and trees.""" +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from source_order import Runner, field +from http_headers import result, tree_list, number +from stable_bundle import byte_tree +from process_rss import run_guarded + +ROOT = Path(__file__).resolve().parent.parent + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu', type=Path) + p.add_argument('probe', type=Path) + p.add_argument('--directory', type=Path, required=True) + args = p.parse_args() + d = args.directory.resolve(); d.mkdir(parents=True, exist_ok=False) + r = Runner(args.tricu, d) + bundle = d/'copy.arboricx' + r.run('compile', ['compile', '--file', ROOT/'demos/io-v2/copy.tri', '--store', r.store, '--output', bundle]) + pin = r.imported(bundle, 'import') + checks = [] + def invoke(name, source, target, label): + command = [str(args.probe.resolve()), str(bundle), name, 'text:'+str(source), 'text:'+str(target)] + output, peak = run_guarded(command, memory_mib=128, seconds=180) + (d/(label+'.log')).write_text(output) + assert output.endswith('\n') and set(output.strip()) <= set('012'), (label, output) + return bytes(map(int, output.strip())), peak + for name in ('copy', 'handledCopy', 'borrowedCopy', 'transferredCopy', 'joinedOnExitCopy', 'handledChildCopy', 'cancelledCopy'): + for size in (0, 1, 4095, 4096, 4097, 65536, 1048576): + label = f'{name}-{size}' + source, target = d/(label+'.in'), d/(label+'.out') + body = (bytes(range(256))*((size+255)//256))[:size] + source.write_bytes(body) + expected = result(True, b'\0') + if name == 'handledCopy': expected = result(True, expected) + if name == 'cancelledCopy': expected = tree_list([byte_tree(b'cancelled'), byte_tree(b'requested'), b'\0']) + actual, peak = invoke(name, source, target, label) + assert actual == expected, (label, actual) + assert target.read_bytes() == (body[:4096] if name == 'cancelledCopy' else body), label + # Actual Haskell comparison on each composition at the boundary; the + # complete corpus is independently checked by io_v2_files.py too. + if size == 4097: + argv = ['--io'] + for i, path in enumerate((source, d/(label+'.haskell'))): + argument = d/f'{label}-{i}.tree'; argument.write_bytes(tree_list([number(ord(c)) for c in str(path)])) + argv += ['--arg-tree-file', argument] + assert r.invoke('haskell-'+label, pin, name, argv) == expected + assert (d/(label+'.haskell')).read_bytes() == target.read_bytes() + checks.append(dict(label=label, peak_kib=peak, sha256=hashlib.sha256(target.read_bytes()).hexdigest())) + source, target = d/'source-é😀', d/'target-é😀' + source.write_bytes(b'\x00\xffunicode-path') + actual, peak = invoke('copy', source, target, 'unicode-path') + assert actual == result(True, b'\0') and target.read_bytes() == source.read_bytes() + flags = ['--io'] + target.unlink() + for i, path in enumerate((source, target)): + arg = d/f'unicode-{i}.tree'; arg.write_bytes(tree_list([number(ord(c)) for c in str(path)])) + flags += ['--arg-tree-file', arg] + assert r.invoke('unicode-path-haskell', pin, 'copy', flags) == result(True, b'\0') + assert target.read_bytes() == source.read_bytes() + checks.append(dict(label='unicode-path', peak_kib=peak)) + for case in ('missing-source', 'missing-parent', 'fifo', 'directory', 'device'): + source, target = d/(case+'.in'), d/(case+'.out') + source.write_bytes(b'keep') + category = b'not-found'; operation = b'file/open-reader' + if case == 'missing-source': source = d/'does-not-exist' + elif case == 'missing-parent': target = d/'missing-parent'/'out'; operation = b'file/open-writer' + elif case == 'fifo': + import os + source.unlink(); os.mkfifo(source); category = b'unsupported' + elif case == 'directory': source = d; category = b'unsupported' + elif case == 'device': source = Path('/dev/null'); category = b'unsupported' + actual, peak = invoke('copy', source, target, case) + assert field(actual, 0) == b'\0', case + problem = field(actual, 1, 0) + assert field(problem, 0) == byte_tree(category), case + assert field(problem, 1, 0) == byte_tree(operation), case + assert not target.exists(), case + checks.append(dict(label=case, peak_kib=peak)) + controls = d/'controls.arboricx' + r.run('compile-controls', ['compile', '--file', ROOT/'test/io-v2/zig-files.tri', '--store', r.store, '--output', controls]) + control_pin = r.imported(controls, 'import-controls') + def problem(category, operation, detail=b''): + return result(False, tree_list([byte_tree(category), byte_tree(operation), byte_tree(detail), byte_tree(b'none')])) + data = bytes([2])+byte_tree(b'data')+byte_tree(b'bc') + eof = bytes([2])+byte_tree(b'eof')+b'\0' + control_cases = { + 'positioned': (b'', tree_list([data, number(3), number(10), eof]), b'a'), + 'appended': (b'k', tree_list([number(3), problem(b'unsupported', b'file/seek', b'operation unavailable in this file mode')]), b'kxy'), + 'emptyWrite': (b'previous', problem(b'invalid-bound', b'stream/write'), b''), + 'closed': (b'keep', problem(b'stale-handle', b'resource/close'), b'keep'), + 'readerWrite': (b'keep', problem(b'wrong-kind', b'stream/write'), b'keep'), + } + for name, (before, expected, after) in control_cases.items(): + for backend in ('zig', 'haskell'): + target = d/(name+'-'+backend+'.file'); target.write_bytes(before) + if backend == 'zig': + output, peak = run_guarded([str(args.probe.resolve()), str(controls), name, 'text:'+str(target)], memory_mib=128, seconds=60) + actual = bytes(map(int, output.strip())) + else: + argument = d/(name+'.tree'); argument.write_bytes(tree_list([number(ord(c)) for c in str(target)])) + actual = r.invoke(name, control_pin, name, ['--io', '--arg-tree-file', argument]) + assert actual == expected, (name, backend, actual, expected) + assert target.read_bytes() == after, (name, backend) + checks.append(dict(label=name)) + for backend in ('zig', 'haskell'): + target = d/('failed-'+backend) + if backend == 'zig': + process = subprocess.run([str(args.probe.resolve()), str(controls), 'failedAfterWrite', 'text:'+str(target)], capture_output=True, timeout=60) + assert process.returncode == 1 and b'not forced' in process.stderr + else: + argument = d/'failed.tree'; argument.write_bytes(tree_list([number(ord(c)) for c in str(target)])) + try: r.invoke('failedAfterWrite', control_pin, 'failedAfterWrite', ['--io', '--arg-tree-file', argument]) + except subprocess.CalledProcessError as error: assert 'IO v2 task failed' in error.output + else: raise AssertionError('failedAfterWrite succeeded') + assert target.read_bytes() == b'*' + checks.append(dict(label='failed-after-write-no-rollback')) + output, _ = run_guarded([str(args.probe.resolve()), str(controls), 'unknownWithPoison', '0'], memory_mib=128, seconds=10) + assert bytes(map(int, output.strip())) == problem(b'unsupported', b'file/open-reader', b'group revision 99') + checks.append(dict(label='unknown-revision-no-demand')) + paths = [ROOT/'test/io-v2/zig-files.tri', ROOT/'demos/io-v2/copy.tri', ROOT/'ext/zig/src/io_v2_files.zig', ROOT/'ext/zig/src/io_v2_file_host.zig', ROOT/'ext/zig/src/io_v2_core.zig', args.probe.resolve()] + (d/'report.json').write_text(json.dumps(dict(checks=checks, sources={str(p):hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}, haskell=r.records, scope='private-regular-file-host-no-public-v2'), indent=2)+'\n') + print(f'PASS {len(checks)} private file-host source/byte controls; 14 Haskell boundary/mode/failure comparisons') +if __name__ == '__main__': main() diff --git a/test/io_v2_zig_paths.py b/test/io_v2_zig_paths.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Private Zig path/directory operations: independent results, Haskell comparison.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +from source_order import Runner +from http_headers import tree_list, number +from stable_bundle import byte_tree +from process_rss import run_guarded +ROOT = Path(__file__).resolve().parent.parent + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu', type=Path); p.add_argument('probe', type=Path) + p.add_argument('--directory', type=Path, required=True) + args = p.parse_args(); d = args.directory.resolve(); d.mkdir(parents=True, exist_ok=False) + r = Runner(args.tricu, d); bundle = d/'paths.arboricx' + r.run('compile', ['compile', '--file', ROOT/'test/io-v2/zig-paths.tri', '--store', r.store, '--output', bundle]) + pin = r.imported(bundle, 'import'); checks = [] + def run(name, paths, expected, label, haskell=True): + output, peak = run_guarded([str(args.probe.resolve()), str(bundle), name, *['text:'+str(path) for path in paths]], memory_mib=128, seconds=60) + (d/(label+'.log')).write_text(output) + assert bytes(map(int, output.strip())) == expected, label + if haskell: + flags = ['--io'] + for i, path in enumerate(paths): + arg = d/f'{label}-{i}.tree'; arg.write_bytes(tree_list([number(ord(c)) for c in str(path)])) + flags += ['--arg-tree-file', arg] + assert r.invoke(label+'-haskell', pin, name, flags) == expected, label + checks.append(dict(label=label, peak_kib=peak)) + regular = tree_list([byte_tree(b'file'), number(3)]) + for case in ('ascii', 'é😀'): + directory, source = d/(case+'-dir'), d/(case+'-source') + target = directory/'target' + run('paths', [directory, source, target], tree_list([regular, tree_list([byte_tree(b'directory')]), regular, byte_tree(b'not-found')]), case) + assert not directory.exists() and not source.exists() and not target.exists() + source = d/'source'; source.write_bytes(b'abc') + link = d/'link'; link.symlink_to(source) + run('observe', [link], tree_list([tree_list([byte_tree(b'symlink')]), regular]), 'symlink') + run('observe', [source], tree_list([regular, regular]), 'regular') + directory_status = tree_list([byte_tree(b'directory')]) + run('observe', [d], tree_list([directory_status, directory_status]), 'directory') + fifo = d/'fifo'; os.mkfifo(fifo) + other = tree_list([byte_tree(b'other')]) + run('observe', [fifo], tree_list([other, other]), 'fifo-observation') + run('badDirectory', [source], byte_tree(b'not-directory'), 'sync-regular') + run('badDirectory', [fifo], byte_tree(b'not-directory'), 'sync-fifo') + target = d/'missing-parent'/'out' + run('failedRename', [source, target], byte_tree(b'not-found'), 'rename-missing-parent') + assert source.read_bytes() == b'abc' and not target.exists() + (d/'report.json').write_text(json.dumps(dict(checks=checks, haskell=r.records, source_sha256=hashlib.sha256((ROOT/'test/io-v2/zig-paths.tri').read_bytes()).hexdigest(), probe_sha256=hashlib.sha256(args.probe.read_bytes()).hexdigest()), indent=2)+'\n') + print(f'PASS {len(checks)} path/directory controls on private Zig and Haskell') +if __name__ == '__main__': main() diff --git a/test/io_v2_zig_publication.py b/test/io_v2_zig_publication.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""Real source-owned publication policy on private Zig, with Haskell comparisons.""" +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from source_order import Runner, field +from http_headers import result, tree_list, number +from process_rss import run_guarded +ROOT = Path(__file__).resolve().parent.parent + +def text(s): return tree_list([number(ord(c)) for c in str(s)]) +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('tricu',type=Path);p.add_argument('probe',type=Path);p.add_argument('--directory',type=Path,required=True) + args=p.parse_args();d=args.directory.resolve();d.mkdir(parents=True,exist_ok=False) + r=Runner(args.tricu,d);bundle=d/'publication.arboricx' + r.run('compile',['compile','--file',ROOT/'demos/io-v2/publish.tri','--store',r.store,'--output',bundle]);pin=r.imported(bundle,'import') + checks=[] + def invoke(label,name,values,expected,compare=False): + command=[str(args.probe.resolve()),str(bundle),name,*[str(v) if isinstance(v,int) else 'text:'+str(v) for v in values]] + output,peak=run_guarded(command,memory_mib=128,seconds=180) + (d/(label+'.log')).write_text(output) + actual=bytes(map(int,output.strip())) + if expected is not None: assert actual==expected,(label,actual,expected) + if compare: + flags=['--io'] + for i,v in enumerate(values): + arg=d/f'{label}-{i}.tree';arg.write_bytes(number(v) if isinstance(v,int) else text(v));flags+=['--arg-tree-file',arg] + assert r.invoke(label+'-haskell',pin,name,flags)==actual,label + checks.append(dict(label=label,peak_kib=peak)) + return actual + for size in (0,1,4095,4096,4097,65536,1048576): + source=d/f'input-{size}';body=(bytes(range(256))*((size+255)//256))[:size];source.write_bytes(body) + target=d/f'objects-{size}';target.mkdir();digest=hashlib.sha256(body).hexdigest() + for state in ('new','existing'): + invoke(f'{state}-{size}','publish',[source,target,size],result(True,text(digest)),size==4097) + assert (target/digest).read_bytes()==body + assert (target/digest).stat().st_mode&0o077==0 + assert sorted(p.name for p in target.iterdir())==[digest] + source=d/'source-λ-树';source.write_bytes(b'\0\xffbinary');target=d/'objects-λ-树';target.mkdir() + digest=hashlib.sha256(source.read_bytes()).hexdigest() + invoke('unicode','publish',[source,target,8],result(True,text(digest)),True) + assert (target/digest).read_bytes()==source.read_bytes() + source=d/'negative-input';source.write_bytes(b'abc');digest=hashlib.sha256(b'abc').hexdigest() + for label,count,winner,message in [('short',4,None,'Incomplete artifact stream'),('excess',2,None,'Artifact exceeds declared length'),('bad-winner',3,b'xyz','Existing artifact digest mismatch'),('short-winner',3,b'ab','Existing artifact length mismatch'),('long-winner',3,b'abcd','Existing artifact length mismatch')]: + target=d/label;target.mkdir() + if winner is not None:(target/digest).write_bytes(winner) + invoke(label,'publish',[source,target,count],result(False,text(message)),True) + assert sorted(p.name for p in target.iterdir())==([] if winner is None else [digest]) + if winner is not None:assert (target/digest).read_bytes()==winner + for name,message in [('earlySuccess','Incomplete artifact stream'),('ignoresWriteFailure','Artifact exceeds declared length'),('emptyChunk','Empty artifact chunk'),('overrideWrite','Incomplete artifact stream'),('failedAfterFull','Producer failed'),('rejectedDestination','Destination rejected')]: + target=d/name;target.mkdir();invoke(name,name,[target],result(False,text(message)),True);assert list(target.iterdir())==[] + for name,problem in [ + ('malformedDestination',tree_list([text('invalid-response'),text('artifact-stream'),text('destination'),text('Expected destination Result'),b'\0'])), + ('badDigest',tree_list([text('invalid-response'),text('artifact-stream'),text('digest'),text('Invalid digest reply'),b'\0'])), + *[(n,tree_list([text('producer-boundary'),text('artifact-stream'),text('publish'),text('Producer must be sequential'),tree_list([text('outer'),text('inner')])])) for n in ('nestedProducer','forkedProducer')]]: + target=d/name;target.mkdir();invoke(name,name,[target],tree_list([text('failed'),problem,b'\0']),True);assert list(target.iterdir())==[] + target=d/'invalidChunk';target.mkdir();actual=invoke('invalidChunk','invalidChunk',[target],None,True) + assert field(actual,0)==b'\0' and field(actual,1,0,0)==text('write-all') and field(actual,1,0,1,0)==number(0) + assert field(actual,1,0,1,1,0,0)==text('invalid-argument');assert list(target.iterdir())==[] + target=d/'cancelled';target.mkdir() + process=subprocess.run([str(args.probe.resolve()),str(bundle),'cancelled','text:'+str(target)],capture_output=True,timeout=60) + assert process.returncode==1 and b'cancelled (reason not forced)' in process.stderr + assert list(target.iterdir())==[] and not (d/'escape').exists() + checks.append(dict(label='cancelled-no-private-names')) + (d/'report.json').write_text(json.dumps(dict(checks=checks,haskell=r.records,probe_sha256=hashlib.sha256(args.probe.read_bytes()).hexdigest()),indent=2)+'\n') + print(f'PASS {len(checks)} private Zig source publication/policy controls') +if __name__=='__main__':main() diff --git a/test/io_v2_zig_stages.py b/test/io_v2_zig_stages.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""Cross-directory publication, EXDEV, invalid destinations and no rollback.""" +import argparse +import hashlib +import json +import subprocess +import tempfile +from pathlib import Path +from source_order import Runner +from http_headers import result,tree_list,number +from process_rss import run_guarded +ROOT=Path(__file__).resolve().parent.parent + +def text(s):return tree_list([number(ord(c)) for c in str(s)]) +def main(): + p=argparse.ArgumentParser(description=__doc__);p.add_argument('tricu',type=Path);p.add_argument('probe',type=Path);p.add_argument('--directory',type=Path,required=True) + args=p.parse_args();d=args.directory.resolve();d.mkdir(parents=True,exist_ok=False);r=Runner(args.tricu,d);bundle=d/'stages.arboricx' + r.run('compile',['compile','--file',ROOT/'test/io-v2/zig-stages.tri','--store',r.store,'--output',bundle]);pin=r.imported(bundle,'import');checks=[] + a,b=d/'a',d/'b';a.mkdir();b.mkdir();target=b/'published' + def invoke(name,values,label,expected): + command=[str(args.probe.resolve()),str(bundle),name,*[str(v) if isinstance(v,int) else 'text:'+str(v) for v in values]] + output,peak=run_guarded(command,memory_mib=128,seconds=60);(d/(label+'.log')).write_text(output) + assert bytes(map(int,output.strip()))==expected,label + checks.append(dict(label=label,peak_kib=peak)) + for mode,state in [(0,'published'),(0,'exists'),(1,'published')]: + invoke('publish',[a,target,mode],f'publish-{mode}-{state}',result(True,text(state))) + assert target.read_bytes()==b'abc' and list(a.iterdir())==[] + target.unlink() # prove visibility was created before the terminal failure + process=subprocess.run([str(args.probe.resolve()),str(bundle),'failureAfterPublication','text:'+str(a),'text:'+str(target)],capture_output=True,timeout=60) + assert process.returncode==1 and b'failed (payload not forced)' in process.stderr + assert target.read_bytes()==b'abc' and list(a.iterdir())==[];checks.append(dict(label='no-rollback-after-failure')) + for name in ('.tricu-stage-protected','x.tricu-stage-protected'): + bad=b/name + expected=tree_list([result(False,tree_list([text('invalid-destination'),text('stage/publish'),text('destination must name a file outside the private staging namespace'),text('not-published')])),result(False,tree_list([text('invalid-state'),text('stage/publish'),text('stage is not in the required phase'),text('none')]))]) + invoke('invalidDestination',[a,bad],name,expected) + assert not bad.exists() and list(a.iterdir())==[] + # Do not manufacture EXDEV through a mock here: require a distinct device. + with tempfile.TemporaryDirectory(prefix='tricu-zig-exdev-',dir='/dev/shm') as foreign: + foreign=Path(foreign) + assert foreign.stat().st_dev != a.stat().st_dev,'EXDEV control needs distinct filesystems' + output=foreign/'out' + expected=tree_list([tree_list([text('io-error'),text('unknown')]),tree_list([text('invalid-state'),text('none')])]) + invoke('crossDevice',[a,output],'actual-exdev',expected) + assert not output.exists() and list(a.iterdir())==[] + flags=['--io'] + for i,v in enumerate((a,output)): + arg=d/f'exdev-{i}.tree';arg.write_bytes(text(v));flags+=['--arg-tree-file',arg] + assert r.invoke('exdev-haskell',pin,'crossDevice',flags)==expected + assert not output.exists() and list(a.iterdir())==[] + (d/'report.json').write_text(json.dumps(dict(checks=checks,haskell=r.records,probe_sha256=hashlib.sha256(args.probe.read_bytes()).hexdigest()),indent=2)+'\n') + print(f'PASS {len(checks)} private staged-publication controls including real EXDEV') +if __name__=='__main__':main() diff --git a/test/library_inspector.py b/test/library_inspector.py @@ -83,6 +83,9 @@ def stage_site(root, site, report): The caller owns this local workspace exclusively. This is not network publication, a signed attestation, or a concurrent index-merge protocol. """ + version = report.get('io_version', 1) + if type(version) is not int or version not in (1, 2): + raise ValueError('Unsupported publication IO version') if not report.get('complete'): raise ValueError('Only an accepted publication can be staged') index_path = site / 'packages.json' @@ -130,16 +133,26 @@ def main(): p.add_argument('tricu', type=Path) p.add_argument('--oracle', required=True, type=Path) p.add_argument('--directory', required=True, type=Path) - p.add_argument('--version', default='0.1.0', help='Exact release label for this library set') + p.add_argument('--io-version', type=int, choices=[2], default=2, help='Consumer IO protocol (current sources require v2)') + p.add_argument('--version', help='Exact release label for this library set') p.add_argument('--stage-site', type=Path, help='After acceptance, stage bundles/releases in this static-site directory') args = p.parse_args() + if os.geteuid() == 0: + p.error('v2 OS-permission negative control requires a non-root user') + base_version = '0.5.0-io-v2' + args.version = args.version or base_version + demo = 'demos/package-inspector' + releases = RELEASES metadata = verify() tricu, oracle, root = args.tricu.resolve(), args.oracle.resolve(), args.directory.resolve() root.mkdir(parents=True, exist_ok=False) producer, tools, application = root / 'producer', root / 'tools', root / 'application' workspace, consumer = producer / 'workspace', root / 'consumer' workspace.mkdir(parents=True); consumer.mkdir() - report = dict(complete=False, compiler_sha256=metadata['sha256'], sources={}, commands=[], releases={}, cases=[]) + # Isolate generated sibling sources from any enclosing checkout workspace. + for selected in (workspace, consumer): + (selected / "tricu.workspace").write_text("tricu.workspace.v1\n") + report = dict(complete=False, io_version=args.io_version, compiler_io_version=metadata['io_version'], compiler_sha256=metadata['sha256'], sources={}, commands=[], releases={}, cases=[]) started = time.monotonic() def record(): (root / 'report.json').write_text(json.dumps(report, indent=2) + '\n') @@ -162,17 +175,18 @@ def main(): return re.search(r'Created module alias .* -> ([0-9a-f]{64})', output)[1] compiler = imported(ROOT / metadata['bundle'], tools) report['compiler_manifest'] = compiler - options = ['--toolchain', compiler, '--tool-store', tools, '--compile-timeout', '900'] + options = ['--toolchain', compiler, '--toolchain-io-version', str(metadata['io_version']), '--tool-store', tools, '--compile-timeout', '900'] sources = {row['alias']: row['path'] for row in metadata['sources']} sources.update(binary='lib/binary.tri', arboricx='lib/arboricx/arboricx.tri', arboricx_common='lib/arboricx/common.tri', arboricx_manifest='lib/arboricx/manifest.tri', arboricx_nodes='lib/arboricx/nodes.tri') - sources.update({alias: path for _, _, alias, path in RELEASES}) + sources.update({alias: path for _, _, alias, path in releases}) + sources.update(io_v2='lib/io-v2.tri', io_v2_storage='lib/io-v2/storage.tri', io_v2_text='lib/io-v2/text.tri') for alias, path in sources.items(): data = (ROOT / path).read_bytes(); (workspace / (alias + '.tri')).write_bytes(data) report['sources'][path] = hashlib.sha256(data).hexdigest() registry = root / 'registry' - for name, binding, alias, _ in RELEASES: + for name, binding, alias, _ in releases: bundle = producer / (name + '.arboricx') print('Compiling publication:', name, flush=True) run('arboricx', 'compile', '--file', workspace / (alias + '.tri'), '--store', producer / 'store', @@ -204,9 +218,9 @@ def main(): config = consumer / 'registries' config.write_text('arboricx.registries.v1\nhome\thttp://127.0.0.1:' + str(server.server_port) + '\n') project_file, project_lock = consumer / 'tricu.project', consumer / 'tricu.lock' - project_source = (ROOT / 'demos/package-inspector/tricu.project').read_text() - project_file.write_text(project_source.replace('\t0.1.0\n', '\t' + args.version + '\n')) - report['sources']['demos/package-inspector/tricu.project'] = hashlib.sha256(project_source.encode()).hexdigest() + project_source = (ROOT / demo / 'tricu.project').read_text() + project_file.write_text(project_source.replace('\t' + base_version + '\n', '\t' + args.version + '\n')) + report['sources'][demo + '/tricu.project'] = hashlib.sha256(project_source.encode()).hexdigest() def project(action, *extra, success=True): return run('project', action, '--project', project_file, *(['--registries', config] if action in ['resolve', 'update', 'fetch'] else []), @@ -229,8 +243,8 @@ def main(): # Actual closed socket: subsequent operations cannot depend on the registry. project('resolve'); project('fetch'); project('bind'); project('check') assert not any(p.is_file() for p in (application / 'aliases').rglob('*')) - shutil.copyfile(ROOT / 'demos/package-inspector/main.tri', consumer / 'main.tri') - report['sources']['demos/package-inspector/main.tri'] = hashlib.sha256((consumer / 'main.tri').read_bytes()).hexdigest() + shutil.copyfile(ROOT / demo / 'main.tri', consumer / 'main.tri') + report['sources'][demo + '/main.tri'] = hashlib.sha256((consumer / 'main.tri').read_bytes()).hexdigest() assert sorted(p.name for p in consumer.glob('*.tri')) == ['fileio.tri', 'foundation.tri', 'inspection.tri', 'main.tri'] print('Compiling consumer with the registry stopped', flush=True) bundle = root / 'inspector.arboricx' @@ -238,9 +252,11 @@ def main(): *options, seconds=930) inspector = imported(bundle, application) report['inspector_manifest'] = inspector - def inspect(path, good=True, granted=True): + def inspect(path, good=True, flags=None): + if flags is None: + flags = [] return run('arboricx', 'run', '--store', application, '--module', inspector, '--name', 'main', - '--arg', path, '--io', '--no-result', *(['--allow-read', path] if granted else []), success=good, seconds=180) + '--arg', path, '--io', '--no-result', *flags, success=good, seconds=180) inputs = cases(root / 'cases') # A real executable IO export would abort if the inspector ran it. shutil.copyfile(consumer / 'fileio.tri', root / 'cases/fileio.tri') @@ -261,7 +277,25 @@ def main(): else: assert 'arboricx.inspect.v1' not in actual, (label, actual) report['cases'].append(dict(label=label, valid=good, report=actual)); record() - inspect(root / 'cases/id.arboricx', False, False) + target = root / 'cases/id.arboricx' + inspect(target, False, ['--allow-read', target]) + inspect(target, False, ['--unsafe-io']) + denied = root / 'cases/os-denied.arboricx' + shutil.copyfile(target, denied) + denied.chmod(0) + try: + inspect(denied, False) + finally: + denied.chmod(0o600) + report['os_permission_denial_checked'] = True + report['v1_grants_rejected'] = True + oversized = root / 'cases/over-budget.arboricx' + data = target.read_bytes() + oversized.write_bytes(data + b'\0' * (1048577-len(data))) + assert 'arboricx.inspect.v1' not in inspect(oversized, False) + report['explicit_byte_budget_checked'] = True + + inspect(root / 'cases/missing.arboricx', False) # Deliberate one-dependency update: add a public constant to the foundation # interface, preserving the three operations used by the authored consumer. diff --git a/test/native_indexed_store.py b/test/native_indexed_store.py @@ -1,26 +1,13 @@ #!/usr/bin/env python3 -"""Opt-in current native object/manifest policy checks with retained compiler. - -No generations or fixture publication. Narrow support transport is completely -hash-audited; expected object and manifest bytes are authored independently. -""" +"""Independent indexed wire oracle; historical v1 source-splicing CLI is retired.""" import argparse import hashlib -import json from pathlib import Path -import re import struct -from collections_batch import ProbeRunner -from bundle_dag import read -from executable_object import INDEXED, indexed_manifest, verify_payload -from http_headers import LEAF, byte_tree, pair, result, number, tree_list -from source_order import end +from executable_object import indexed_manifest from stable_bundle import assembled -ROOT = Path(__file__).resolve().parent.parent - - def digest(domain, payload): return hashlib.sha256(domain.encode() + b'\0' + payload).hexdigest() @@ -53,107 +40,8 @@ def main(): parser.add_argument('--generation', type=Path, required=True) parser.add_argument('--compiler', type=Path, required=True) parser.add_argument('--directory', type=Path, required=True) - args = parser.parse_args() - root = args.directory.resolve(); root.mkdir(parents=True) - runner = ProbeRunner(args.tricu, root, 1500, memory_mib=8192) - old = args.generation.resolve() / 'cas' - object_source = (ROOT / 'lib/source/object.tri').read_text() - module_source = (ROOT / 'lib/source/module.tri').read_text() - body = object_source.replace('N.lowerHex', 'nativeLowerHex') + '\n' + module_source[:module_source.index('sourceModulePublish root')] - body += module_source[module_source.index('\nsourceModulePublishNames root'):module_source.index('-- One native header interpretation')] - body = '\n'.join(line for line in body.splitlines() if not line.startswith(('!import', '!export'))) - constructor_names = ('readIndexedTree', 'hashIndexedTree', 'writeIndexedTreeIfAbsent') - constructors = [line for line in (ROOT / 'lib/io.tri').read_text().splitlines() - if any(line.startswith(name + ' =') for name in constructor_names)] - assert sorted(line.split()[0] for line in constructors) == sorted(constructor_names) - body = '\n'.join(constructors) + '\n' + body - wrapper = ''' -publishProbe root tree = io (sourceModulePublishNames root [(pair "value" tree)] ["value"]) -readProbe root manifest = io (sourceModuleLoadSelected root manifest "value") -decodeProbe bytes = sourceModuleDecode bytes -''' - local = set(re.findall(r'^([A-Za-z_][A-Za-z0-9_?\-]*)[^\n=]*=', body, re.M)) - words = set(re.findall(r'[A-Za-z_][A-Za-z0-9_?\-]*', body + wrapper)) - selected = {} - for module in ['prelude', 'io', 'source_resolve', 'source_lexer', 'parser', 'native_math']: - pin = (old / 'cache/source-build' / module).read_text().split('\t')[2] - payload = (old / 'objects' / pin[:3] / pin).read_bytes() - assert digest('arboricx.module-manifest.v1', payload) == pin - for row in payload.decode().splitlines()[1:]: - _, name, kind, tree, abi = row.split('\t') - assert kind == 'arboricx.tree-term.v1' and abi == 'arboricx.abi.tree.v1' - if module == 'native_math' and name != 'lowerHex': continue - alias = 'nativeLowerHex' if module == 'native_math' else name - if alias in words and alias not in local: - selected[alias] = tree - command = ['export', '--store', old, '--output', root / 'support.arboricx'] - for name, tree in selected.items(): command += ['--target', tree, '--name', name] - runner.run('support-export', command, 180) - _, _, nodes, exports, _ = read(root / 'support.arboricx') - assert set(exports) == set(selected) - sizes = [] - for node in nodes: sizes.append(1 + sum(sizes[child] for child in node[1:])) - expanded = sum(sizes[index] for index in exports.values()) - assert expanded <= 64 * 1024 * 1024, 'support exceeds unchanged flat transport budget' - reply = runner.run('support-import', ['import', '--file', root / 'support.arboricx', '--store', runner.store, '--flat'], 180) - support = re.search(r'Created module alias .* -> ([a-f0-9]{64})', reply)[1] - payload = (runner.store / 'objects' / support[:3] / support).read_bytes() - assert digest('arboricx.module-manifest.v1', payload) == support - entries = [row.split('\t') for row in payload.decode().splitlines()[1:]] - assert len(entries) == len(selected) and {row[1]: row[3] for row in entries} == selected - for pin in set(selected.values()): - assert digest('arboricx.tree-term.v1', (runner.store / 'objects' / pin[:3] / pin).read_bytes()) == pin - (root / 'support.tri').write_text(f'!export * from {support}\n') - (root / 'driver.tri').write_text('!import support *\n!export publishProbe\n!export readProbe\n!export decodeProbe\n' + body + wrapper) - compiler = runner.imported(args.compiler, 'compiler-import') - runner.run('compile', ['compile', '--file', root / 'driver.tri', '--toolchain', compiler, - '--tool-store', runner.store, '--store', runner.store, - '--output', root / 'driver.arboricx', '--compile-timeout', '900'], 900) - pin = runner.imported(root / 'driver.arboricx', 'driver-import') - checks = [] - def invoke(label, name, values, io=True): - options = ['--io', '--allow-read', root, '--allow-write', root] if io else [] - for index, value in enumerate(values): - path = root / f'arg-{index}.tree'; path.write_bytes(value) - options += ['--arg-tree-file', path] - output = runner.run(label, ['run', '--module', pin, '--name', name, '--store', runner.store, - '--format', 'ternary', *options], 180) - raw = bytes(int(c) for c in re.search(r'(?m)^([012]+)', output)[1]) - return raw[:end(raw)] - values = [LEAF, b'\1\0', pair(b'\1\0', b'\2\0\0'), number(42)] - for index, tree in enumerate(values): - store = root / f'native-{index}' - path_arg = byte_tree(str(store).encode()) - payload = indexed_bytes(tree); verify_payload(INDEXED, payload) - tree_pin = digest(INDEXED, payload) - manifest = f'arboricx.module-manifest.v1\nexport\tvalue\t{INDEXED}\t{tree_pin}\tarboricx.abi.tree.v1\n'.encode() - manifest_pin = digest('arboricx.module-manifest.v1', manifest) - expected = result(True, byte_tree(manifest_pin.encode()), LEAF) - assert invoke(f'publish-{index}', 'publishProbe', [path_arg, tree]) == expected - assert (store / 'objects' / tree_pin[:3] / tree_pin).read_bytes() == payload - assert (store / 'objects' / manifest_pin[:3] / manifest_pin).read_bytes() == manifest - assert invoke(f'repeat-{index}', 'publishProbe', [path_arg, tree]) == expected - assert invoke(f'read-{index}', 'readProbe', [path_arg, byte_tree(manifest_pin.encode())]) == result(True, tree, LEAF) - for mode in ['module', 'target']: - output = root / f'export-{index}-{mode}.arboricx' - runner.run(f'export-{index}-{mode}', ['export', '--store', store, - '--' + mode, manifest_pin if mode == 'module' else tree_pin, '--name', 'value', '--output', output], 180) - _, _, actual_nodes, exports, _ = read(output) - _, _, expected_nodes, expected_exports, _ = read(store / 'objects' / tree_pin[:3] / tree_pin) - assert actual_nodes == expected_nodes and exports == {'value': expected_exports['root']} - loaded = runner.imported(output, f'reimport-{index}-{mode}') - answer = runner.run(f'execute-{index}-{mode}', ['run', '--module', loaded, '--name', 'value', - '--store', runner.store, '--format', 'ternary'], 180) - assert re.search(r'(?m)^([012]+)', answer)[1] == ''.join(map(str, tree)) - old = manifest.replace(INDEXED.encode(), b'arboricx.tree-term.v1') - assert invoke(f'old-kind-{index}', 'decodeProbe', [byte_tree(old)], False) == result(False, byte_tree(b'unsupported export kind'), LEAF) - (store / 'objects' / tree_pin[:3] / tree_pin).write_bytes(b'corrupt') - assert invoke(f'corrupt-read-{index}', 'readProbe', [path_arg, byte_tree(manifest_pin.encode())]) == result(False, byte_tree(b'CAS digest mismatch'), LEAF) - assert invoke(f'corrupt-write-{index}', 'publishProbe', [path_arg, tree]) == result(False, byte_tree(b'CAS object exists with different bytes'), LEAF) - checks.append(index) - (root / 'result.json').write_text(json.dumps(dict(complete=True, checked_values=checks, - support_exports=len(selected), expanded_support_bytes=expanded), indent=2) + '\n') - print('Native indexed publication/read/manifest/host-export and corruption controls passed.') + parser.parse_args() + parser.error("Retired v1 source-splicing probe; use io_v2_indexed.py, io_v2_indexed_write.py and io_v2_compiler_store.py with the current compiler.") if __name__ == '__main__': main() diff --git a/test/native_library_migration.py b/test/native_library_migration.py @@ -15,10 +15,12 @@ import signal import subprocess import time from process_rss import ChildRSS +from generation_io import cli_version from generation_backend import BACKENDS, DEFAULT_BACKEND REPO = Path(__file__).resolve().parent.parent LIBRARIES = { + 'io': 'lib/io.tri', 'socket': 'lib/socket.tri', 'binary': 'lib/binary.tri', 'parser': 'lib/parser.tri', 'patterns': 'lib/patterns.tri', 'intensional': 'lib/intensionalContracts.tri', 'http': 'lib/http.tri', @@ -46,6 +48,22 @@ for path in sorted((REPO / 'demos').glob('*.tri')) + sorted((REPO / 'demos/inter LIBRARIES['demos.' + '.'.join(path.relative_to(REPO / 'demos').with_suffix('').parts)] = str(path.relative_to(REPO)) +# Explicit IO-v2 support-cohort additions; aliases match authored workspace imports. +IO_V2_SUPPORT = { + 'arboricx_handlers': 'lib/arboricx/handlers.tri', + 'arboricx_streaming_v2': 'lib/arboricx/streaming-v2.tri', + 'handler_artifacts': 'lib/handlers/artifacts.tri', + 'handler_log': 'lib/handlers/log.tri', + 'handlers': 'lib/handlers.tri', + 'io_v2': 'lib/io-v2.tri', + 'io_v2_artifacts': 'lib/io-v2/artifacts.tri', + 'io_v2_http': 'lib/io-v2/http-transport.tri', + 'io_v2_storage': 'lib/io-v2/storage.tri', + 'io_v2_text': 'lib/io-v2/text.tri', +} +LIBRARIES.update(IO_V2_SUPPORT) + + def alias(name): return name.replace('.', '_').replace('-', '_') @@ -114,10 +132,37 @@ def checked_candidate_source(workspace, record, source_root=REPO): return staged +def dependency_sources(source_root, entries, reserved): + """Explicit source inputs, never facades overriding authenticated providers.""" + records, seen = [], set(reserved) + source_root = source_root.resolve() + for entry in entries: + name, separator, relative = entry.partition('=') + path = Path(relative) + if (not separator or not re.fullmatch(r'[A-Za-z_][A-Za-z0-9_-]*', name) or + name in seen or path.is_absolute() or '..' in path.parts or + not relative or not (source_root / path).resolve().is_relative_to(source_root)): + raise ValueError('invalid/duplicate source dependency: ' + entry) + payload = (source_root / path).read_bytes() + records.append(dict(alias=name, path=relative, after_sha256=hashlib.sha256(payload).hexdigest())) + seen.add(name) + return records + + +def check_source_dependencies(directory, source_root): + selection = json.loads((directory / 'selection.json').read_text()) + for record in selection.get('source_dependencies', []): + checked_candidate_source(directory / 'workspace', record, source_root) + + def main(): p = argparse.ArgumentParser(description=__doc__) p.add_argument('tricu', type=Path) - p.add_argument('generation', type=Path) + p.add_argument('generation', type=Path, nargs='?', help='completed generation; omit only with explicit compiler bundle/providers') + p.add_argument('--compiler-bundle', type=Path) + p.add_argument('--compiler-providers', type=Path, help='explicit namespaced compiler provider bundle, not a fabricated generation') + p.add_argument('--compiler-io-version', type=cli_version, choices=(1,2)) + p.add_argument('--source-root', type=Path, default=REPO) p.add_argument('directory', type=Path) p.add_argument('--library', action='append', choices=list(LIBRARIES), required=True) p.add_argument('--terminal-export', action='append', default=[], metavar='LIBRARY:SYMBOL', help='explicitly remove a reviewed bare terminal selector; keep/export its definition') @@ -131,6 +176,8 @@ def main(): p.add_argument('--capabilities', type=int, default=1, help='GHC capabilities; select eight for a full support rebuild') p.add_argument('--resume', action='store_true', help='reuse only unchanged staged candidates/checkpoints') p.add_argument('--dependencies', action='append', type=Path, default=[], help='verified completed candidate directories supplying full dependency manifests') + p.add_argument('--source-dependency', action='append', default=[], metavar='ALIAS=PATH', + help='explicit additional authored provider; no compiler/support alias overrides') args = p.parse_args() if not 1 <= args.seconds <= 7200 or not 1 <= args.batch_seconds <= 7200 or not 0 < args.memory_mib <= 65536 or args.capabilities < 1: p.error('jobs must be bounded within 64 GiB and two hours') @@ -142,8 +189,20 @@ def main(): p.error('resume directory is missing') else: root.mkdir(exist_ok=False, parents=True) - generation = args.generation.resolve() - assert json.loads((generation / 'status.json').read_text())['complete'] + from generation_io import versions, compiler_flags + generation = args.generation.resolve() if args.generation else None + source_root = args.source_root.resolve() + if generation: + if args.compiler_bundle or args.compiler_providers: + p.error('select generation or explicit compiler/provider bundles, not both') + assert json.loads((generation / 'status.json').read_text())['complete'] + io_version = versions(json.loads((generation / 'inputs.json').read_text()))[1] + else: + if not args.compiler_bundle or not args.compiler_providers: + p.error('explicit --compiler-bundle and --compiler-providers required without generation') + io_version = args.compiler_io_version or 1 + if args.compiler_io_version is not None and args.compiler_io_version != io_version: + p.error('compiler IO override disagrees with generation metadata') events = json.loads((root / 'events.json').read_text()) if args.resume else [] deadline = time.monotonic() + args.batch_seconds @@ -191,12 +250,26 @@ def main(): workspace.mkdir(exist_ok=args.resume); store.mkdir(exist_ok=args.resume) # Staged sources/facades must not inherit the caller's checkout map. (workspace / 'tricu.workspace').write_text('tricu.workspace.v1\n') - bundle = generation / ('build-stable.arboricx' if args.backend == 'stable' else 'build.arboricx') + bundle = generation / ('build-stable.arboricx' if args.backend == 'stable' else 'build.arboricx') if generation else args.compiler_bundle.resolve() + map_path = generation / 'source-map.v1' if generation else source_root / 'bootstrap/compiler-source-map.v1' + map_bytes = map_path.read_bytes() + selection = dict(backend=args.backend, map_sha256=hashlib.sha256(map_bytes).hexdigest(), compiler_sha256=hashlib.sha256(bundle.read_bytes()).hexdigest(), + compiler_io_version=io_version, source_root=str(source_root), + providers_sha256=hashlib.sha256(args.compiler_providers.read_bytes()).hexdigest() if args.compiler_providers else None) + mapped_names = {row.split('\t')[1] for row in map_bytes.decode().splitlines()[1:]} + source_dependencies = dependency_sources(source_root, args.source_dependency, mapped_names | {alias(name) for name in LIBRARIES}) + if source_dependencies: + selection['source_dependencies'] = source_dependencies + selection_file = root / 'selection.json' + if args.resume and (not selection_file.exists() or json.loads(selection_file.read_text()) != selection): + raise ValueError('resume compiler/provider/source selection changed') + selection_file.write_text(json.dumps(selection, indent=2)+'\n') output = run(['arboricx', 'import', '--file', bundle, '--store', tools, '--indexed']) compiler = re.search(r'Created module alias .* -> ([0-9a-f]{64})', output)[1] - rows = [line.split('\t') for line in (generation / 'source-map.v1').read_text().splitlines()[1:]] + rows = [line.split('\t') for line in map_bytes.decode().splitlines()[1:]] names = {legacy: mapped for _, mapped, legacy, _, _ in rows} names.update({name: alias(name) for name in LIBRARIES}) + names.update({record['alias']: record['alias'] for record in source_dependencies}) names.update({ 'STAGING_EXPERIMENT_MANIFEST': alias('source.staging-experiment'), 'STAGED_LIST_EXPERIMENT_MANIFEST': alias('source.staged-list-experiment'), @@ -210,9 +283,20 @@ def main(): selectors[name] = symbol records = [] needed = set() + for record in source_dependencies: + payload = (source_root / record['path']).read_bytes() + candidate, _ = native_candidate(payload, names) + if candidate != payload: + raise ValueError('source dependency must already be a native module') + from native_header import native_header + needed.update(native_header(payload.decode())[0]) + staged = workspace / (record['alias'] + '.tri') + if args.resume and staged.read_bytes() != payload: + raise ValueError('resume source dependency changed') + staged.write_bytes(payload) # Stage candidates before compiling so their declared edges are resolvable. for name in args.library: - raw = (REPO / LIBRARIES[name]).read_bytes() + raw = (source_root / LIBRARIES[name]).read_bytes() # Legacy patterns imports exposed prelude transitively. Native patterns # owns only its declared exports; this demo uses equal? directly. extra = [(names['prelude'], '!Local')] if name == 'demos.patternMatching' else [] @@ -238,10 +322,23 @@ def main(): raise ValueError('resume membership or inputs changed') records = previous (root / 'migration.json').write_text(json.dumps(records, indent=2) + '\n') - cas = generation / 'cas' + cas = generation / 'cas' if generation else None for _, mapped, _, _, _ in rows: if mapped not in needed or mapped in {record['alias'] for record in records}: continue + if cas is None: + from bootstrap_archive import select_exports + from bundle_dag import read + provider = root / ('provider-' + mapped + '.arboricx') + exports = read(args.compiler_providers)[3] + selected = [(key, key.split('.', 1)[1]) for key in exports if key.startswith(mapped + '.')] + if not selected: + raise ValueError('missing complete compiler provider: ' + mapped) + select_exports(args.compiler_providers, provider, selected) + reply = run(['arboricx', 'import', '--file', provider, '--store', store, '--indexed']) + pin = re.search(r'Created module alias .* -> ([0-9a-f]{64})', reply)[1] + (workspace / (mapped + '.tri')).write_text('!export * from ' + pin + '\n') + continue pin = (cas / 'cache/source-build' / mapped).read_text().split('\t')[2] payload = (cas / 'objects' / pin[:3] / pin).read_bytes() assert digest('arboricx.module-manifest.v1', payload) == pin @@ -258,7 +355,7 @@ def main(): mapped = dependency['alias'] if mapped not in needed or mapped in [r['alias'] for r in records]: continue - source = (REPO / dependency['path']).read_bytes() + source = (source_root / dependency['path']).read_bytes() if not dependency['complete'] or hashlib.sha256(source).hexdigest() != dependency['after_sha256']: raise ValueError('stale/incomplete dependency candidate: ' + mapped) pin = dependency['manifest'] @@ -274,12 +371,12 @@ def main(): failures = [] for record in records: name = record['alias'] - checked_candidate_source(workspace, record) + checked_candidate_source(workspace, record, source_root) if record['complete']: continue try: run(['arboricx', 'compile', '--file', workspace / (name + '.tri'), - '--toolchain', compiler, '--tool-store', tools, '--store', store, + '--toolchain', compiler, *compiler_flags(io_version), '--tool-store', tools, '--store', store, '--output', root / (name + '.arboricx'), '--compile-timeout', str(args.seconds)]) except RuntimeError as error: if not args.keep_going: @@ -293,10 +390,18 @@ def main(): assert digest('arboricx.module-manifest.v1', manifest) == checkpoint[2] assert digest('arboricx.build-receipt.v1', receipt) == checkpoint[3] assert 'source\t' + digest('arboricx.source.v1', (workspace / (name + '.tri')).read_bytes()) in receipt.decode().splitlines() - checked_candidate_source(workspace, record) + checked_candidate_source(workspace, record, source_root) record.update(complete=True, manifest=checkpoint[2], receipt=checkpoint[3]) (root / 'migration.json').write_text(json.dumps(records, indent=2) + '\n') print('Completed:', name, flush=True) + for record in records: + checked_candidate_source(workspace, record, source_root) + check_source_dependencies(root, source_root) + if map_path.read_bytes() != map_bytes: + raise ValueError('source map changed during preparation') + if hashlib.sha256(bundle.read_bytes()).hexdigest() != selection['compiler_sha256'] or (args.compiler_providers and + hashlib.sha256(args.compiler_providers.read_bytes()).hexdigest() != selection['providers_sha256']): + raise ValueError('compiler/provider inputs changed during preparation') if failures: raise SystemExit('Incomplete candidates: ' + ', '.join(failures)) diff --git a/test/native_source_map_fixture.py b/test/native_source_map_fixture.py @@ -2,7 +2,7 @@ """Focused Haskell-only native nested-map staging/build; no compiler source build.""" import argparse import json -import os +import shutil from pathlib import Path import re from research_session import ResearchSession, ROOT @@ -13,7 +13,10 @@ parser.add_argument('generation', type=Path) parser.add_argument('--directory', type=Path, required=True) args = parser.parse_args() generation = args.generation.resolve() -if not json.loads((generation / 'status.json').read_text())['complete']: +status = json.loads((generation / 'status.json').read_text()) +if status.get('produced_io_version', 1) != 2: + parser.error('current probe requires a v2 generation; use its frozen probe/runtime for v1') +if not status['complete']: raise ValueError('incomplete generation') session = ResearchSession(args.tricu, args.directory, [], generation / 'build-stable.arboricx') source_cas = generation / 'cas' @@ -26,7 +29,7 @@ exports = {line.split('\t')[1]: line.split('\t')[3] for line in manifest.decode( tree = exports['sourceMapStage'] target = session.store / 'objects' / tree[:3] / tree target.parent.mkdir(parents=True, exist_ok=True) -if not target.exists(): os.link(source_cas / 'objects' / tree[:3] / tree, target) +if not target.exists(): shutil.copyfile(source_cas / 'objects' / tree[:3] / tree, target) bundle = session.root / 'stage.arboricx' session.run([session.tricu, 'arboricx', 'export', '--store', session.store, '--target', tree, '--name', 'stage', '--output', bundle]) @@ -36,14 +39,14 @@ module = re.search(rb'Created module alias .* -> ([a-f0-9]{64})', output)[1].dec overlay = session.root / 'overlay' map_bytes = (ROOT / 'demos/source-map/map.v1').read_text() # Stored IO execution expects the io envelope, not a raw native IO action. -io_pin = (source_cas / 'cache/source-build/io').read_text().split('\t')[2] +io_pin = (source_cas / 'cache/source-build/compiler_io').read_text().split('\t')[2] io_payload = (source_cas / 'objects' / io_pin[:3] / io_pin).read_bytes() if digest('arboricx.module-manifest.v1', io_payload) != io_pin: raise ValueError('corrupt io manifest') for pin in {io_pin, *[line.split('\t')[3] for line in io_payload.decode().splitlines()[1:]]}: target = session.store / 'objects' / pin[:3] / pin target.parent.mkdir(parents=True, exist_ok=True) - if not target.exists(): os.link(source_cas / 'objects' / pin[:3] / pin, target) + if not target.exists(): shutil.copyfile(source_cas / 'objects' / pin[:3] / pin, target) driver = session.workspace / 'stage_driver.tri' driver.write_text(f'!import {module} as M\n!import {io_pin} as I\n!export main\n' 'main source overlay cas text = I.io (M.stage source overlay cas text)\n') @@ -54,7 +57,7 @@ output, _ = session.run([session.tricu, 'arboricx', 'import', '--store', session module = re.search(rb'Created module alias .* -> ([a-f0-9]{64})', output)[1].decode() session.run([session.tricu, 'arboricx', 'run', '--store', session.store, '--module', module, '--name', 'main', '--arg', str(ROOT), '--arg', str(overlay), '--arg', str(session.store), - '--arg', map_bytes, '--io', '--unsafe-io', '--format', 'decode']) + '--arg', map_bytes, '--io', '--format', 'decode']) for name, path in [('base', 'demos/source-map/base.tri'), ('prelude', 'demos/source-map/nested/prelude.tri'), ('app', 'demos/source-map/nested/app.tri')]: diff --git a/test/native_source_utf8.py b/test/native_source_utf8.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 -"""Opt-in source-boundary candidate check; never publishes fixtures/generations. +"""Current IO v2 source-boundary check with independent byte/token oracles. -Compile current build/map decoding consumers and IO constructors against explicitly -selected retained support. The support is executable rollback, not current-source -compiler acceptance. Every smoke uses the edited Haskell host and fresh files. +Compile complete workspace modules through the selected portable compiler, not +source-spliced consumers bound to retained v1 support. No fixture publication. """ import argparse import hashlib @@ -20,70 +19,24 @@ ROOT = Path(__file__).resolve().parent.parent def main(): p = argparse.ArgumentParser(description=__doc__) p.add_argument('tricu', type=Path) - p.add_argument('--generation', type=Path, required=True) p.add_argument('--compiler', type=Path, required=True) p.add_argument('--directory', type=Path, required=True) args = p.parse_args() root = args.directory.resolve() root.mkdir(parents=True) runner = ProbeRunner(args.tricu, root, 1500, memory_mib=8192) - old = args.generation.resolve() / 'cas' - parts = [] - for name in ['build', 'map']: - text = (ROOT / 'lib/source' / (name + '.tri')).read_text() - if name == 'build': - text = text[text.index('sourceBuildStage source'):text.index('-- Lock rewriting')] + text[text.index('sourceBuildReadTriPath path'):text.index('sourceBuildReadTri workspace')] - else: - text = text[:text.index('sourceMapStageRows rows')] - parts.append('\n'.join(line for line in text.splitlines() - if not line.startswith(('!import', '!export')))) - body = '\n'.join(parts) - constructors = '\n'.join(line for line in (ROOT / 'lib/io.tri').read_text().splitlines() - if line.startswith(('readSourceFile =', 'encodeSourceText ='))) - body = constructors + '\n' + body - local = set(re.findall(r'^([A-Za-z_][A-Za-z0-9_?\-]*)[^\n=]*=', body, re.M)) - wrapper = ''' + source = """!import prelude * +!import compiler_io * +!import source_build * +!import source_map * +!import source_lexer * +!export readProbe +!export mapProbe +!export lexProbe readProbe path = io (sourceBuildReadTriPath path) mapProbe sourceRoot overlayRoot row = io (sourceMapStageOne sourceRoot overlayRoot [] row) lexProbe path = io (onOk_ (readSourceFile path) (source : pure (lexGateSource (snd source)))) -''' - words = set(re.findall(r'[A-Za-z_][A-Za-z0-9_?\-]*', body + wrapper)) - selected = {} - modules = ['prelude', 'io', 'source_object', 'source_module', 'source_lock', - 'source_bundle', 'source_resolve', 'source_unit', 'source_lexer', 'parser'] - for module in modules: - pin = (old / 'cache/source-build' / module).read_text().split('\t')[2] - raw = (old / 'objects' / pin[:3] / pin).read_bytes() - assert hashlib.sha256(b'arboricx.module-manifest.v1\0' + raw).hexdigest() == pin - for row in raw.decode().splitlines()[1:]: - _, name, _, tree, _ = row.split('\t') - if name in words and name not in local: - selected[name] = tree - command = ['export', '--store', old, '--output', root / 'support.arboricx'] - for name, tree in selected.items(): - command += ['--target', tree, '--name', name] - runner.run('support-export', command, 180) - from bundle_dag import read - _, _, nodes, exports, _ = read(root / 'support.arboricx') - assert set(exports) == set(selected), 'complete support export set' - sizes = [] - for node in nodes: - sizes.append(1 + sum(sizes[child] for child in node[1:])) - expanded_bytes = sum(sizes[index] for index in exports.values()) - support_reply = runner.run('support-import', ['import', '--file', root / 'support.arboricx', '--store', runner.store, '--indexed'], 180) - support = re.search(r'Created module alias .* -> ([0-9a-f]{64})', support_reply)[1] - manifest = (runner.store / 'objects' / support[:3] / support).read_bytes() - assert hashlib.sha256(b'arboricx.module-manifest.v1\0' + manifest).hexdigest() == support - entries = [line.split('\t') for line in manifest.decode().splitlines()[1:]] - assert {fields[1]: fields[3] for fields in entries} == selected - assert len(entries) == len(selected) - from executable_object import verify_payload - for _, name, kind, tree, abi in entries: - raw = (runner.store / 'objects' / tree[:3] / tree).read_bytes() - verify_payload(kind, raw) - assert hashlib.sha256(kind.encode() + b'\0' + raw).hexdigest() == tree - (root / 'support.tri').write_text(f'!export * from {support}\n') - source = '!import support *\n!export readProbe\n!export mapProbe\n!export lexProbe\n' + body + wrapper +""" (root / 'driver.tri').write_text(source) compiler = runner.imported(args.compiler, 'compiler-import') runner.run('compile', ['compile', '--file', root / 'driver.tri', '--store', runner.store, @@ -95,7 +48,7 @@ lexProbe path = io (onOk_ (readSourceFile path) (source : pure (lexGateSource (s checks = [] def invoke(label, name, values): - argv = ['--io', '--allow-read', root, '--allow-write', root] + argv = ['--io'] for i, value in enumerate(values): path = root / f'arg-{i}.tree' path.write_bytes(value) @@ -173,8 +126,7 @@ lexProbe path = io (onOk_ (readSourceFile path) (source : pure (lexGateSource (s assert (overlay / 'sample.tri').read_bytes() == before checks.append(label + ': read and map rejected without overlay writes') report = dict(complete=True, checks=checks, compiler_sha256=hashlib.sha256(args.compiler.read_bytes()).hexdigest(), - source_sha256=hashlib.sha256(source.encode()).hexdigest(), support=selected, - support_expanded_bytes=expanded_bytes, support_manifest=support, + source_sha256=hashlib.sha256(source.encode()).hexdigest(), io_version=2, limits=dict(memory_mib=8192, job_seconds=1500, compile_seconds=900, smoke_seconds=180), scope='candidate read/stage/map boundary; not generation acceptance') (root / 'report.json').write_text(json.dumps(report, indent=2) + '\n') diff --git a/test/package_registry.py b/test/package_registry.py @@ -26,7 +26,7 @@ def main(): p = argparse.ArgumentParser(description=__doc__) p.add_argument('tricu', type=Path) group = p.add_mutually_exclusive_group() - group.add_argument('--native-server', type=Path) + group.add_argument('--native-server', type=Path, help='IO v2 server bundle exporting main root port') group.add_argument('--prepare-server', type=Path, help='Compile current native server into a new retained directory') args = p.parse_args() tricu = args.tricu.resolve() @@ -36,15 +36,19 @@ def main(): workspace = prepared / 'workspace' workspace.mkdir() metadata = json.loads((ROOT / 'bootstrap/stable-compiler.json').read_text()) - sources = {row['alias']: row['path'] for row in metadata['sources']} - sources.update(http='lib/http.tri', socket='lib/socket.tri', arboricx_server='lib/arboricx/server.tri') + # Freeze workspace inputs; the native compiler resolves the actual closure. + sources = dict(line.split('\t') for line in + (ROOT / 'tricu.workspace').read_text().splitlines()[1:]) hashes = {} for alias, path in sources.items(): data = (ROOT / path).read_bytes() (workspace / (alias + '.tri')).write_bytes(data) hashes[path] = hashlib.sha256(data).hexdigest() - (workspace / 'main.tri').write_text('!import io *\n!import arboricx_server as S\n!export main\n' - 'main root port = io (S.arboricxServer root "127.0.0.1" port)\n') + (workspace / 'main.tri').write_text( + '!import io_v2 as IO\n!import arboricx_streaming_v2 as Server\n' + '!import arboricx_handlers as Registry\n!export main\n' + 'main root port = IO.io (Server.serve root 1048576 Registry.stdoutLogV2 ' + '(IO.ipv4 [127 0 0 1] port) 16 (_ : IO.pure t))\n') args.native_server = prepared / 'server.arboricx' command = [str(tricu), 'arboricx', 'compile', '--file', str(workspace / 'main.tri'), '--store', str(prepared / 'store'), '--output', str(args.native_server), @@ -212,7 +216,7 @@ def main(): log = open(root / 'server.log', 'w+') proc = subprocess.Popen([str(tricu), 'arboricx', 'run', '--module', pin, '--name', 'main', '--store', str(runtime), '--arg', str(registry), '--arg-tree-file', str(port_file), - '--io', '--allow-read', str(registry), '+RTS', '-N1', '-M2048m', '-RTS'], stdout=log, stderr=log) + '--io', '+RTS', '-N1', '-M2048m', '-RTS'], stdout=log, stderr=log) url = 'http://127.0.0.1:' + str(port) # Ignore environmental proxies for our local protocol probes. opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) diff --git a/test/portable_cli.py b/test/portable_cli.py @@ -3,12 +3,14 @@ import argparse import hashlib import json +import os import pathlib import re import subprocess import tempfile import time from generation_backend import BACKENDS, DEFAULT_BACKEND +from generation_io import cli_version, compiler_flags parser = argparse.ArgumentParser() parser.add_argument("tricu") @@ -16,6 +18,7 @@ parser.add_argument("--backend", choices=BACKENDS, default=DEFAULT_BACKEND, help="lowering backend (default: %(default)s)") parser.add_argument("--alternate-compiler-bundle", type=pathlib.Path, help="Check switching backend/cache identity using the other backend's artifact") +parser.add_argument("--compiler-io-version", type=cli_version, choices=(2,), default=2) parser.add_argument("--compiler-bundle", type=pathlib.Path, help="Reuse an explicitly prepared sourceBuildGraph bundle") transport = parser.add_mutually_exclusive_group() @@ -53,7 +56,7 @@ with tempfile.TemporaryDirectory(prefix="tricu-portable-cli-") as temporary: assert hashlib.sha256(compiler_domain.encode() + b"\0" + compiler_payload).hexdigest() == compiler_tree assert not args.indexed or len(compiler_payload) < 1024 * 1024 print(f"compiler CAS bytes={len(compiler_payload):,} kind={compiler_domain}", flush=True) - flags = ["--toolchain", manifest, "--tool-store", str(tools), "--store", str(store)] + flags = ["--toolchain", manifest, *compiler_flags(args.compiler_io_version), "--tool-store", str(tools), "--store", str(store)] patterns = workspace / "patterns.tri" app = workspace / "app.tri" patterns.write_text("!export first\n!export shape\n" @@ -140,7 +143,7 @@ with tempfile.TemporaryDirectory(prefix="tricu-portable-cli-") as temporary: "--store", str(tools), *indexed_flags], cwd=root) alternate_pin = re.search(r"Created module alias .* -> ([0-9a-f]{64})", alternate).group(1) alternate_tree = re.search(r"sourceBuildGraph -> ([0-9a-f]{64})", alternate).group(1) - alternate_flags = ["--toolchain", alternate_pin, "--tool-store", str(tools), "--store", str(store)] + alternate_flags = ["--toolchain", alternate_pin, *compiler_flags(args.compiler_io_version), "--tool-store", str(tools), "--store", str(store)] text, _ = run([tricu, "eval", str(app), *alternate_flags, "--format", "number"], cwd=root) assert text == "23" switched = (store / "cache" / "source-build" / "app").read_text().split("\t") @@ -264,16 +267,24 @@ with tempfile.TemporaryDirectory(prefix="tricu-portable-cli-") as temporary: run([tricu, "eval", str(app), "--toolchain", manifest, "--tool-store", str(root / "absent"), "--store", str(store)], cwd=root, ok=False, contains="Module manifest not found") - # Compiler IO permissions are not inherited by the application action. + # The compiler's ability to build a source does not confer OS read access + # to the resulting v2 application. No grant flags or implicit state slot. + if os.geteuid() == 0: + parser.error("OS-denial application control requires non-root") message = root / "message.txt" message.write_text("hello native") action = workspace / "action.tri" - action.write_text('!export main\nmain = (t "tricuIO" (t 1 (t 20 ' - + json.dumps(str(message)) + ')))\n') - denied, _ = run([tricu, "eval", str(action), *flags, "--io", "--format", "decode"], cwd=root) - assert "permission denied" in denied and "hello native" not in denied + action.write_text('!export main\nrequest group operation arguments = t 10 (t group (t 1 (t operation arguments)))\nread reader = t 1 (t (request "stream" "read" (t reader 64)) (reply : case reply of\n ((t t), (("data", bytes), t)) = t 0 bytes\n _ = t 0 reply))\nmain = t "tricuIO" (t 2 (t 3 (owner : t 1\n (t (request "file" "open-reader" (t owner PATH))\n (reply : case reply of\n ((t t), (reader, t)) = read reader\n _ = t 0 reply)))))\n'.replace('PATH', json.dumps(str(message)))) + message.chmod(0) + try: + denied, _ = run([tricu, "eval", str(action), *flags, "--io", "--format", "decode"], cwd=root) + assert "permission" in denied and "hello native" not in denied + finally: + message.chmod(0o600) + run([tricu, "eval", str(action), *flags, "--io", "--format", "decode"], cwd=root, + contains="hello native") run([tricu, "eval", str(action), *flags, "--io", "--allow-read", str(message), - "--format", "decode"], cwd=root, contains="hello native") + "--format", "decode"], cwd=root, ok=False, contains="permission grants are not accepted") run([tricu, "eval", str(action), *flags, "--compile-timeout", "0"], cwd=root, ok=False, contains="--compile-timeout must be") diff --git a/test/prepare_compiler_kit.py b/test/prepare_compiler_kit.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""Compile a source-mapped worker/adapter kit with an explicitly selected compiler. + +No retained flat support facade, inferred framing, fallback or generation claim. +The map and output IO version are caller assertions; actual generation execution +and independent acceptance remain separate gates. +""" +import argparse +import hashlib +import json +from pathlib import Path +import re +import time + +from bootstrap_generations import source_map +from bootstrap_inputs import load_kit +from generation_backend import BACKENDS, DEFAULT_BACKEND +from generation_io import cli_version, compiler_flags +from process_rss import run_guarded +from rts_metrics import parse_rts_stats + +ROOT = Path(__file__).resolve().parent.parent + + +def digest(data): + return hashlib.sha256(data).hexdigest() + + +def project(mapping, source_root, io_version, backend): + text, entries, _ = source_map(mapping, source_root, 'source_map') + io_alias = 'io_v2' if io_version == 2 else 'io' + if io_alias not in entries: + raise ValueError('selected map lacks adapter IO module: ' + io_alias) + sources = {alias: (source_root / relative).read_bytes() + for alias, (_, relative, _) in entries.items()} + entry = BACKENDS[backend][0] + worker = f'!import source_map as M\n!export {entry}\n{entry} = M.{entry}\n' + adapter = f'!import {io_alias} as IO\n!export main\nmain build source overlay store compiler mapping root = IO.io (build source overlay store compiler mapping root)\n' + return text, sources, worker, adapter + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tricu', type=Path) + parser.add_argument('output', type=Path) + parser.add_argument('--compiler-bundle', type=Path, required=True) + parser.add_argument('--compiler-io-version', type=cli_version, default=1) + parser.add_argument('--io-version', type=cli_version, required=True, help='declared worker/adapter output framing') + parser.add_argument('--map', type=Path, required=True) + parser.add_argument('--source-root', type=Path, default=ROOT) + parser.add_argument('--backend', choices=BACKENDS, default=DEFAULT_BACKEND) + parser.add_argument('--memory-mib', type=int, default=8192) + parser.add_argument('--seconds', type=float, default=1800) + args = parser.parse_args() + if not 0 < args.memory_mib <= 65536 or not 0 < args.seconds <= 7200: + parser.error('limits must be within 64 GiB and two hours') + start = time.monotonic() + runtime = args.tricu.resolve() + # Capture all source/compiler bytes before any output is created. Compilation + # uses only these copies, never a later re-read of mutable selected inputs. + text, sources, worker, adapter = project(args.map, args.source_root, args.io_version, args.backend) + compiler_bytes = args.compiler_bundle.read_bytes() + runtime_pin = digest(runtime.read_bytes()) + root = args.output.resolve(); root.mkdir(parents=True, exist_ok=False) + projection = root/'projection'; (projection/'inputs').mkdir(parents=True) + (root/'source-map.v1').write_text(text) + workspace = ['tricu.workspace.v1'] + for alias, payload in sources.items(): + target = 'inputs/' + alias + '.tri' + (projection/target).write_bytes(payload) + workspace.append(alias + '\t' + target) + (projection/'tricu.workspace').write_text('\n'.join(workspace)+'\n') + (projection/'worker.tri').write_text(worker) + (projection/'adapter.tri').write_text(adapter) + (root/'compiler.arboricx').write_bytes(compiler_bytes) + records = [] + + def run(label, command): + remaining = args.seconds - (time.monotonic()-start) + if remaining <= 0: + raise TimeoutError('kit preparation wall-time guard') + command = [str(runtime), 'arboricx', *map(str, command), '+RTS', '-N1', + '-M' + str(args.memory_mib) + 'm', '-s', '-RTS'] + began = time.monotonic() + reply, peak = run_guarded(command, memory_mib=args.memory_mib, seconds=remaining) + (root/(label+'.log')).write_text(reply) + records.append(dict(label=label, command=command, seconds=time.monotonic()-began, + peak_kib=peak, rts=parse_rts_stats(reply))) + return reply + + reply = run('import-compiler', ['import', '--file', root/'compiler.arboricx', + '--store', root/'tools', '--indexed']) + compiler = re.search(r'Created module alias .* -> ([0-9a-f]{64})', reply)[1] + kit = root/'kit'; kit.mkdir() + metadata = dict(schema='tricu.bootstrap-kit.v2' if args.io_version == 2 else 'tricu.bootstrap-kit.v1', + backend=args.backend, io_version=args.io_version, artifacts={}) + for role, label, filename, export in [ + ('compiler', 'worker', 'rebuilt-compiler.arboricx', BACKENDS[args.backend][0]), + ('adapter', 'adapter', 'io-adapter.arboricx', 'main')]: + run('compile-'+label, ['compile', '--file', projection/(label+'.tri'), + '--toolchain', compiler, '--tool-store', root/'tools', + *compiler_flags(args.compiler_io_version), '--store', root/'store', + '--output', kit/filename, '--compile-timeout', str(max(1, int(args.seconds-(time.monotonic()-start))))]) + metadata['artifacts'][role] = dict(file=filename, export=export, + sha256=digest((kit/filename).read_bytes())) + if digest(runtime.read_bytes()) != runtime_pin: + raise ValueError('runtime changed during preparation') + (kit/'bootstrap-kit.json').write_text(json.dumps(metadata, indent=2)+'\n') + load_kit(kit, args.backend) + (root/'report.json').write_text(json.dumps(dict(complete=True, generation_built=False, + compiler_io_version=args.compiler_io_version, produced_io_version=args.io_version, + runtime_sha256=runtime_pin, compiler_sha256=digest(compiler_bytes), compiler_manifest=compiler, + map_sha256=digest(text.encode()), sources={a:digest(b) for a,b in sources.items()}, + worker_source_sha256=digest(worker.encode()), adapter_source_sha256=digest(adapter.encode()), + kit=metadata, records=records, elapsed_seconds=time.monotonic()-start), indent=2)+'\n') + print('Prepared explicit compiler kit:', kit, '(not generation acceptance)') + + +if __name__ == '__main__': + main() diff --git a/test/prepare_generation_worker.py b/test/prepare_generation_worker.py @@ -13,6 +13,7 @@ import subprocess import time from process_rss import run_guarded from generation_backend import BACKENDS, DEFAULT_BACKEND +from generation_io import cli_version, compiler_flags, versions ROOT = Path(__file__).resolve().parent.parent parser = argparse.ArgumentParser(description=__doc__) @@ -20,6 +21,7 @@ parser.add_argument('tricu', type=Path) parser.add_argument('previous', type=Path) parser.add_argument('output', type=Path) parser.add_argument('--compiler-bundle', type=Path, required=True) +parser.add_argument('--compiler-io-version', type=cli_version, default=1) parser.add_argument('--backend', choices=BACKENDS, default=DEFAULT_BACKEND) parser.add_argument('--plan-only', action='store_true', help='report selected support and flat size without writes or execution') parser.add_argument('--support-flat-bytes', type=int, default=64 * 1024 * 1024, help='explicit flat transport budget for retained support') @@ -30,6 +32,8 @@ parser.add_argument('--seconds', type=float, default=1800, help='whole preparati args = parser.parse_args() if not 0 < args.memory_mib <= 65536 or not 0 < args.seconds <= 7200 or args.support_flat_bytes <= 0: parser.error('preparation limits must be within 64 GiB and two hours') +if versions(json.loads((args.previous / 'inputs.json').read_text()))[1] != 1: + parser.error('legacy flat bridge requires v1 support; use prepare_compiler_kit.py for source-mapped v2 preparation') start = time.monotonic() peak_kib = 0 tricu = str(args.tricu.resolve()) @@ -161,12 +165,13 @@ source = header + '!export ' + entrypoint + '\n' + constructors + '\n' + body (root / 'worker.tri').write_text(source) compiler = imported(args.compiler_bundle.resolve(), tools, True) print(run(['arboricx', 'compile', '--file', root / 'worker.tri', '--store', store, - '--toolchain', compiler, '--tool-store', tools, '--output', root / 'worker.arboricx', + '--toolchain', compiler, *compiler_flags(args.compiler_io_version), '--tool-store', tools, '--output', root / 'worker.arboricx', '--compile-timeout', str(max(1, min(900, int(remaining()))))]), end='') pin = imported(root / 'worker.arboricx', store, indexed=True) (root / 'worker.pin').write_text(pin) (root / 'provenance.json').write_text(json.dumps(dict( support=support, compiler=compiler, worker=pin, selected=selected, + compiler_io_version=args.compiler_io_version, produced_io_version=1, backend=args.backend, entrypoint=entrypoint, local_definitions=sorted(needed), elapsed_seconds=time.monotonic()-start, peak_kib=peak_kib, memory_mib=args.memory_mib, seconds=args.seconds, diff --git a/test/process_rss.py b/test/process_rss.py @@ -40,7 +40,7 @@ class ChildRSS: try: for value in path.read_text().split(): yield int(value) - except FileNotFoundError: + except (FileNotFoundError, ProcessLookupError): pass def sample(self): @@ -51,7 +51,7 @@ class ChildRSS: for pid in candidates: try: fields = status_fields(Path(f'/proc/{pid}/status').read_text()) - except FileNotFoundError: + except (FileNotFoundError, ProcessLookupError): continue diagnostic = repr({key: fields.get(key) for key in ('Pid', 'PPid', 'NSpid', 'State', 'VmRSS')}) if not self.matches(fields): @@ -68,7 +68,7 @@ class ChildRSS: for path in Path(f'/proc/{pid}/task').glob('*/status'): try: task = status_fields(path.read_text()) - except FileNotFoundError: + except (FileNotFoundError, ProcessLookupError): continue if int(task.get('Tgid', '-1')) == pid and 'VmRSS' in task: value, unit = task['VmRSS'].split() @@ -80,7 +80,7 @@ class ChildRSS: # unlike missing status, this is a verified zero-memory observation. try: statm = Path(f'/proc/{pid}/statm').read_text().split() - except FileNotFoundError: + except (FileNotFoundError, ProcessLookupError): statm = [] if len(statm) == 7 and all(value == '0' for value in statm): return 0 diff --git a/test/process_rss_test.py b/test/process_rss_test.py @@ -0,0 +1,45 @@ +"""Disappearing procfs entries are not zero samples or permission to run unguarded.""" +import subprocess +import unittest +from pathlib import Path +from unittest.mock import Mock, patch +from process_rss import ChildRSS + +class ProcfsExitTests(unittest.TestCase): + def reader(self, *, exited): + sample = ChildRSS.__new__(ChildRSS) + sample.pid = 123 + sample.parent = 1 + sample.namespace_index = 0 + sample.proc = Mock(pid=123) + sample.proc.poll.side_effect = [None,0 if exited else None] + sample.proc.wait.side_effect = subprocess.TimeoutExpired('child',.25) + return sample + + def disappearing(self, target, *, exited): + sample = self.reader(exited=exited) + def read(path, *args, **kwargs): + if str(path).endswith(target): + raise ProcessLookupError('process exited during procfs read') + return 'Pid:\t123\nPPid:\t1\nNSpid:\t123\nState:\tR\n' + with patch.object(Path,'read_text',read), patch.object(Path,'glob',return_value=[]): + return sample.sample() + + def test_status_esrch_after_exit_is_no_sample(self): + self.assertIsNone(self.disappearing('status',exited=True)) + + def test_statm_esrch_after_exit_is_no_sample(self): + self.assertIsNone(self.disappearing('statm',exited=True)) + + def test_unreaped_unobservable_child_remains_fatal(self): + with self.assertRaisesRegex(RuntimeError,'refusing an unguarded run'): + self.disappearing('statm',exited=False) + + def test_permission_failure_is_not_silently_ignored(self): + sample = self.reader(exited=True) + with patch.object(Path,'read_text',side_effect=PermissionError('denied')): + with self.assertRaises(PermissionError): + sample.sample() + +if __name__ == '__main__': + unittest.main() diff --git a/test/provenance.py b/test/provenance.py @@ -78,9 +78,9 @@ def controls(exe): key = digest('arboricx.source-build-key.v1', ('arboricx.source-build-key.v1\n' + rows).encode()) cache = store / 'cache/source-build/main'; cache.parent.mkdir(parents=True) cache.write_text('\t'.join(['arboricx.source-build-cache.v1', key, output, receipt])) - def capture(target, good=True, selected_receipt=None): + def capture(target, good=True, selected_receipt=None, io_version=1): return run('capture', '--file', workspace / 'main.tri', '--store', store, - '--tool-store', store, '--toolchain', compiler, '--output', target, + '--tool-store', store, '--toolchain', compiler, '--toolchain-io-version', io_version, '--output', target, *(['--receipt', selected_receipt] if selected_receipt else []), good=good) (workspace / 'main.tri').write_bytes(source + b'-- drift\n') capture(root / 'stale', False); assert not (root / 'stale').exists() @@ -94,6 +94,43 @@ def controls(exe): run('verify', '--pack', pack, '--pin', '0'*64, good=False) run('rebuild', '--pack', pack, '--pin', pin, '--trust-compiler', '0'*64, '--output', root / 'unauthorized', good=False) assert not (root / 'unauthorized').exists() + # A separately domain-separated v2 descriptor remains inert even though + # this deliberately bogus compiler cannot execute either protocol. + v2 = root/'v2-pack' + pin2 = re.search(r'Retained build inputs: ([0-9a-f]{64})', capture(v2, io_version=2))[1] + descriptor2 = v2/'build-inputs'; original2 = descriptor2.read_bytes() + assert original2.startswith(b'arboricx.build-inputs.v2\n') + assert b'compiler-io\t2\n' in original2 + assert pin2 == digest('arboricx.build-inputs.v2', original2) and pin2 != pin + before2 = snapshot(v2) + assert 'Compiler IO version 2' in run('verify','--pack',v2,'--pin',pin2) + assert snapshot(v2) == before2 + run('verify','--pack',v2,'--pin',digest(INPUTS,original2),good=False) + for changed in [original2.replace(b'compiler-io\t2\n',b''), + original2.replace(b'compiler-io\t2',b'compiler-io\t1'), + original2.replace(b'compiler-io\t2',b'compiler-io\t02'), + original2.replace(b'compiler-io\t2',b'compiler-io\t3'), + original2.replace(b'compiler-io\t2',b'compiler-io\ttrue'), + original2 + b'compiler-io\t2\n']: + descriptor2.write_bytes(changed) + run('verify','--pack',v2,'--pin',digest('arboricx.build-inputs.v2',changed),good=False) + descriptor2.write_bytes(original2) + for target, selected in [('default-authority',[]),('wrong-authority',['--trust-io-version','1'])]: + run('rebuild','--pack',v2,'--pin',pin2,'--trust-compiler',compiler, + '--output',root/target,*selected,good=False) + assert not (root/target).exists() + run('rebuild','--pack',pack,'--pin',pin,'--trust-compiler',compiler, + '--trust-io-version','2','--output',root/'v1-wrong-authority',good=False) + assert not (root/'v1-wrong-authority').exists() + for flag, command in [('toolchain-io-version','capture'),('trust-io-version','rebuild')]: + for value in ['0','3','01','02','+2','true','-1']: + target=root/('invalid-'+command+'-'+value) + arguments = (['--file',workspace/'main.tri','--store',store,'--tool-store',store, + '--toolchain',compiler] if command=='capture' else + ['--pack',v2,'--pin',pin2,'--trust-compiler',compiler]) + reply=run(command,*arguments,'--output',target,'--'+flag,value,good=False) + assert 'IO version must be 1 or 2' in reply,reply + assert not target.exists() packed = pack / 'store' for kind, obj in [(SOURCE, digest(SOURCE, source)), (OPTIONS, options), (RECEIPT, receipt), (INDEXED, leaf)]: path = object_path(packed, obj); original = path.read_bytes() @@ -125,14 +162,20 @@ def acceptance(exe, accepted, directory): directory.mkdir(parents=True, exist_ok=False) metadata = json.loads((accepted / 'report.json').read_text()) compiler = metadata['compiler_manifest'] + compiler_io = metadata.get('compiler_io_version', 1) + assert type(compiler_io) is int and compiler_io in (1, 2) producer = directory / 'disposable-producer'; producer.mkdir() for name in ['producer', 'tools', 'application', 'consumer']: shutil.copytree(accepted / name, producer / name) isolated = directory / 'isolated'; isolated.mkdir() - report = dict(complete=False, commands=[], packs={}, compiler=compiler) + report = dict(complete=False, commands=[], packs={}, compiler=compiler, compiler_io_version=compiler_io) started = time.monotonic() def record(): (directory / 'report.json').write_text(json.dumps(report, indent=2) + '\n') def run(*args, good=True, seconds=120, namespace='provenance'): + if args[0] in ('compile', 'capture'): + args = (*args, '--toolchain-io-version', str(compiler_io)) + elif args[0] == 'rebuild': + args = (*args, '--trust-io-version', str(compiler_io)) remaining = 2400 - (time.monotonic() - started) if remaining <= 0: raise TimeoutError('whole provenance acceptance deadline') before = time.monotonic() @@ -204,10 +247,12 @@ def acceptance(exe, accepted, directory): src = put(store, SOURCE, source) new_receipt_bytes = ('\n'.join([RECEIPT, 'source\t'+src, receipt_rows[2], receipt_rows[3], receipt_rows[4]]) + '\n').encode() new_receipt = put(store, RECEIPT, new_receipt_bytes) - new_descriptor = ('\n'.join([INPUTS, 'entry\tmain', 'receipt\t'+new_receipt, rows[3], 'unit\tmain\t'+new_receipt] + new_descriptor = ('\n'.join([rows[0], 'entry\tmain', 'receipt\t'+new_receipt, rows[3]] + + [row for row in rows if row.startswith('compiler-io\t')] + + ['unit\tmain\t'+new_receipt] + [row for row in rows if row.startswith('project\t')]) + '\n').encode() (forged / 'build-inputs').write_bytes(new_descriptor) - pin = digest(INPUTS, new_descriptor) + pin = digest(rows[0], new_descriptor) key_body = '\n'.join(['arboricx.source-build-key.v1', 'source\t'+src, receipt_rows[2], receipt_rows[3]]) + '\n' key = digest('arboricx.source-build-key.v1', key_body.encode()) poison = store / 'cache/source-build/main'; poison.parent.mkdir(parents=True, exist_ok=True) diff --git a/test/repair_support_fixture.py b/test/repair_support_fixture.py @@ -114,6 +114,10 @@ def main(): parser.add_argument('--replace', action='append', default=[], metavar='ALIAS') parser.add_argument('--relocate-patterns', action='store_true', help='explicitly move rebuilt patterns from compiler to support membership') + parser.add_argument('--add-io-v2-support', action='store_true', help='explicit complete ten-module IO-v2 support expansion; genuine rebuilds required') + parser.add_argument('--compiler-io-cohort', action='store_true', help='explicit v2 compiler ownership of core/text/storage; rebuild general io in support') + parser.add_argument('--compiler-fixture', type=Path, help='explicit freshly packed compiler candidate; default is the published fixture') + parser.add_argument('--source-root', type=Path, default=ROOT, help='authored source snapshot; must exactly match each prepared source/digest') parser.add_argument('--output-directory', type=Path, required=True, help='new candidate directory; never overwrite published fixtures') parser.add_argument('--verification-memory-mib', type=int, default=8192, @@ -122,10 +126,14 @@ def main(): args = parser.parse_args() if not 1024 <= args.verification_memory_mib <= 65536 or args.capabilities < 1: parser.error('invalid audit resource limits') + source_root = args.source_root.resolve() inputs = ROOT / 'test/fixtures' output = args.output_directory.resolve() output.mkdir(parents=True, exist_ok=False) selected = select_candidates([p.resolve() for p in args.directory], args.replace) + from native_library_migration import check_source_dependencies + for directory in {directory for directory, _ in selected.values()}: + check_source_dependencies(directory, source_root) deadline = time.monotonic() + 1200 verifications = [] @@ -155,7 +163,8 @@ def main(): shutil.copyfile(inputs / name, output / name) old = (output / 'support-origin.v1').read_text().splitlines() original = output / 'support-origin.arboricx' - compiler = inputs / 'compiler-library.arboricx' + compiler = args.compiler_fixture.resolve() if args.compiler_fixture else inputs / 'compiler-library.arboricx' + read(compiler) # Validate candidate bundle framing before binding its exact bytes. compiler_bytes = compiler.read_bytes() if ('bundle\t' + digest('tricu.support-library-fixture.v1', original.read_bytes()) not in old or 'compiler-fixture\t' + digest('tricu.compiler-library-fixture.v1', @@ -166,7 +175,7 @@ def main(): providers.update({alias: record['manifest'] for alias, (_, record) in selected.items()}) candidates, identities, sources = {}, {}, {} for alias, (directory, record) in selected.items(): - source = (ROOT / record['path']).read_bytes() + source = (source_root / record['path']).read_bytes() if (source != (directory / 'workspace' / (alias + '.tri')).read_bytes() or hashlib.sha256(source).hexdigest() != record['after_sha256']): raise ValueError('authored source differs from compiled candidate: ' + alias) @@ -175,6 +184,38 @@ def main(): imported = json.loads((directory / 'events.json').read_text())[0]['stdout'] identities[alias] = re.search(r'sourceBuildGraph -> ([0-9a-f]{64})', imported)[1] membership = relocate_patterns(old, candidates) if args.relocate_patterns else old + additions = [] + compiler_owned = {'io_v2': 'lib/io-v2.tri', 'io_v2_storage': 'lib/io-v2/storage.tri', + 'io_v2_text': 'lib/io-v2/text.tri'} if args.compiler_io_cohort else {} + if args.compiler_io_cohort: + if not args.add_io_v2_support: + raise ValueError('compiler IO cohort requires explicit IO-v2 support expansion') + rows = [line.split('\t') for line in compiler.with_suffix('.v1').read_text().splitlines()] + if ['bundle', digest('tricu.compiler-library-fixture.v1', compiler_bytes)] not in rows: + raise ValueError('compiler metadata identity mismatch') + required = dict(compiler_owned, compiler_io='lib/source/io-v2.tri') + for alias, path in required.items(): + owned = [row for row in rows if row[:1] == ['module'] and row[2] == alias] + if len(owned) != 1 or owned[0][3:] != [path, digest('arboricx.source.v1', (source_root/path).read_bytes())]: + raise ValueError('compiler source ownership mismatch: ' + alias) + if any(row[:1] == ['module'] and row[2] == 'io' for row in rows): + raise ValueError('general IO must not remain compiler-owned') + if set(compiler_owned) & candidates.keys(): + raise ValueError('compiler-owned modules submitted as support candidates') + if args.add_io_v2_support: + if not args.relocate_patterns: + raise ValueError('IO-v2 support expansion requires the existing patterns relocation') + from native_library_migration import IO_V2_SUPPORT + expanded = {alias: path for alias, path in IO_V2_SUPPORT.items() if alias not in compiler_owned} + if args.compiler_io_cohort: + expanded['io'] = 'lib/io.tri' + for alias, path in expanded.items(): + if alias not in candidates or (candidates[alias][0]['legacy'], candidates[alias][0]['path']) != (alias, path): + raise ValueError('missing/mismatched rebuilt IO-v2 provider: ' + alias) + if any(row.startswith('module\t') and row.split('\t')[2] == alias for row in membership): + raise ValueError('IO-v2 addition already in original cohort') + membership.append('\t'.join(['module', alias, alias, path, digest('arboricx.source.v1', candidates[alias][1])])) + additions.append('\t'.join(['added', alias, alias, path])) modules = updated_module_rows(membership, candidates) if args.relocate_patterns: from fixture_sources import source_rows @@ -215,14 +256,20 @@ def main(): for name in ('support-rebuilt.arboricx', 'support-library.arboricx'): verify_bundle(output / name, indexed=True) # Do not finish a candidate if source/compiler inputs changed while packing. - if any((ROOT / path).read_bytes() != payload for path, payload in sources.items()): + if any((source_root / path).read_bytes() != payload for path, payload in sources.items()): raise ValueError('source changed during publication') + for directory in {directory for directory, _ in selected.values()}: + check_source_dependencies(directory, source_root) if compiler.read_bytes() != compiler_bytes: raise ValueError('compiler fixture changed during publication') - metadata = ['tricu.support-library-fixture.v3' if args.relocate_patterns else + metadata = ['tricu.support-library-fixture.v5' if args.compiler_io_cohort else + 'tricu.support-library-fixture.v4' if args.add_io_v2_support else + 'tricu.support-library-fixture.v3' if args.relocate_patterns else 'tricu.support-library-fixture.v2', *modules] if args.relocate_patterns: metadata.append('relocated\tcompiler-library\tpatterns\tpatterns\tlib/patterns.tri') + metadata += additions + metadata += ['\t'.join(['compiler-provider', alias, path]) for alias, path in compiler_owned.items()] for label, domain, payload in [ ('compiler-fixture', 'tricu.compiler-library-fixture.v1', compiler_bytes), ('origin-metadata', 'tricu.support-origin-metadata.v1', (output / 'support-origin.v1').read_bytes()), diff --git a/test/rts_metrics.py b/test/rts_metrics.py @@ -0,0 +1,24 @@ +"""Whole-process GHC -s statistics; never conflate these with invocation deltas.""" +import re + + +def parse_rts_stats(output): + fields = {} + for label, key in [('allocated in the heap', 'allocated_bytes'), + ('copied during GC', 'copied_bytes'), + ('maximum residency', 'maximum_live_bytes')]: + found = re.search(r'^[ \t]*([0-9,]+) bytes ' + label, output, re.M) + if found: + fields[key] = int(found[1].replace(',', '')) + for label, key in [('MUT', 'mutator'), ('GC', 'gc')]: + found = re.search(r'^\s*' + label + r'\s+time\s+([0-9.]+)s\s+\(\s*([0-9.]+)s elapsed\)', output, re.M) + if found: + fields[key + '_cpu_seconds'] = float(found[1]) + fields[key + '_elapsed_seconds'] = float(found[2]) + found = re.search(r'Productivity\s+([0-9.]+)% of total user,\s+([0-9.]+)% of total elapsed', output) + if found: + fields['productivity_cpu_percent'] = float(found[1]) + fields['productivity_elapsed_percent'] = float(found[2]) + if fields: + fields['measurement_scope'] = 'whole-process-including-load-render-and-sampling' + return fields diff --git a/test/rts_metrics_test.py b/test/rts_metrics_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +import unittest +from rts_metrics import parse_rts_stats + +class RTSMetricsTests(unittest.TestCase): + def test_absent_is_not_zero(self): + self.assertEqual(parse_rts_stats('no statistics'), {}) + + def test_whole_process_units_and_parallel_cpu(self): + sample = ''' + 123,456 bytes allocated in the heap + 12,345 bytes copied during GC + 1,234 bytes maximum residency (7 sample(s)) + MUT time 8.501s ( 8.530s elapsed) + GC time 1.200s ( 0.614s elapsed) + Productivity 87.6% of total user, 93.3% of total elapsed +''' + self.assertEqual(parse_rts_stats(sample), { + 'allocated_bytes': 123456, 'copied_bytes': 12345, 'maximum_live_bytes': 1234, + 'mutator_cpu_seconds': 8.501, 'mutator_elapsed_seconds': 8.530, + 'gc_cpu_seconds': 1.200, 'gc_elapsed_seconds': .614, + 'productivity_cpu_percent': 87.6, 'productivity_elapsed_percent': 93.3, + 'measurement_scope': 'whole-process-including-load-render-and-sampling'}) + + def test_large_rendered_tree_is_not_scanned_from_every_digit(self): + # An unanchored numeric regex retries at every position, going quadratic + # on rendered ternary trees. Real statistics occupy their own lines. + output = '210' * 100000 + '\n 123 bytes copied during GC\n' + self.assertEqual(parse_rts_stats(output)['copied_bytes'], 123) + + def test_partial_output_preserves_unknowns(self): + stats = parse_rts_stats('0 bytes copied during GC') + self.assertEqual(stats['copied_bytes'], 0) + self.assertNotIn('gc_cpu_seconds', stats) + +if __name__ == '__main__': + unittest.main() diff --git a/test/shipped_compiler.py b/test/shipped_compiler.py @@ -16,7 +16,8 @@ ROOT = Path(__file__).resolve().parent.parent def verify(root=ROOT): metadata = json.loads((root / 'bootstrap/stable-compiler.json').read_text()) - assert metadata['schema'] == 'tricu.shipped-stable-compiler.v1' + assert metadata['schema'] == 'tricu.shipped-stable-compiler.v2' + assert metadata['io_version'] == 2 assert metadata['backend'] == 'stable' for path, digest in [(metadata['bundle'], metadata['sha256']), (metadata['source_map'], metadata['source_map_sha256'])] + [ @@ -41,6 +42,7 @@ def verify(root=ROOT): assert shipped == published, 'shipped stable builder differs from published accepted tree' raw, _, _ = load_kit(root / 'bootstrap/rebuild', 'stable') kit = json.loads(raw) + assert kit['schema'] == 'tricu.bootstrap-kit.v2' and kit['io_version'] == 2 assert kit['accepted_source'] == metadata['accepted_source'] assert kit['source_map_sha256'] == metadata['source_map_sha256'] # Acceptance summaries must describe these bytes, not an older publication. diff --git a/test/sourceEliminationProbe.tri b/test/sourceEliminationProbe.tri @@ -1,20 +0,0 @@ --- Manual canonical probe (use a fresh --store with --io --unsafe-io): --- compiling unchanged test/size.tri through the portable frontend prints 301. -!import "prelude" !Local -!import "parser" !Local -!import "io" !Local -!import "source.unit" !Local -fileExternals = - [(pair "map" map) - (pair "append" append) - (pair "equal?" equal?) - (pair "head" head) - (pair "y" y) - (pair "triage" triage)] -main = io (onResult_ (readFile "test/size.tri") - (problem : putStrLn "read-failed") - (source : lazyResult - (problem rest : putStrLn (append "diagnostic=" (showNumber (parserDiagnosticCode problem)))) - (tree rest : putStrLn (showNumber tree)) - (compileGateProgram fileExternals source))) -main diff --git a/test/source_order.py b/test/source_order.py @@ -17,6 +17,7 @@ import time from bundle_dag import merge_bundles from fixture_sources import verify from process_rss import run_guarded +from rts_metrics import parse_rts_stats from stable_bundle import byte_tree ROOT = Path(__file__).resolve().parent.parent @@ -137,6 +138,7 @@ class Runner: row['measurement_scope'] = 'invocation-through-rendered-output-v1' row['invoke_seconds'] = (after['wall_ns'] - before['wall_ns']) / 1e9 row['allocated_bytes'] = after['allocated_bytes'] - before['allocated_bytes'] + row['rts_process'] = parse_rts_stats(output) self.records.append(row) (self.root / 'samples.json').write_text(json.dumps(self.records, indent=2) + '\n') return output diff --git a/test/stableBundleRuntime.tri b/test/stableBundleRuntime.tri @@ -1,25 +0,0 @@ -!import bundle_support * -!export main -!export emit - --- Stable-backend runtime adapter, shared by every measured implementation. --- Fresh filesystem reads defeat repeated pure-result sharing. Input/expected --- trees are forced before checkpoint 1; results before checkpoint 2. Equality --- is checked afterward. No stageOn or argument-root force token is needed. --- forceTree is the same optimized, input-driven traversal for both variants; --- timing the stable backend's own traversal would obscure assembly costs. - -finish result expected = (matchBool - (bind yield (_ : pure (ok (equal? result expected) t))) - (abortIO "result forcing failed") - (forceTree result)) -main function manifestPath nodesPath expectedPath = (io - (onOk_ (readFile manifestPath) (manifest : - onOk_ (readFile nodesPath) (nodes : - onOk_ (readFile expectedPath) (expected : matchBool - (bind yield (_ : finish (function manifest nodes) expected)) - (abortIO "input forcing failed") - (and? (forceTree manifest) (and? (forceTree nodes) (forceTree expected)))))))) -emit function manifestPath nodesPath outputPath = (io - (onOk_ (readFile manifestPath) (manifest : - onOk_ (readFile nodesPath) (nodes : writeBytes outputPath (function manifest nodes))))) diff --git a/test/stable_bundle.py b/test/stable_bundle.py @@ -1,26 +1,8 @@ #!/usr/bin/env python3 -"""Stable bundle assembly: publication, independent byte goldens, real bundles, -and preloaded execution intervals. Haskell only; opt-in, no default-gate work. - -python3 test/stable_bundle.py result/bin/tricu --compiler-bundle STABLE.arboricx \ - --directory /tmp/stable-bundle --input-bundle APPLICATION.arboricx - -Sampling uses fresh reads each iteration, excludes input IO/forcing, compilation, -loading and equality; includes full output traversal, checkpoint overhead and GC. -""" +"""Independent bundle wire helpers; historical benchmark CLI is retired.""" import argparse -import hashlib -import json from pathlib import Path -import re -import statistics import struct -import subprocess -import tempfile -import time - -ROOT = Path(__file__).resolve().parent.parent - def assembled(manifest, nodes): def section(identifier, offset, size): @@ -70,188 +52,6 @@ def main(): parser.add_argument('--reverse-order', action='store_true') args = parser.parse_args() parser.error("Retired implicit recovery preparation: this historical workflow has no verified native-support input route. Use current portable candidate tooling; do not reuse its old support/cache artifacts.") - assert 2 <= args.repeat <= 20 - assert 2 <= args.cold_repeat <= 10 - lengths = [int(n) for n in args.lengths.split(',')] - assert all(0 <= n <= 65536 for n in lengths) - tricu = str(args.tricu.resolve()) - root = args.directory.resolve() if args.directory else Path(tempfile.mkdtemp(prefix='stable-bundle-')) - root.mkdir(parents=True, exist_ok=True) - store, tools = root / 'store', root / 'tools' - preparation = [] - - def run(command, timeout=180): - reply = subprocess.run([tricu, *map(str, command)], cwd=ROOT, capture_output=True, - text=True, timeout=timeout) - assert reply.returncode == 0, (command, reply.stdout, reply.stderr) - return reply - - def imported(bundle, destination=store, indexed=False): - reply = run(['arboricx', 'import', '--file', bundle, '--store', destination, - *(['--indexed'] if indexed else ['--flat'])]).stdout - pin = re.search(r'Created module alias .* -> ([0-9a-f]{64})', reply)[1] - rows = (destination / 'objects' / pin[:3] / pin).read_text().splitlines()[1:] - return pin, {r.split('\t')[1]: r.split('\t')[3] for r in rows} - - compiler, _ = imported(args.compiler_bundle.resolve(), tools, indexed=True) - # Bootstrap facade stays in the checkout for workspace module resolution. - # Cache validity includes all library source and the selected executable. - digest = hashlib.sha256(tricu.encode()) - for path in sorted((ROOT / 'lib').rglob('*.tri')): - digest.update(path.read_bytes()) - support_text = ('!import "prelude" B\n!import "io" I\n!import "source.bundle" W\n' + - ''.join(f'{n} = B.{n}\n' for n in ['append', 'add', 'length', 'pair', 'true', 'and?', - 'triage', 'matchBool', 'equal?', 'ok', 'y', 'succ', 'lazyList', 'lazyResult']) + - ''.join(f'{n} = I.{n}\n' for n in ['io', 'bind', 'yield', 'pure', 'readFile', 'writeBytes', 'onOk_', 'abortIO']) + - ''.join(f'{n} = W.{n}\n' for n in ['sourceBundleHeader', 'sourceBundleSection']) + - 'reference = W.sourceBundleAssemble\n' - 'passthrough = B.append\n' - 'forceTree = B.y (self value : B.triage B.true (child : self child) ' - '(left right : B.and? (self left) (self right)) value)\n') - digest.update(support_text.encode()) - stamp = root / 'support.sha256' - support_bundle = root / 'support.arboricx' - if not support_bundle.exists() or not stamp.exists() or stamp.read_text() != digest.hexdigest(): - with tempfile.TemporaryDirectory(prefix='.stable-bundle-support-', dir=ROOT / 'test') as temporary: - source = Path(temporary) / 'support.tri' - source.write_text(support_text) - names = [line.split(' = ')[0] for line in support_text.splitlines() if ' = ' in line] - started = time.monotonic() - run(['arboricx', 'compile', '--file', source, '--store', store, - *[a for name in names for a in ('--name', name)], '--output', support_bundle], timeout=600) - preparation.append(dict(name='support', seconds=time.monotonic()-started)) - stamp.write_text(digest.hexdigest()) - support, support_exports = imported(support_bundle) - (root / 'bundle_support.tri').write_text(f'!export * from {support}\n') - - def portable(name, text): - source, bundle = root / (name + '.tri'), root / (name + '.arboricx') - identity = hashlib.sha256((compiler + support + text).encode()).hexdigest() - stamp = root / (name + '.sha256') - if not bundle.exists() or not stamp.exists() or stamp.read_text() != identity: - source.write_text(text) - started = time.monotonic() - run(['arboricx', 'compile', '--file', source, '--store', store, - '--toolchain', compiler, '--tool-store', tools, '--output', bundle, - '--compile-timeout', '240'], timeout=270) - preparation.append(dict(name=name, seconds=time.monotonic()-started, bytes=bundle.stat().st_size)) - stamp.write_text(identity) - return bundle, imported(bundle)[1] - - candidate, candidate_exports = portable('assembly', (ROOT / 'lib/source/bundle-stable.tri').read_text()) - adapter, _ = portable('adapter', (ROOT / 'test/stableBundleRuntime.tri').read_text()) - adapter_pin, _ = imported(adapter, indexed=True) - variants = {'reference': support_exports['reference'], 'stable-direct': candidate_exports['assemble']} - # Serialize both a known-first-argument partial application and a known - # input constructor whose tail is still unknown. Only ordinary syntax. - partial, partial_exports = portable('specialized', - '!import assembly as A\n!import bundle_support *\n' - '!export known\n!export prefix\n' - 'known = A.assemble "abc"\nprefix tail nodes = A.assemble (pair 97 tail) nodes\n') - - def paths(label, manifest, nodes, expected=None): - files = [root / (label + suffix) for suffix in ('.manifest', '.nodes', '.expected')] - for path, content in zip(files, (manifest, nodes, assembled(manifest, nodes) if expected is None else expected)): - path.write_bytes(content) - return files - - def invoke(function, files, repeat=1): - return run(['arboricx', 'run', '--store', store, '--module', adapter_pin, '--name', 'main', - '--arg-tree-pin', function, *[a for path in files for a in ('--arg', path)], - '--io', '--allow-read', root, '--repeat', repeat, '--profile', '--result', '--format', 'fsl', - '+RTS', '-T', '-N1', '-RTS'], timeout=300) - - cases = [('empty', b'', b''), ('small', b'abc', b'de'), - ('carry', b'x'*258, b'y'*257), ('manifest-only', bytes(range(256)), b''), - ('nodes-only', b'', bytes(range(256)))] - for label, manifest, nodes in cases: - files = paths(label, manifest, nodes) - for mode, function in variants.items(): - assert invoke(function, files).stdout.strip() == 'ok: Stem Leaf', (label, mode) - print(f'{label}: independent byte golden passed for both variants', flush=True) - files = paths('prefix', b'bc', b'de', assembled(b'abc', b'de')) - assert invoke(partial_exports['prefix'], files).stdout.strip() == 'ok: Stem Leaf' - # Adapt known's remaining one-argument interface without re-running compilation. - known_adapter, known_exports = portable('known_adapter', - '!import specialized as S\n!export applyKnown\napplyKnown unused nodes = S.known nodes\n') - assert invoke(known_exports['applyKnown'], files).stdout.strip() == 'ok: Stem Leaf' - print('partial application and known-head specialization passed', flush=True) - - samples = [('synthetic-' + str(n), bytes((i % 256 for i in range(n//4))), - bytes(((i*17+3) % 256 for i in range(n-n//4))), None) for n in lengths] - for i, path in enumerate(args.input_bundle): - manifest, nodes = sections(path.resolve()) - samples.append((f'artifact-{i}-{path.stem}', manifest, nodes, path.resolve())) - report = [] - measured = {**variants, 'traversal-control': support_exports['passthrough']} - candidate_pin, _ = imported(candidate) - direct_exports = {'reference': (support, 'reference'), 'stable-direct': (candidate_pin, 'assemble'), - 'traversal-control': (support, 'passthrough')} - cold_report = [] - for index, (label, manifest, nodes, original) in enumerate(samples): - files = paths(label, manifest, nodes) - order = list(measured) - if (index + args.reverse_order) % 2: - order.reverse() - for mode in order: - measured_files = (paths(label + '-control', manifest, nodes, manifest + nodes) - if mode == 'traversal-control' else files) - reply = invoke(measured[mode], measured_files, args.repeat) - assert reply.stdout.splitlines().count('ok: Stem Leaf') == args.repeat, reply.stdout - checkpoints = {p['profile']: p for line in reply.stderr.splitlines() if line.startswith('{') for p in [json.loads(line)]} - measurements = [] - for i in range(1, args.repeat+1): - start, end = checkpoints[f'yield-{i}-1'], checkpoints[f'yield-{i}-2'] - measurements.append(dict(ms=(end['wall_ns']-start['wall_ns'])/1e6, - allocated=end['allocated_bytes']-start['allocated_bytes'], - gc_ms=(end['gc_elapsed_ns']-start['gc_elapsed_ns'])/1e6)) - warm = measurements[1:] - assert all(s['allocated'] >= measurements[0]['allocated'] * 0.5 for s in warm), 'possible shared-result timing' - report.append(dict(case=label, mode=mode, manifest_bytes=len(manifest), nodes_bytes=len(nodes), samples=measurements)) - print(f'{label}/{mode}: {len(manifest)}+{len(nodes)} bytes; warm median ' - f'{statistics.median(s["ms"] for s in warm):.3f} ms, ' - f'{statistics.median(s["allocated"] for s in warm)/1e6:.3f} MB allocated', flush=True) - # Each direct invocation gets a fresh process and independently decoded - # concrete arguments. Host result forcing avoids the TC forceTree cost. - # Rendering/equality follow invoke-end and are outside the measured region. - arguments = [root / (label + suffix) for suffix in ('.manifest.tree', '.nodes.tree')] - for path, payload in zip(arguments, (manifest, nodes)): - path.write_bytes(byte_tree(payload)) - golden_trees = {False: byte_tree(assembled(manifest, nodes)).translate(bytes.maketrans(b'\x00\x01\x02', b'012')).decode(), - True: byte_tree(manifest + nodes).translate(bytes.maketrans(b'\x00\x01\x02', b'012')).decode()} - direct_samples = {mode: [] for mode in measured} - for iteration in range(args.cold_repeat): - for mode in (order if iteration % 2 == 0 else list(reversed(order))): - module, export = direct_exports[mode] - reply = run(['arboricx', 'run', '--store', store, '--module', module, '--name', export, - *[a for path in arguments for a in ('--arg-tree-file', path)], - '--format', 'ternary', '--profile', '+RTS', '-T', '-N1', '-RTS']) - assert reply.stdout.strip() == golden_trees[mode == 'traversal-control'], (label, mode, 'direct output') - profiles = {p['profile']: p for line in reply.stderr.splitlines() if line.startswith('{') for p in [json.loads(line)]} - start, end = profiles['invoke-begin-1'], profiles['invoke-end-1'] - direct_samples[mode].append(dict(ms=(end['wall_ns']-start['wall_ns'])/1e6, - allocated=end['allocated_bytes']-start['allocated_bytes'], - gc_ms=(end['gc_elapsed_ns']-start['gc_elapsed_ns'])/1e6)) - for mode, measurements in direct_samples.items(): - cold_report.append(dict(case=label, mode=mode, manifest_bytes=len(manifest), nodes_bytes=len(nodes), samples=measurements)) - print(f'{label}/{mode}: direct cold median {statistics.median(s["ms"] for s in measurements):.3f} ms, ' - f'{statistics.median(s["allocated"] for s in measurements)/1e6:.3f} MB allocated', flush=True) - (root / ('cold-results-reversed.json' if args.reverse_order else 'cold-results.json')).write_text(json.dumps(cold_report, indent=2)+'\n') - if original: - output = root / (label + '-rebuilt.arboricx') - run(['arboricx', 'run', '--store', store, '--module', adapter_pin, '--name', 'emit', - '--arg-tree-pin', variants['stable-direct'], '--arg', files[0], '--arg', files[1], '--arg', output, - '--io', '--allow-read', root, '--allow-write', root]) - assert output.read_bytes() == original.read_bytes() - imported(output, root / (label + '-verified')) - print(f'{label}: written artifact byte-identical; verified/imported into a separate store', flush=True) - (root / ('results-reversed.json' if args.reverse_order else 'results.json')).write_text(json.dumps(report, indent=2)+'\n') - wrong = paths('wrong', b'abc', b'de', b'wrong') - assert invoke(variants['stable-direct'], wrong).stdout.strip() == 'ok: Leaf' - print('wrong-output control passed', flush=True) - if preparation: - (root / 'preparation.json').write_text(json.dumps(preparation, indent=2)+'\n') - print(f'Artifacts and samples: {root}', flush=True) if __name__ == '__main__': diff --git a/test/stable_costs.py b/test/stable_costs.py @@ -1,21 +1,7 @@ #!/usr/bin/env python3 -"""Small cold-invocation cost check, reusing stable_abstraction.py artifacts. - -python3 test/stable_costs.py result/bin/tricu /tmp/tricu-stable -Each sample is a new process: no repeated pure-result sharing. GC-backed -invocation deltas exclude loading, include complete result traversal and ending -GC. This is a feasibility check, not a warm-runtime benchmark or compiler peak -memory measurement. Exact output equality is checked outside the timed region. -""" +"""Retired recovery-dependent cost CLI: reject before touching artifacts.""" import argparse -import json from pathlib import Path -import re -import statistics -import subprocess - -ROOT = Path(__file__).resolve().parent.parent - def main(): parser = argparse.ArgumentParser(description=__doc__) @@ -23,69 +9,6 @@ def main(): parser.add_argument('directory', type=Path) args = parser.parse_args() parser.error("Retired implicit recovery preparation: this historical workflow has no verified native-support input route. Use current portable candidate tooling; do not reuse its old support/cache artifacts.") - tricu = str(args.tricu.resolve()) - artifacts = args.directory.resolve() - root = artifacts / 'costs' - root.mkdir(exist_ok=True) - store = root / 'store' - - def run(command): - result = subprocess.run([tricu, *map(str, command)], cwd=ROOT, - capture_output=True, timeout=120) - assert result.returncode == 0, (command, result.stderr) - return result - - # Same algorithm and library parsers; handwritten fixed point and thunks. - reference = root / 'reference.tri' - reference.write_text('!import "prelude" !Local\n!import "parser" P\n' + - 'repeat_ self input parser reversed = (P.parserMatchReply\n' - ' (problem rest consumed committed : lazyBool\n' - ' (_ : err problem rest)\n' - ' (_ : ok (reverse reversed) input) (or? consumed committed))\n' - ' (value rest consumed committed : lazyBool\n' - ' (_ : self rest parser (pair value reversed))\n' - ' (_ : err "non-consuming parser" input) consumed)\n' - ' (parser input))\n' - 'main input = y repeat_ input (P.parserThen P.parserAny P.parserAny) []\n') - reference_bundle = root / 'reference.arboricx' - run(['arboricx', 'compile', '--file', reference, '--store', artifacts / 'store', - '--name', 'main', '--output', reference_bundle]) - stable_bundle = artifacts / 'repeat-empty.arboricx' - if not stable_bundle.exists(): - stable_bundle = artifacts / 'repeat-pairs.arboricx' - pins = {} - for label, bundle in [('reference', reference_bundle), ('stable', stable_bundle)]: - reply = run(['arboricx', 'import', '--file', bundle, '--store', store]).stdout - pins[label] = re.search(rb'Created module alias .* -> ([0-9a-f]{64})', reply)[1].decode() - - results = [] - for size in [512, 2048]: - text = 'ab' * (size // 2) - # Independently construct the expected Result tree using literal data. - golden = root / 'golden.tri' - golden.write_text('!import "prelude" !Local\n' + - 'main = ok ' + json.dumps('b' * (size // 2)) + ' []\n') - expected = run(['eval', golden, '--store', artifacts / 'store', '--format', 'tree']).stdout - for sample in range(3): - for label in (['reference', 'stable'] if sample % 2 == 0 else ['stable', 'reference']): - reply = run(['arboricx', 'run', '--store', store, '--module', pins[label], - '--name', 'main', '--arg', text, '--format', 'tree', '--profile', '+RTS', '-T', '-RTS']) - assert reply.stdout == expected, (label, size, 'wrong output') - profile = [json.loads(line) for line in reply.stderr.splitlines() if line.startswith(b'{')] - results.append(dict(label=label, input_bytes=size, sample=sample, profile=profile)) - (root / 'samples.json').write_text(json.dumps(results, indent=2) + '\n') - for size in [512, 2048]: - for label in pins: - selected = [r['profile'] for r in results if r['input_bytes'] == size and r['label'] == label] - # Keep labels explicit: fail rather than silently time the wrong region. - intervals = [] - for profile in selected: - begin = next(p for p in profile if p['profile'] == 'invoke-begin-1') - end = next(p for p in profile if p['profile'] == 'invoke-end-1') - intervals.append((end['wall_ns'] - begin['wall_ns'], - end['allocated_bytes'] - begin['allocated_bytes'])) - print(f'{size} bytes {label}: median {statistics.median(i[0] for i in intervals)/1e6:.2f} ms; ' - f'{statistics.median(i[1] for i in intervals)/1e6:.2f} MB allocated') if __name__ == '__main__': diff --git a/test/streaming_protocol.py b/test/streaming_protocol.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Real-socket negative controls for the opt-in streaming upload composition. + +Uses fixed port 8080; requires a Nix-built runner. No production fault flags. +Checks exact binary objects/digests, staged visibility, EOF cleanup, authority +and commit failures, framing rejection, and version-specific admission limits. +""" +import argparse +import hashlib +from pathlib import Path +import socket +import subprocess +import tempfile +import time + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('runner', type=Path) + parser.add_argument('--io-version', type=int, choices=[1, 2], default=1) + args = parser.parse_args() + runner = str(args.runner.resolve()) + passed = [] + def connect(): + return socket.create_connection(('127.0.0.1', 8080), timeout=30) + def headers(length): + return (f'POST /_arboricx/bundle HTTP/1.1\r\nHost: local\r\nContent-Length: {length}\r\n\r\n').encode() + def response(client, expected): + parts = [] + while True: + part = client.recv(65536) + if not part: break + parts.append(part) + header, body = b''.join(parts).split(b'\r\n\r\n', 1) + assert header.startswith(f'HTTP/1.1 {expected} '.encode()), header + fields = dict(line.split(b': ', 1) for line in header.split(b'\r\n')[1:]) + assert fields[b'Connection'] == b'close' + assert int(fields[b'Content-Length']) == len(body) + return body + def request(wire, expected): + with connect() as client: + client.sendall(wire); client.shutdown(socket.SHUT_WR) + return response(client, expected) + def wait_for(predicate, message): + for _ in range(300): + if predicate(): return + time.sleep(.01) + raise AssertionError(message) + + with tempfile.TemporaryDirectory(prefix='tricu-streaming-protocol-') as directory: + root = Path(directory) + with (root/'server.log').open('w+') as log: + proc = subprocess.Popen([runner], cwd=root, stdout=log, stderr=log) + try: + for _ in range(100): + if proc.poll() is not None: + log.seek(0); raise AssertionError(log.read()) + try: + assert request(b'GET /_arboricx/health HTTP/1.1\r\nHost: local\r\n\r\n', 200) == b'OK\n' + break + except OSError: + time.sleep(.1) + else: raise AssertionError('startup timeout') + staging = root/'store/tmp' + def object_path(body): + digest = hashlib.sha256(body).hexdigest() + return digest, root/'store/objects'/digest[:3]/(digest+'.arboricx') + def empty_stage(): + assert list(staging.iterdir()) == [] + for size in (0, 1, 4095, 4096, 4097, 8193): + body = (bytes(range(256))*((size+255)//256))[:size] + digest, path = object_path(body) + with connect() as client: + client.sendall(headers(size)) + # Deliberately fragment bodies across odd-sized writes. + for offset in range(0, size, 997): + client.sendall(body[offset:offset+997]); time.sleep(.001) + client.shutdown(socket.SHUT_WR) + assert response(client, 201) == digest.encode() + assert path.read_bytes() == body + empty_stage(); passed.append(f'binary-{size}') + + # No object is visible while only a prefix has been accepted. + prefix = b'not a committed artifact\x00\xff'*200 + digest, path = object_path(prefix) + with connect() as client: + client.sendall(headers(len(prefix)+100)+prefix) + wait_for(lambda: len(list(staging.iterdir())) == 1, 'no staging file') + assert not path.exists() + client.shutdown(socket.SHUT_WR) + assert response(client, 400) == b'Upload failed\n' + assert not path.exists(); empty_stage(); passed.append('early-eof-invisible') + + for name, wire, status in [ + ('oversized', headers(1048577), 413), + ('duplicate-length', b'POST /_arboricx/bundle HTTP/1.1\r\nHost: local\r\nContent-Length: 0\r\nContent-Length: 0\r\n\r\n', 400), + ('transfer-encoding', b'POST /_arboricx/bundle HTTP/1.1\r\nHost: local\r\nTransfer-Encoding: chunked\r\n\r\n', 400)]: + request(wire, status); empty_stage(); passed.append(name) + + # V1 grants reject an escaping staging symlink; v2 follows OS authority. + outside = root/'outside'; outside.mkdir() + staging.rmdir(); staging.symlink_to(outside, target_is_directory=True) + try: + if args.io_version == 1: + assert request(headers(0), 400) == b'Upload failed\n' + else: + assert request(headers(0), 201) == hashlib.sha256(b'').hexdigest().encode() + assert list(outside.iterdir()) == [] + finally: + staging.unlink(); staging.mkdir() + passed.append('open-denied' if args.io_version == 1 else 'os-authority-symlink') + + # An unusable staging path fails under either authority model. + staging.rmdir(); staging.write_bytes(b'unchanged') + try: + assert request(headers(0), 400) == b'Upload failed\n' + assert staging.read_bytes() == b'unchanged' + finally: + staging.unlink(); staging.mkdir() + passed.append('open-failed') + + body = b'commit failure must leave the original target intact' + digest, path = object_path(body) + path.mkdir(parents=True); (path/'marker').write_bytes(b'unchanged') + assert request(headers(len(body))+body, 400) == b'Upload failed\n' + assert (path/'marker').read_bytes() == b'unchanged' + empty_stage(); passed.append('commit-failed') + + clients = [] + try: + for _ in range(16): + client = connect(); clients.append(client) + client.sendall(headers(2)+b'A') + wait_for(lambda: len(list(staging.iterdir())) == 16, 'sessions did not open') + if args.io_version == 1: + assert request(headers(0), 400) == b'Upload failed\n' + assert len(list(staging.iterdir())) == 16 + assert request(b'GET /_arboricx/health HTTP/1.1\r\nHost: local\r\n\r\n', 200) == b'OK\n' + else: + # V2 bounds connections, not a separate native upload + # table. The seventeenth connection waits in the backlog + # until the oldest owned child is joined. + with connect() as queued: + queued.sendall(headers(0)); queued.shutdown(socket.SHUT_WR) + queued.settimeout(.2) + try: + unexpected = queued.recv(1) + except socket.timeout: + pass + else: + raise AssertionError(('connection admitted past window', unexpected)) + assert len(list(staging.iterdir())) == 16 + clients[0].shutdown(socket.SHUT_WR) + assert response(clients[0], 400) == b'Upload failed\n' + queued.settimeout(30) + assert response(queued, 201) == hashlib.sha256(b'').hexdigest().encode() + wait_for(lambda: len(list(staging.iterdir())) == 15, 'oldest connection not released') + for client in (clients if args.io_version == 1 else clients[1:]): + client.shutdown(socket.SHUT_WR) + assert response(client, 400) == b'Upload failed\n' + finally: + for client in clients: client.close() + wait_for(lambda: not list(staging.iterdir()), 'abandoned sessions not removed') + assert proc.poll() is None + assert request(b'GET /_arboricx/health HTTP/1.1\r\nHost: local\r\n\r\n', 200) == b'OK\n' + passed.append('session-limit-and-abandonment' if args.io_version == 1 else 'fifo-admission-and-abandonment') + finally: + proc.terminate() + try: proc.wait(timeout=5) + except subprocess.TimeoutExpired: + proc.kill(); proc.wait() + print(f'PASS IO v{args.io_version}:', ', '.join(passed)) + + +if __name__ == '__main__': + main() diff --git a/test/unit.py b/test/unit.py @@ -510,7 +510,51 @@ class BootstrapAcceptanceTests(unittest.TestCase): self.verify() +class SourceMappedKitTests(unittest.TestCase): + def test_projection_keeps_authored_sources_and_explicit_framing(self): + from prepare_compiler_kit import project + _, sources, worker, adapter = project(ROOT/'bootstrap/compiler-source-map.io-v2.v1', ROOT, 2, 'stable') + self.assertEqual(sources['compiler_io'], (ROOT/'lib/source/io-v2.tri').read_bytes()) + self.assertIn('sourceMapBuildStable = M.sourceMapBuildStable', worker) + self.assertIn('!import io_v2 as IO', adapter) + self.assertEqual(sources['source_object'], (ROOT/'lib/source/object.tri').read_bytes()) + with self.assertRaisesRegex(ValueError, 'lacks adapter IO module'): + project(ROOT/'bootstrap/compiler-source-map.v1', ROOT, 1, 'stable') + self.assertNotIn('io', sources) + self.assertIn('!import compiler_io *', sources['source_object'].decode()) + _, _, worker, adapter = project(ROOT/'bootstrap/compiler-source-map.v1', ROOT, 2, 'optimized') + self.assertIn('sourceMapBuild = M.sourceMapBuild', worker) + self.assertIn('!import io_v2 as IO', adapter) + + class ArchiveRecoveryPolicyTests(unittest.TestCase): + def test_recovery_binds_produced_version_and_adapter(self): + from bootstrap_archive import recovery_profile + import hashlib + with tempfile.TemporaryDirectory() as directory: + root = Path(directory); adapter = root/'adapter' + adapter.write_bytes(b'v2') + pin = hashlib.sha256(b'v2').hexdigest() + (root/'produced-io-adapter.arboricx').write_bytes(b'v2') + inputs = dict(execution_io_version=1, produced_io_version=2, + produced_adapter_sha256=pin, adapter_sha256='0'*64) + self.assertEqual(recovery_profile(root, inputs, adapter, True), 2) + with self.assertRaisesRegex(ValueError, 'accepted pair'): + recovery_profile(root, inputs, adapter, False) + adapter.write_bytes(b'wrong') + with self.assertRaisesRegex(ValueError, 'produced adapter'): + recovery_profile(root, inputs, adapter, True) + adapter.write_bytes(b'v2') + (root/'produced-io-adapter.arboricx').write_bytes(b'corrupt') + with self.assertRaisesRegex(ValueError, 'identity mismatch'): + recovery_profile(root, inputs, adapter, True) + del inputs['execution_io_version'] + with self.assertRaisesRegex(ValueError, 'incomplete'): + recovery_profile(root, inputs, adapter, True) + self.assertEqual(recovery_profile(root, {}, adapter, False), 1) + with self.assertRaisesRegex(ValueError, 'accepted output'): + recovery_profile(root, {'adapter_sha256':'0'*64}, adapter, True) + def test_scp_execution_is_not_a_compiler_restore_gate(self): archive = (ROOT / 'test/bootstrap_archive.py').read_text() self.assertNotIn('scp_native_validation.py', archive) @@ -581,6 +625,19 @@ class SupportProjectionTests(unittest.TestCase): self.assertFalse((ROOT / 'src/ScpCommand.hs').exists()) +class LibraryPublicationAliases(unittest.TestCase): + def test_authored_aliases_are_not_filename_guesses(self): + import importlib.util + spec = importlib.util.spec_from_file_location('prepare_libraries', ROOT/'ext/demo-site/prepare-libraries.py') + module = importlib.util.module_from_spec(spec); spec.loader.exec_module(module) + rows = module.library_sources("tricu.workspace.v1\nhandler_upload\tlib/handlers/upload.tri\nhandler_testing\tlib/handlers/testing.tri\n") + self.assertEqual(next(alias for alias,path,_ in rows if path=='lib/handlers/upload.tri'), 'handler_upload') + self.assertEqual(next(alias for alias,path,_ in rows if path=='lib/handlers/testing.tri'), 'handler_testing') + self.assertEqual({path for _,path,_ in rows}, {str(path.relative_to(ROOT)) for path in (ROOT/'lib').rglob('*.tri')}) + for text in ['bad', 'tricu.workspace.v1\na\tx\na\ty', 'tricu.workspace.v1\na\tx\nb\tx', 'tricu.workspace.v1\na\t../x']: + with self.assertRaises(ValueError): module.workspace_aliases(text) + + class SupportPublicationControls(unittest.TestCase): def test_resource_limits_fail_before_build_or_publication(self): import contextlib @@ -615,7 +672,8 @@ class CompilerDependencyTests(unittest.TestCase): from bootstrap_generations import source_map _, entries, required = source_map( ROOT / 'bootstrap/compiler-source-map.v1', ROOT, 'source_map') - self.assertEqual(entries['io'][2], ['base']) + self.assertEqual(entries['compiler_io'][2], ['base', 'io_v2', 'io_v2_storage', 'io_v2_text']) + self.assertNotIn('io', entries) self.assertNotIn('patterns', entries) self.assertEqual(set(entries), required) @@ -654,6 +712,19 @@ class FixtureSourceTests(unittest.TestCase): self.assertEqual(len(verify(self.fixtures)), 2) self.assertEqual(current_mismatches(self.fixtures, self.source), ['compiler.tri']) + def test_materialize_authenticates_reference_without_blessing_worktree(self): + from fixture_sources import materialize, current_mismatches + self.capture() + (self.source / 'support.tri').write_bytes(b'changed work') + target = self.root / 'reference' + materialize(self.fixtures, target) + self.assertEqual(current_mismatches(self.fixtures, target), []) + self.assertEqual(current_mismatches(self.fixtures, self.source), ['support.tri']) + with self.assertRaises(FileExistsError): materialize(self.fixtures, target) + next((self.fixtures / 'reference-source').glob('*.tri')).write_bytes(b'corrupt') + with self.assertRaises(ValueError): materialize(self.fixtures, self.root / 'bad') + self.assertFalse((self.root / 'bad').exists()) + def test_capture_cannot_bless_edited_sources(self): (self.source / 'support.tri').write_text('changed') with self.assertRaisesRegex(ValueError, 'does not match'): @@ -809,6 +880,24 @@ class LocalControlHarnessTests(unittest.TestCase): with self.assertRaises(ValueError): expect(expected+b'\0', expected, 'extra AST field') + def test_explicit_source_dependencies_cannot_override_or_escape(self): + from native_library_migration import dependency_sources, check_source_dependencies + import tempfile, json + from pathlib import Path + with tempfile.TemporaryDirectory() as directory: + root = Path(directory); (root/'extra.tri').write_text('!export main\nmain = 42\n') + rows = dependency_sources(root, ['extra=extra.tri'], {'base'}) + workspace = root/'workspace'; workspace.mkdir() + (workspace/'extra.tri').write_bytes((root/'extra.tri').read_bytes()) + (root/'selection.json').write_text(json.dumps(dict(source_dependencies=rows))) + check_source_dependencies(root, root) + for entries in [['base=extra.tri'], ['extra=extra.tri']*2, ['extra=../escape'], ['extra=/absolute'], ['!bad=extra.tri'], ['extra']]: + with self.assertRaises(ValueError): dependency_sources(root, entries, {'base'}) + (root/'outside.tri').symlink_to('/etc/passwd') + with self.assertRaises(ValueError): dependency_sources(root, ['extra=outside.tri'], set()) + (root/'extra.tri').write_text('!export main\nmain = 43\n') + with self.assertRaises(ValueError): check_source_dependencies(root, root) + def test_candidate_source_guard_rejects_old_facade_and_source_edits(self): from native_library_migration import checked_candidate_source import tempfile, hashlib @@ -1588,6 +1677,17 @@ class BootstrapKitTests(unittest.TestCase): self.metadata['artifacts']['adapter']['sha256'] = pin; self.save() with self.assertRaisesRegex(ValueError, 'digest'): self.load() + def test_explicit_versioned_kit(self): + self.metadata.update(schema='tricu.bootstrap-kit.v2', io_version=2); self.save() + self.load() + for bad in [True, '2', 0, 3, None]: + self.metadata['io_version'] = bad; self.save() + with self.assertRaises(ValueError): self.load() + self.metadata.pop('io_version'); self.save() + with self.assertRaises(ValueError): self.load() + self.metadata.update(schema='tricu.bootstrap-kit.v1', io_version=2); self.save() + with self.assertRaises(ValueError): self.load() + def test_missing_or_corrupt_adapter_fails(self): path = self.directory / 'io-adapter.arboricx' path.write_bytes(b'corrupt') @@ -1596,6 +1696,111 @@ class BootstrapKitTests(unittest.TestCase): with self.assertRaises(FileNotFoundError): self.load() +class CrossHostIOSelectionTests(unittest.TestCase): + def test_maps_and_sandbox_selection(self): + from bootstrap_cross_host import map_name, sandbox_command + for io,expected in [(1,'bootstrap/compiler-source-map.v1'),(2,'bootstrap/compiler-source-map.io-v2.v1')]: + self.assertEqual(map_name(io),expected) + command=sandbox_command(Path('/source'),Path('/kit'),Path('/stage'),Path('/tricu'),Path('/library'),'zig','source_map',32768,3600,io) + self.assertEqual(command[command.index('--map')+1],'/source/'+expected) + self.assertNotIn('--unsafe-io',command) + for bad in [True,'2',3]: + with self.assertRaises(ValueError): map_name(bad) + + def test_output_adapter_and_seed_are_bound(self): + from bootstrap_cross_host import kit_from_generation, freeze_kit + from bootstrap_inputs import load_kit, kit_io_version + with tempfile.TemporaryDirectory() as tmp: + root=Path(tmp); run=root/'generation'; run.mkdir() + (run/'rebuilt-compiler.arboricx').write_bytes(b'compiler') + (run/'io-adapter.arboricx').write_bytes(b'old adapter') + (run/'produced-io-adapter.arboricx').write_bytes(b'new adapter') + record=dict(execution_io_version=1,produced_io_version=2,produced_adapter_sha256=hashlib.sha256(b'new adapter').hexdigest()) + (run/'inputs.json').write_text(json.dumps(record)) + kit=kit_from_generation(run,root/'kit') + raw,payloads,_=load_kit(kit,'stable') + self.assertEqual(kit_io_version(json.loads(raw)),2) + self.assertEqual(payloads['adapter'],b'new adapter') + self.assertEqual(kit_from_generation(run,kit),kit) + with self.assertRaises(ValueError): freeze_kit(kit,root/'wrong',1) + self.assertFalse((root/'wrong').exists()) + freeze_kit(kit,root/'seed',2) + freeze_kit(kit,root/'seed',2) + (run/'produced-io-adapter.arboricx').write_bytes(b'corrupt') + with self.assertRaises(ValueError): kit_from_generation(run,root/'corrupt') + self.assertFalse((root/'corrupt').exists()) + (root/'seed/io-adapter.arboricx').write_bytes(b'corrupt') + with self.assertRaises(ValueError): freeze_kit(kit,root/'seed',2) + # Historical absent generation framing still converts to v1 only. + (run/'inputs.json').write_text(json.dumps(dict(adapter_sha256=hashlib.sha256(b'old adapter').hexdigest()))) + raw,payloads,_=load_kit(kit_from_generation(run,root/'legacy'),'stable') + self.assertEqual(kit_io_version(json.loads(raw)),1) + self.assertEqual(payloads['adapter'],b'old adapter') + + +class GenerationIOTests(unittest.TestCase): + def test_strict_versions_and_historical_default(self): + from generation_io import versions, version + self.assertEqual(versions({}), (1, 1)) + for bad in [True, False, 0, 3, '2', None, 2.0]: + with self.subTest(bad=bad), self.assertRaises(ValueError): version(bad) + for incomplete in [{'execution_io_version': 2}, {'produced_adapter_sha256': 'a'*64}]: + with self.assertRaises(ValueError): versions(incomplete) + with self.assertRaises(ValueError): versions(dict(execution_io_version=2, + produced_io_version=2, produced_adapter_sha256='A'*64)) + + def test_host_and_authority_are_explicit(self): + from generation_io import execution_flags, compiler_flags + self.assertEqual(compiler_flags(1), []) + self.assertEqual(compiler_flags(2), ['--toolchain-io-version', '2']) + self.assertEqual(execution_flags(1), ['--unsafe-io']) + self.assertEqual(execution_flags(1, 'zig'), ['--unsafe-io']) + self.assertEqual(execution_flags(2), []) + self.assertEqual(execution_flags(2, 'zig'), []) + for host in ['browser', 'unknown']: + with self.assertRaises(ValueError): execution_flags(2, host) + + def test_invocation_grants_and_canonical_cli_selection(self): + from generation_io import cli_version, check_invocation + import argparse + v2 = dict(execution_io_version=2, produced_io_version=2, produced_adapter_sha256='a'*64) + check_invocation(v2, ['tricu', 'arboricx', 'run', '--io']) + check_invocation({}, ['tricu', '--unsafe-io']) + for flag in ['--unsafe-io', '--allow-read=/tmp', '--allow-write', '--allow-read-all', '--allow-write-all']: + with self.assertRaises(ValueError): check_invocation(v2, ['tricu', flag]) + with self.assertRaises(ValueError): check_invocation({}, ['tricu', '--io']) + for text in ['01', '+2', '2.0', ' 2', '3', '-1']: + with self.assertRaises(argparse.ArgumentTypeError): cli_version(text) + self.assertEqual(cli_version('2'), 2) + + def test_transition_chain_not_identical_execution_framing(self): + from generation_io import check_pair + first = dict(adapter_sha256='a'*64, execution_io_version=1, + produced_io_version=2, produced_adapter_sha256='b'*64) + second = dict(adapter_sha256='b'*64, execution_io_version=2, + produced_io_version=2, produced_adapter_sha256='b'*64) + check_pair(first, second) + for key, value in [('execution_io_version', 1), ('produced_io_version', 1), + ('adapter_sha256', 'a'*64), ('produced_adapter_sha256', 'c'*64)]: + with self.subTest(key=key), self.assertRaises(ValueError): + check_pair(first, {**second, key: value}) + check_pair(dict(adapter_sha256='a'*64), dict(adapter_sha256='a'*64)) + + def test_produced_adapter_is_authenticated_not_input_adapter(self): + from generation_io import produced_adapter + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root/'io-adapter.arboricx').write_bytes(b'v1') + (root/'produced-io-adapter.arboricx').write_bytes(b'v2') + old = dict(adapter_sha256=hashlib.sha256(b'v1').hexdigest()) + new = dict(**old, execution_io_version=1, produced_io_version=2, + produced_adapter_sha256=hashlib.sha256(b'v2').hexdigest()) + self.assertEqual(produced_adapter(root, old), b'v1') + self.assertEqual(produced_adapter(root, new), b'v2') + (root/'produced-io-adapter.arboricx').write_bytes(b'v1') + with self.assertRaises(ValueError): produced_adapter(root, new) + + class IndexedObjectAuditTests(unittest.TestCase): def test_independent_writer_and_complete_audit(self): from native_indexed_store import indexed_bytes @@ -1628,8 +1833,11 @@ class InspectorStagingTests(unittest.TestCase): digest = hashlib.sha256(payload.read_bytes()).hexdigest() report = dict(complete=True, releases={'tricu-base': dict(version='0.1.0', manifest='a'*64, bundle=digest)}) with self.assertRaises(ValueError): stage_site(root, site, dict(report, complete=False)) + for version in (0, 3, True, "2"): + with self.assertRaises(ValueError): stage_site(root, site, dict(report, io_version=version)) + self.assertEqual(list((site / "bundles").iterdir()), []) stage_site(root, site, report) - before = index.read_bytes(); stage_site(root, site, report) + before = index.read_bytes(); stage_site(root, site, dict(report, io_version=2)) self.assertEqual(index.read_bytes(), before) self.assertEqual((site / 'bundles' / (digest + '.arboricx')).read_bytes(), payload.read_bytes()) payload.write_bytes(b'changed bytes') diff --git a/test/verified_tree_loading.py b/test/verified_tree_loading.py @@ -1,9 +1,6 @@ #!/usr/bin/env python3 -"""Opt-in typed-tree transport preparation and bounded load/publication/import probes. -Uses pinned artifacts and native packing; never invokes the recovery frontend. -""" +"""Retired v1 transport experiment: reject before touching retained artifacts.""" import argparse -import subprocess,json,re,os,pty,selectors,signal,time,tempfile from pathlib import Path parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('tricu',type=Path) @@ -15,150 +12,5 @@ parser.add_argument('--imports',action='store_true',help='also trace source_unit parser.add_argument('--publish',action='store_true',help='also publish raw parser trees into a separate CAS') parser.add_argument('--memory-mib',type=int,default=32768) parser.add_argument('--seconds',type=float,default=240) -args=parser.parse_args() -assert args.memory_mib>0 and args.seconds>0 -root=(args.directory or Path(tempfile.mkdtemp(prefix='tricu-typed-load-'))).resolve();root.mkdir(parents=True,exist_ok=True) -old=args.previous.resolve()/'cas';generation=args.generation.resolve() -store=root/'store';tools=root/'tools';tricu=str(args.tricu.resolve()) -# Retain the old self-built compiler, swapping only transport and importing -# module-loader definitions from the checkout against that transport. -cache=Path('lib/source/export-cache.tri').read_text() -cache='\n'.join(l for l in cache.splitlines() if not l.startswith('!import')) -module=Path('lib/source/module.tri').read_text() -loader=module[module.index('sourceModuleExtendUses_ self'):module.index('sourceModuleExportAll_ self')] -obj=Path('lib/source/object.tri').read_text();gettree=obj[obj.index('sourceObjectPutTree root'):] -# GetTree is currently the final definition; retain exact current transport source. -assert 'sourceObjectGetTree root hash =' in gettree and gettree.count('sourceObjectGetTree')==1 -io='\n'.join(l for l in Path('lib/io.tri').read_text().splitlines() - if l.startswith(('readTreeTerm =','hashTreeTerm =','writeTreeTermIfAbsent ='))) -cache=cache.replace('(environment : lazyResult','(environment : thenIO (putStrLn "imports-loaded") (lazyResult') -cache=cache.replace(' (definitions unused : y sourceExportCacheSelect_\n (fst (snd staged)) root scope definitions t)', - ' (definitions unused : thenIO (putStrLn "compile-done")\n (y sourceExportCacheSelect_ (fst (snd staged)) root scope definitions t))') -cache=cache.replace('(compileModuleDefinitions environment (snd (snd staged)))))', - '(compileModuleDefinitions environment (snd (snd staged))))))') -body=io+'\n'+gettree+'\n'+loader+'\n'+cache+''' -main cas path compiler = (io (onOk_ (readFile path) (source : - thenIO (putStrLn "imports-start") (onResult_ (sourceExportCacheBuild cas source compiler) - (problem : pure (err problem t)) (manifest : pure (ok manifest t)))))) -load cas hash = (io (onResult_ (sourceObjectGetTree cas hash) - (problem : pure (err problem t)) (tree : pure (ok t t)))) -publish cas tree = io (sourceObjectPutTree cas tree) -runCompiler compiler source = (lazyResult - (problem unused : err problem t) - (definitions unused : lazyMaybe (err "missing main" t) (tree : ok tree t) - (sourceFindExternal definitions "main")) - (compiler t source)) -''' -locals=set(re.findall(r'^([A-Za-z_][A-Za-z0-9_?\-]*)[^\n=]*=',body,re.M)) -words=set(re.findall(r'[A-Za-z_][A-Za-z0-9_?\-]*',body));selected={} -for mod in ['prelude','io','source_object','source_module','source_lock','source_build','source_unit','source_resolve']: - manifest=(old/'cache/source-build'/mod).read_text().split('\t')[2] - for row in (old/'objects'/manifest[:3]/manifest).read_text().splitlines()[1:]: - _,name,_,tree,_=row.split('\t') - if name in words and name not in locals:selected[name]=tree -cmd=[tricu,'arboricx','export','--store',str(old),'--output',str(root/'support.arboricx')] -for name,tree in selected.items():cmd+=['--target',tree,'--name',name] -subprocess.run(cmd,check=True,timeout=180) -def imported(bundle,destination,indexed=False): - reply=subprocess.check_output([tricu,'arboricx','import','--file',str(bundle),'--store',str(destination),*(['--indexed'] if indexed else ['--flat'])],text=True) - return re.search(r'Created module alias .* -> ([0-9a-f]{64})',reply)[1] -support=imported(root/'support.arboricx',store) -(root/'support.tri').write_text(f'!export * from {support}\n') -(root/'probe.tri').write_text('!import support *\n!export main\n!export load\n!export publish\n!export runCompiler\n'+body) -compiler=imported(args.compiler_bundle.resolve(),tools,True) -subprocess.run([tricu,'arboricx','compile','--file',str(root/'probe.tri'),'--store',str(store),'--toolchain',compiler,'--tool-store',str(tools),'--output',str(root/'probe.arboricx'),'--compile-timeout','240','+RTS','-N1','-RTS'],check=True,timeout=270) -(root/'probe.pin').write_text(imported(root/'probe.arboricx',store,True)) -(root/'selected.json').write_text(json.dumps(selected,indent=2)) -print('probe', (root/'probe.pin').read_text(),flush=True) - -rows=[] -def measure(label,cmd): - master,slave=pty.openpty();proc=subprocess.Popen(cmd,stdout=slave,stderr=slave,start_new_session=True);os.close(slave) - selector=selectors.DefaultSelector();selector.register(master,selectors.EVENT_READ) - start=time.monotonic();peak=0;pending=b'';lines=[];reason='completed' - try: - while proc.poll() is None: - for _,_ in selector.select(.1): - try:data=os.read(master,65536) - except OSError:data=b'' - pending+=data - while b'\n' in pending: - line,pending=pending.split(b'\n',1);line=line.decode(errors='replace').rstrip('\r') - lines.append(dict(seconds=time.monotonic()-start,line=line)) - if line in ['verified','decoded'] or line.startswith(('ok:','imports-','compile-','export-')):print(label,line,round(time.monotonic()-start,3),flush=True) - try: - status=Path(f'/proc/{proc.pid}/status').read_text() - rss=int(next(line.split()[1] for line in status.splitlines() if line.startswith('VmHWM:')));peak=max(peak,rss) - except (FileNotFoundError,ProcessLookupError,StopIteration):rss=0 - if rss>args.memory_mib*1024:reason='RSS guard';break - if time.monotonic()-start>args.seconds:reason='wall guard';break - finally: - if proc.poll() is None: - os.killpg(proc.pid,signal.SIGTERM) - try:proc.wait(timeout=10) - except subprocess.TimeoutExpired:os.killpg(proc.pid,signal.SIGKILL);proc.wait() - # Drain final result and RTS statistics after exit. - while True: - try:data=os.read(master,65536) - except OSError:break - if not data:break - pending+=data - for line in pending.decode(errors='replace').splitlines():lines.append(dict(seconds=time.monotonic()-start,line=line)) - selector.close();os.close(master) - row=dict(label=label,code=proc.returncode,reason=reason,wall_seconds=time.monotonic()-start,peak_kib=peak,lines=lines,command=cmd) - rows.append(row);(root/'load-results.json').write_text(json.dumps(rows,indent=2)+'\n') - print(label,reason,proc.returncode,round(row['wall_seconds'],3),'s',round(peak/1024,2),'MiB',flush=True) - return row -def export_info(cas,module,name): - manifest=(cas/'cache/source-build'/module).read_text().split('\t')[2] - tree=next(r.split('\t')[3] for r in (cas/'objects'/manifest[:3]/manifest).read_text().splitlines()[1:] if r.split('\t')[1]==name) - return tree,(cas/'objects'/tree[:3]/tree).stat().st_size -for label,cas in [('historical-parser',old),('current-parser',generation/'cas')]: - tree,size=export_info(cas,'source_program','parseGateProgram') - row=measure(label+'/verified-native',[tricu,'arboricx','run','--store',str(store),'--module',(root/'probe.pin').read_text(),'--name','load','--arg',str(cas),'--arg',tree,'--io','--unsafe-io','--result','--format','fsl','--profile','+RTS','-N1','-T','-s','-RTS']) - row['raw_bytes']=size - assert row['code']==0 and any(l['line'].startswith('ok: Leaf') for l in row['lines']), row - if args.publish: - destination=root/('published-'+label) - payload=cas/'objects'/tree[:3]/tree - for iteration in range(2): - published=measure(label+'/publication-'+str(iteration),[tricu,'arboricx','run','--store',str(store),'--module',(root/'probe.pin').read_text(),'--name','publish','--arg',str(destination),'--arg-tree-file',str(payload),'--io','--unsafe-io','--result','--format','decode','--profile','+RTS','-N1','-T','-s','-RTS']) - assert published['code']==0 and any(('ok: "'+tree+'"') in l['line'] for l in published['lines']), published - assert (destination/'objects'/tree[:3]/tree).read_bytes()==payload.read_bytes() - print(label+': independent raw-byte publication agreement',flush=True) -if args.imports: - locked=[] - for line in (generation/'overlay/source_unit.tri').read_text().splitlines(True): - if line.startswith('!source '):continue - if line.startswith('!import '): - alias=line.split()[1];pin=(generation/'cas/cache/source-build'/alias).read_text().split('\t')[2] - line=line.replace(alias,pin,1) - locked.append(line) - source=root/'source_unit.locked.tri';source.write_text(''.join(locked)) - # Distinct diagnostic identity avoids declaring the unmodified map-builder - # while exercising a patched IO transport profile. Local export checkpoints - # cannot be mistaken for the ordinary compiler's cache scope. - import hashlib - identity=hashlib.sha256((root/'probe.arboricx').read_bytes()).hexdigest() - unit=measure('source-unit/imports-and-exports',[tricu,'arboricx','run','--store',str(store),'--module',(root/'probe.pin').read_text(),'--name','main','--arg',str(generation/'cas'),'--arg',str(source),'--arg',identity,'--io','--unsafe-io','--result','--format','decode','+RTS','-N1','-T','-s','-RTS']) - if unit['code']==0: - manifest=next(re.search(r'ok: "([0-9a-f]{64})"',l['line'])[1] for l in unit['lines'] if re.search(r'ok: "([0-9a-f]{64})"',l['line'])) - cas=generation/'cas' - payload=(cas/'objects'/manifest[:3]/manifest).read_bytes() - assert hashlib.sha256(b'arboricx.module-manifest.v1\0'+payload).hexdigest()==manifest - exports={r.split('\t')[1]:r.split('\t')[3] for r in payload.decode().splitlines()[1:]} - for tree in set(exports.values()): - checksum=hashlib.sha256(b'arboricx.tree-term.v1\0') - with (cas/'objects'/tree[:3]/tree).open('rb') as f: - while chunk:=f.read(1024*1024):checksum.update(chunk) - assert checksum.hexdigest()==tree - assert len(exports)==13 - (root/'rebuilt-unit.pin').write_text(manifest) - subprocess.run([tricu,'arboricx','export','--store',str(cas),'--module',manifest,'--output',str(root/'rebuilt-unit.arboricx')],check=True,timeout=180) - for backend,text,expected in [('compileModuleDefinitions','main = 23\n','23'),('compileStableModuleDefinitions','walk t = 7\nwalk (t x) = walk x\nwalk input = 9\nmain = walk (t (t t))\n','7')]: - tree=exports[backend] - proof=measure('rebuilt-unit/'+backend,[tricu,'arboricx','run','--store',str(store),'--module',(root/'probe.pin').read_text(),'--name','runCompiler','--arg-tree-file',str(cas/'objects'/tree[:3]/tree),'--arg',text,'--result','--format','number','+RTS','-N1','-T','-s','-RTS']) - assert proof['code']==0 and any(l['line'].startswith('ok: '+expected) for l in proof['lines']),proof - print('rebuilt unit: all 13 export digests verified; optimized and stable compiler application checks passed',flush=True) -(root/'load-results.json').write_text(json.dumps(rows,indent=2)+'\n') -print('Artifacts: '+str(root),flush=True) +parser.parse_args() +parser.error("Retired v1 source-splicing probe; use io_v2_indexed.py, io_v2_indexed_write.py and io_v2_compiler_store.py with the current compiler.") diff --git a/test/zig_generation_executor.py b/test/zig_generation_executor.py @@ -3,8 +3,8 @@ The supervisor authenticates/imports captured bundles, supplies six arguments and independently audits all publications. This child rechecks captured bundle -digests and runs the selected entrypoint through the owned ABI. It grants broad -file IO to trusted compiler jobs, matching the generation runner's unsafe-IO scope. +digests and runs the selected entrypoint through the owned ABI. V1 grants broad file IO to trusted compiler jobs. Explicit v2 requests use the +same native v2 interpreter as the CLI, with OS process authority and no grants. """ import argparse import ctypes as C @@ -13,12 +13,29 @@ import json from pathlib import Path import resource import time +from generation_io import version, versions def save(path, value): path.write_text(json.dumps(value, indent=2) + '\n') +def validate_library(path, io_version): + """Trusted selected runtime preflight, no IO execution or fallback.""" + selected = version(io_version) + library = C.CDLL(str(path)) + abi = library.arb_abi_version + abi.argtypes, abi.restype = [], C.c_uint32 + if abi() not in (2, 3): + raise ValueError('owned ABI version 2 or 3 required') + if abi() == 3 and selected != 2: + raise ValueError('IO v1 requires a retained ABI 2 runtime') + for name in ('arboricx_init','arboricx_free','arb_load_bundle','arb_app', + 'arb_of_number','arb_of_list','arb_of_string','arb_release', + 'arb_reduce','arb_unwrap_result','arb_run_io' if selected == 1 else 'arb_run_io_v2'): + getattr(library,name) + + def child(args): # No asynchronous subprocesses in the Zig IO driver; the parent additionally # guards this whole process group with a wall deadline and RSS sampling. @@ -34,8 +51,8 @@ def child(args): return f init = bind('arboricx_init', [], ctx_t) free = bind('arboricx_free', [ctx_t], None) - version = bind('arb_abi_version', [], C.c_uint32) - assert version() == 2 + abi_version = bind('arb_abi_version', [], C.c_uint32) + assert abi_version() in (2, 3) load = bind('arb_load_bundle', [ctx_t, C.c_void_p, C.c_size_t, C.c_char_p, C.POINTER(handle)]) string = bind('arb_of_string', [ctx_t, C.c_char_p, C.POINTER(handle)]) app = bind('arb_app', [ctx_t, handle, handle, C.POINTER(handle)]) @@ -48,7 +65,8 @@ def child(args): free_buf = bind('arboricx_free_buf', [ctx_t, C.c_void_p, C.c_size_t], None) class Perms(C.Structure): _fields_ = [('read', C.c_int), ('write', C.c_int)] - run_io = bind('arb_run_io', [ctx_t, handle, C.POINTER(Perms), C.POINTER(handle)]) + of_number = bind('arb_of_number', [ctx_t, C.c_uint64, C.POINTER(handle)]) + of_list = bind('arb_of_list', [ctx_t, C.POINTER(handle), C.c_size_t, C.POINTER(handle)]) def check(status): if status != 0: raise RuntimeError(f'ABI status {status}') @@ -67,6 +85,16 @@ def child(args): request = json.loads((run / 'execution-request.json').read_text()) started = time.monotonic() try: + execution_io = version(request.get('execution_io_version', 1)) + recorded = json.loads((run / 'inputs.json').read_text()) + if execution_io != versions(recorded)[0]: + raise ValueError('execution request IO version mismatch') + for key in ('worker_export','compiler_bundle_sha256','adapter_sha256'): + if request[key] != recorded[key]: + raise ValueError('execution request identity mismatch: '+key) + # Symbol selection is declared metadata, never a trial execution. Old + # libraries remain usable for v1; missing v2 symbols are terminal. + selected_io = bind('arb_run_io_v2', [ctx_t, handle, C.POINTER(handle)]) if execution_io == 2 else bind('arb_run_io', [ctx_t, handle, C.POINTER(Perms), C.POINTER(handle)]) if args.gc_policy: configure = bind('arb_set_gc_policy', [ctx_t, C.c_uint32, C.c_uint32]) check(configure(ctx, *args.gc_policy)) @@ -80,12 +108,20 @@ def child(args): for argument in request['arguments']: if '\0' in argument: raise ValueError('NUL in compiler argument') - current = apply(current, owned(string, argument.encode())) + if execution_io == 1: + value = owned(string, argument.encode()) + else: + # ABI byte strings and language scalar text are different. + scalars = [owned(of_number, ord(ch)) for ch in argument] + items = (handle * max(1,len(scalars)))(*scalars) + value = owned(of_list, items, len(scalars)) + for scalar in scalars: check(release(ctx,scalar)) + current = apply(current,value) prepared = time.monotonic() program = owned(reduce, current, 1_000_000_000) check(release(ctx, current)) forced = time.monotonic() - result = owned(run_io, program, C.byref(Perms(1, 1))) + result = owned(selected_io, program) if execution_io == 2 else owned(selected_io, program, C.byref(Perms(1, 1))) check(release(ctx, program)) ran = time.monotonic() ok, value, rest = C.c_int(), handle(), handle() @@ -111,7 +147,7 @@ def child(args): check(release(ctx, normalized)) if diagnostic is None: diagnostic = f'non-string diagnostic (decode status {status})' - record = dict(ok=bool(ok.value), diagnostic=diagnostic, + record = dict(ok=bool(ok.value), diagnostic=diagnostic, execution_io_version=execution_io, prepare_seconds=prepared-started, initial_reduce_seconds=forced-prepared, io_seconds=ran-forced, total_seconds=time.monotonic()-started) if args.gc_policy: diff --git a/tricu-apps/arboricxServer.tri b/tricu-apps/arboricxServer.tri @@ -1,30 +1,10 @@ -!import io * -!import arboricx_server as Registry +!import io_v2 as IO +!import arboricx_handlers as Registry +!import arboricx_streaming_v2 as Server !export main --- Arboricx HTTP registry server. +-- Scoped registry with a 1-MiB body/object budget and FIFO16 admission. +-- Store initialization and listener failures terminate startup. -- Build/deploy: nix build .#arboricxServer (see docs/build-and-run.md). --- The runner grants reads/writes only to ./store; dependencies are compiled --- from their declared locations in the repository tricu.workspace. --- --- Endpoints: --- GET / -> HTML landing page --- GET /_arboricx/health -> "OK" --- POST /_arboricx/bundle -> upload bundle, returns hash --- GET /_arboricx/bundle/hash/:h -> download bundle by hash --- GET /_arboricx/package/:n/:v -> administratively published release --- --- Example usage: --- curl http://localhost:8080/ --- curl http://localhost:8080/_arboricx/health --- curl -X POST --data-binary @mybundle.arboricx http://localhost:8080/_arboricx/bundle --- curl http://localhost:8080/_arboricx/bundle/hash/<hash> - --- Initialization and bind/listen failures must terminate the process, not leave --- a deployment looking healthy after silently returning an error Result. -main = io (onResult_ - (do onOk_ - Registry.ensureStore "./store" - Registry.arboricxServer "./store" "0.0.0.0" 8080) - abortIO - pure) +main = IO.io (Server.serve "./store" 1048576 Registry.stdoutLogV2 + (IO.ipv4 [0 0 0 0] 8080) 16 (_ : IO.pure t)) diff --git a/tricu-apps/arboricxStreamingV2.tri b/tricu-apps/arboricxStreamingV2.tri @@ -0,0 +1,8 @@ +!import base as B +!import io_v2 as IO +!import arboricx_handlers as Registry +!import arboricx_streaming_v2 as Server +!export main +-- Opt-in native OS authority. Bounded FIFO connection admission; no deadlines. +main = IO.io (Server.serve "./store" 1048576 Registry.stdoutLogV2 + (IO.ipv4 [0 0 0 0] 8080) 16 (_ : IO.pure t)) diff --git a/tricu.cabal b/tricu.cabal @@ -63,6 +63,7 @@ executable tricu , text , time , transformers + , process , unix , vector , zlib @@ -78,11 +79,28 @@ executable tricu NodeTable ContentStore.Bundle ContentStore.Indexed + ContentStore.Snapshot ContentStore.Executable ContentStore.Filesystem ContentStore.Object ContentStore.Resolver IODriver + IOV2.Resolver + IOV2.Network + IOV2.Filesystem + IOV2.Resources + IOV2.Driver + IOV2.Protocol + IOV2.Wire + IOV2.Tasks + IOV2.Staging + IOV2.Digest + IOV2.Codec + IOV2.Clock + IOV2.Process + IOV2.Context + IOV2.UTF8 + IOV2.Descriptor Module.Manifest Paths_tricu REPL @@ -168,6 +186,7 @@ test-suite tricu-tests , text , time , transformers + , process , unix , vector , zlib @@ -189,11 +208,28 @@ test-suite tricu-tests NodeTable ContentStore.Bundle ContentStore.Indexed + ContentStore.Snapshot ContentStore.Executable ContentStore.Filesystem ContentStore.Object ContentStore.Resolver IODriver + IOV2.Resolver + IOV2.Network + IOV2.Filesystem + IOV2.Resources + IOV2.Driver + IOV2.Protocol + IOV2.Wire + IOV2.Tasks + IOV2.Staging + IOV2.Digest + IOV2.Codec + IOV2.Clock + IOV2.Process + IOV2.Context + IOV2.UTF8 + IOV2.Descriptor Module.Manifest Paths_tricu REPL @@ -212,6 +248,21 @@ test-suite tricu-tests ParserValueTests PortableTestCompiler IODriverTests + IOV2Tests + IOV2ClockTests + IOV2ProcessTests + IOV2ContextTests + IOV2ProtocolTests + IOV2TaskTests + IOV2ResolverTests + SnapshotTests + IndexedWriterTests + IOV2SyncTests + IOV2UTF8Tests + IOV2NetworkTests + IOV2FilesystemTests + IOV2PositionTests + IOV2StandardTests + IOV2StorageTests IOBoundaryTests - ScriptedEffects ReferenceFixtures diff --git a/tricu.workspace b/tricu.workspace @@ -40,3 +40,18 @@ source_program lib/source/program.tri source_resolve lib/source/resolve.tri source_stable lib/source/stable.tri source_unit lib/source/unit.tri +handlers lib/handlers.tri +handler_log lib/handlers/log.tri +handler_artifacts lib/handlers/artifacts.tri +handler_testing lib/handlers/testing.tri +arboricx_handlers lib/arboricx/handlers.tri +handler_upload lib/handlers/upload.tri +arboricx_streaming lib/arboricx/streaming.tri +io_v2 lib/io-v2.tri +io_v2_artifacts lib/io-v2/artifacts.tri +io_v2_objects lib/io-v2/objects.tri +io_v2_text lib/io-v2/text.tri +io_v2_storage lib/io-v2/storage.tri +io_v2_http lib/io-v2/http-transport.tri +arboricx_streaming_v2 lib/arboricx/streaming-v2.tri +compiler_io lib/source/io-v2.tri