functionalscript 0.44.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (571) hide show
  1. package/README.md +5 -3
  2. package/fjs/asn.1/module.f.mjs +8 -1
  3. package/fjs/asn.1/proof.f.d.mts +15 -0
  4. package/fjs/asn.1/proof.f.mjs +31 -3
  5. package/fjs/asserts/module.f.d.mts +14 -0
  6. package/fjs/asserts/module.f.mjs +20 -0
  7. package/fjs/asserts/proof.f.d.mts +3 -0
  8. package/fjs/asserts/proof.f.mjs +15 -1
  9. package/fjs/basen/base128/module.f.mjs +22 -5
  10. package/fjs/basen/base128/proof.f.d.mts +3 -1
  11. package/fjs/basen/base128/proof.f.mjs +3 -1
  12. package/fjs/basen/base64/module.f.mjs +7 -3
  13. package/fjs/basen/base64/proof.f.d.mts +3 -1
  14. package/fjs/basen/base64/proof.f.mjs +7 -4
  15. package/fjs/basen/cbase32/proof.f.d.mts +3 -1
  16. package/fjs/basen/cbase32/proof.f.mjs +3 -1
  17. package/fjs/bnf/data/module.f.d.mts +32 -6
  18. package/fjs/bnf/data/module.f.mjs +112 -7
  19. package/fjs/bnf/data/proof.f.d.mts +2 -0
  20. package/fjs/bnf/data/proof.f.mjs +74 -4
  21. package/fjs/bnf/data/types.d.ts +20 -2
  22. package/fjs/bnf/descent/module.f.d.mts +34 -5
  23. package/fjs/bnf/descent/module.f.mjs +176 -54
  24. package/fjs/bnf/descent/proof.f.d.mts +3 -0
  25. package/fjs/bnf/descent/proof.f.mjs +189 -27
  26. package/fjs/bnf/descent/types.d.ts +24 -18
  27. package/fjs/bnf/ll1/module.f.d.mts +74 -6
  28. package/fjs/bnf/ll1/module.f.mjs +297 -95
  29. package/fjs/bnf/ll1/proof.f.d.mts +17 -2
  30. package/fjs/bnf/ll1/proof.f.mjs +372 -137
  31. package/fjs/bnf/ll1/types.d.ts +25 -27
  32. package/fjs/bnf/matcher/module.f.d.mts +66 -0
  33. package/fjs/bnf/matcher/module.f.mjs +81 -0
  34. package/fjs/bnf/matcher/proof.f.d.mts +10 -0
  35. package/fjs/bnf/matcher/proof.f.mjs +79 -0
  36. package/fjs/bnf/matcher/types.d.ts +54 -0
  37. package/fjs/bnf/module.f.d.mts +15 -6
  38. package/fjs/bnf/module.f.mjs +52 -11
  39. package/fjs/bnf/proof.f.d.mts +2 -0
  40. package/fjs/bnf/proof.f.mjs +79 -3
  41. package/fjs/bnf/testlib.f.d.mts +31 -0
  42. package/fjs/bnf/testlib.f.mjs +80 -0
  43. package/fjs/bnf/token_symbol/module.f.d.mts +4 -3
  44. package/fjs/bnf/token_symbol/module.f.mjs +7 -6
  45. package/fjs/bnf/token_symbol/proof.f.mjs +5 -2
  46. package/fjs/bnf/token_symbol/types.d.ts +2 -2
  47. package/fjs/bnf/types.d.ts +10 -3
  48. package/fjs/cas/cli/module.f.d.mts +1 -1
  49. package/fjs/cas/cli/module.f.mjs +14 -20
  50. package/fjs/cas/cli/proof.f.d.mts +4 -4
  51. package/fjs/cas/cli/proof.f.mjs +47 -34
  52. package/fjs/cas/evo/module.f.d.mts +71 -31
  53. package/fjs/cas/evo/module.f.mjs +223 -138
  54. package/fjs/cas/evo/proof.f.d.mts +10 -1
  55. package/fjs/cas/evo/proof.f.mjs +311 -226
  56. package/fjs/cas/evo/types.d.ts +45 -9
  57. package/fjs/cas/module.f.d.mts +18 -32
  58. package/fjs/cas/module.f.mjs +164 -176
  59. package/fjs/cas/proof.f.d.mts +11 -8
  60. package/fjs/cas/proof.f.mjs +259 -147
  61. package/fjs/cas/types.d.ts +24 -10
  62. package/fjs/ci/common/module.f.d.mts +1 -1
  63. package/fjs/ci/common/module.f.mjs +1 -0
  64. package/fjs/ci/config/module.f.d.mts +3 -3
  65. package/fjs/ci/config/module.f.mjs +3 -3
  66. package/fjs/ci/module.f.d.mts +5 -5
  67. package/fjs/ci/module.f.mjs +8 -7
  68. package/fjs/ci/nix/module.f.d.mts +7 -5
  69. package/fjs/ci/nix/module.f.mjs +13 -12
  70. package/fjs/ci/nix/proof.f.d.mts +2 -0
  71. package/fjs/ci/nix/proof.f.mjs +4 -4
  72. package/fjs/ci/proof.f.d.mts +4 -2
  73. package/fjs/ci/proof.f.mjs +13 -8
  74. package/fjs/cli/module.f.d.mts +4 -6
  75. package/fjs/cli/module.f.mjs +4 -8
  76. package/fjs/cli/proof.f.d.mts +4 -2
  77. package/fjs/cli/proof.f.mjs +21 -18
  78. package/fjs/cli/types.d.ts +2 -3
  79. package/fjs/common/monoid/module.f.d.mts +70 -12
  80. package/fjs/common/monoid/module.f.mjs +177 -14
  81. package/fjs/common/monoid/proof.f.d.mts +12 -1
  82. package/fjs/common/monoid/proof.f.mjs +88 -4
  83. package/fjs/common/monoid/types.d.ts +23 -0
  84. package/fjs/crypto/hmac/module.f.mjs +2 -2
  85. package/fjs/crypto/secp/proof.f.d.mts +3 -1
  86. package/fjs/crypto/secp/proof.f.mjs +3 -1
  87. package/fjs/crypto/sha2/module.f.mjs +3 -1
  88. package/fjs/crypto/sha2/proof.f.d.mts +4 -1
  89. package/fjs/crypto/sha2/proof.f.mjs +27 -1
  90. package/fjs/crypto/sha2/types.d.ts +11 -0
  91. package/fjs/crypto/sign/module.f.mjs +2 -2
  92. package/fjs/crypto/vdf/module.f.d.mts +23 -4
  93. package/fjs/crypto/vdf/module.f.mjs +6 -4
  94. package/fjs/dev/module.f.d.mts +17 -5
  95. package/fjs/dev/module.f.mjs +60 -30
  96. package/fjs/dev/update/module.f.d.mts +11 -3
  97. package/fjs/dev/update/module.f.mjs +16 -11
  98. package/fjs/dev/update/proof.f.d.mts +4 -3
  99. package/fjs/dev/update/proof.f.mjs +11 -7
  100. package/fjs/djs/module.f.d.mts +13 -5
  101. package/fjs/djs/module.f.mjs +31 -16
  102. package/fjs/djs/parser/module.f.d.mts +16 -3
  103. package/fjs/djs/parser/module.f.mjs +135 -16
  104. package/fjs/djs/parser/proof.f.d.mts +8 -1
  105. package/fjs/djs/parser/proof.f.mjs +287 -13
  106. package/fjs/djs/parser/types.d.ts +7 -1
  107. package/fjs/djs/proof.f.d.mts +20 -1
  108. package/fjs/djs/proof.f.mjs +190 -12
  109. package/fjs/djs/serializer/module.f.d.mts +25 -8
  110. package/fjs/djs/serializer/module.f.mjs +66 -19
  111. package/fjs/djs/serializer/proof.f.d.mts +5 -0
  112. package/fjs/djs/serializer/proof.f.mjs +23 -1
  113. package/fjs/djs/tokenizer/module.f.d.mts +17 -5
  114. package/fjs/djs/tokenizer/module.f.mjs +120 -73
  115. package/fjs/djs/tokenizer/proof.f.d.mts +3 -0
  116. package/fjs/djs/tokenizer/proof.f.mjs +86 -67
  117. package/fjs/djs/transpiler/module.f.d.mts +13 -19
  118. package/fjs/djs/transpiler/module.f.mjs +79 -72
  119. package/fjs/djs/transpiler/types.d.ts +29 -0
  120. package/fjs/djs/types.d.ts +7 -1
  121. package/fjs/effects/list/module.f.d.mts +23 -12
  122. package/fjs/effects/list/module.f.mjs +16 -12
  123. package/fjs/effects/list/types.d.ts +27 -7
  124. package/fjs/effects/memory/module.f.d.mts +6 -3
  125. package/fjs/effects/memory/module.f.mjs +7 -7
  126. package/fjs/effects/memory/proof.f.d.mts +4 -0
  127. package/fjs/effects/memory/proof.f.mjs +18 -9
  128. package/fjs/effects/memory/types.d.ts +4 -3
  129. package/fjs/effects/mock/module.f.d.mts +28 -4
  130. package/fjs/effects/mock/module.f.mjs +39 -5
  131. package/fjs/effects/mock/types.d.ts +10 -1
  132. package/fjs/effects/module.d.mts +12 -2
  133. package/fjs/effects/module.f.d.mts +469 -281
  134. package/fjs/effects/module.f.mjs +538 -299
  135. package/fjs/effects/module.mjs +11 -2
  136. package/fjs/effects/node/memory/module.d.mts +7 -2
  137. package/fjs/effects/node/memory/module.mjs +9 -5
  138. package/fjs/effects/node/memory/proof.d.mts +2 -0
  139. package/fjs/effects/node/memory/proof.mjs +12 -5
  140. package/fjs/effects/node/module.d.mts +8 -2
  141. package/fjs/effects/node/module.f.d.mts +183 -43
  142. package/fjs/effects/node/module.f.mjs +260 -62
  143. package/fjs/effects/node/module.mjs +61 -50
  144. package/fjs/effects/node/proof.f.d.mts +28 -2
  145. package/fjs/effects/node/proof.f.mjs +161 -42
  146. package/fjs/effects/node/types.d.ts +106 -18
  147. package/fjs/effects/node/virtual/module.f.d.mts +21 -4
  148. package/fjs/effects/node/virtual/module.f.mjs +113 -70
  149. package/fjs/effects/node/virtual/proof.f.d.mts +31 -2
  150. package/fjs/effects/node/virtual/proof.f.mjs +201 -8
  151. package/fjs/effects/proof.f.d.mts +69 -34
  152. package/fjs/effects/proof.f.mjs +413 -130
  153. package/fjs/effects/types.d.ts +161 -33
  154. package/fjs/emergent_testing/all.test.d.mts +8 -0
  155. package/fjs/emergent_testing/all.test.mjs +14 -0
  156. package/fjs/emergent_testing/module.f.d.mts +22 -10
  157. package/fjs/emergent_testing/module.f.mjs +119 -85
  158. package/fjs/emergent_testing/proof.f.d.mts +21 -7
  159. package/fjs/emergent_testing/proof.f.mjs +169 -35
  160. package/fjs/emergent_testing/types.d.ts +22 -4
  161. package/fjs/fsc/bnf.f.d.mts +3 -0
  162. package/fjs/fsc/bnf.f.mjs +4 -1
  163. package/fjs/fsc/json.f.d.mts +3 -0
  164. package/fjs/fsc/json.f.mjs +4 -1
  165. package/fjs/fsc/module.f.d.mts +9 -0
  166. package/fjs/fsc/module.f.mjs +5 -3
  167. package/fjs/fsm/module.f.d.mts +14 -4
  168. package/fjs/fsm/module.f.mjs +54 -37
  169. package/fjs/fsm/proof.f.d.mts +5 -0
  170. package/fjs/fsm/proof.f.mjs +87 -115
  171. package/fjs/js/keywords/module.f.d.mts +52 -0
  172. package/fjs/js/keywords/module.f.mjs +72 -0
  173. package/fjs/{emergent_testing/scenarios/return-value.pass.d.ts → js/keywords/proof.f.d.mts} +1 -1
  174. package/fjs/js/keywords/proof.f.mjs +13 -0
  175. package/fjs/js/tokenizer/module.f.d.mts +30 -3
  176. package/fjs/js/tokenizer/module.f.mjs +161 -211
  177. package/fjs/js/tokenizer/proof.f.d.mts +1 -0
  178. package/fjs/js/tokenizer/proof.f.mjs +54 -24
  179. package/fjs/js/tokenizer/types.d.ts +33 -24
  180. package/fjs/mcp/cas/module.f.d.mts +1 -6
  181. package/fjs/mcp/cas/module.f.mjs +55 -51
  182. package/fjs/mcp/cas/proof.f.d.mts +15 -0
  183. package/fjs/mcp/cas/proof.f.mjs +174 -0
  184. package/fjs/mcp/evo/module.f.d.mts +19 -10
  185. package/fjs/mcp/evo/module.f.mjs +48 -27
  186. package/fjs/mcp/evo/proof.f.d.mts +6 -1
  187. package/fjs/mcp/evo/proof.f.mjs +115 -31
  188. package/fjs/mcp/module.f.d.mts +39 -4
  189. package/fjs/mcp/module.f.mjs +14 -12
  190. package/fjs/mcp/proof.f.d.mts +13 -1
  191. package/fjs/mcp/proof.f.mjs +120 -61
  192. package/fjs/media/html/module.f.d.mts +12 -0
  193. package/fjs/media/html/module.f.mjs +7 -5
  194. package/fjs/media/html/proof.f.d.mts +3 -0
  195. package/fjs/media/html/proof.f.mjs +4 -1
  196. package/fjs/media/json/extended/module.f.d.mts +82 -0
  197. package/fjs/media/json/extended/module.f.mjs +153 -0
  198. package/fjs/media/json/extended/proof.f.d.mts +42 -0
  199. package/fjs/media/json/extended/proof.f.mjs +127 -0
  200. package/fjs/media/json/extended/types.d.ts +23 -0
  201. package/fjs/media/json/module.f.d.mts +8 -2
  202. package/fjs/media/json/module.f.mjs +43 -41
  203. package/fjs/media/json/number/module.f.d.mts +59 -0
  204. package/fjs/media/json/number/module.f.mjs +136 -0
  205. package/fjs/media/json/number/proof.f.d.mts +24 -0
  206. package/fjs/media/json/number/proof.f.mjs +86 -0
  207. package/fjs/media/json/number/types.d.ts +28 -0
  208. package/fjs/media/json/parser/module.f.d.mts +25 -13
  209. package/fjs/media/json/parser/module.f.mjs +133 -72
  210. package/fjs/media/json/parser/proof.f.d.mts +5 -0
  211. package/fjs/media/json/parser/proof.f.mjs +31 -1
  212. package/fjs/media/json/parser/types.d.ts +38 -14
  213. package/fjs/media/json/rtti/module.f.d.mts +1 -1
  214. package/fjs/media/json/rtti/module.f.mjs +1 -1
  215. package/fjs/media/json/rtti/proof.f.d.mts +3 -0
  216. package/fjs/media/json/rtti/proof.f.mjs +12 -12
  217. package/fjs/media/json/schema/module.f.d.mts +61 -27
  218. package/fjs/media/json/schema/module.f.mjs +218 -63
  219. package/fjs/media/json/schema/proof.f.d.mts +41 -1
  220. package/fjs/media/json/schema/proof.f.mjs +220 -9
  221. package/fjs/media/json/serializer/module.f.d.mts +44 -0
  222. package/fjs/media/json/serializer/module.f.mjs +71 -9
  223. package/fjs/media/json/tokenizer/module.f.d.mts +8 -0
  224. package/fjs/media/json/tokenizer/module.f.mjs +31 -8
  225. package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
  226. package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
  227. package/fjs/media/json/types.d.ts +36 -10
  228. package/fjs/media/lock/module.f.d.mts +100 -0
  229. package/fjs/media/lock/module.f.mjs +125 -0
  230. package/fjs/media/lock/proof.f.d.mts +33 -0
  231. package/fjs/media/lock/proof.f.mjs +196 -0
  232. package/fjs/media/lock/types.d.ts +15 -0
  233. package/fjs/media/module.f.d.mts +6 -5
  234. package/fjs/media/module.f.mjs +8 -7
  235. package/fjs/media/nix/module.f.d.mts +13 -0
  236. package/fjs/media/nix/module.f.mjs +4 -2
  237. package/fjs/media/nix/proof.f.d.mts +3 -0
  238. package/fjs/media/nix/proof.f.mjs +4 -1
  239. package/fjs/media/note/module.f.d.mts +121 -0
  240. package/fjs/media/note/module.f.mjs +131 -0
  241. package/fjs/media/note/proof.f.d.mts +29 -0
  242. package/fjs/media/note/proof.f.mjs +150 -0
  243. package/fjs/media/note/types.d.ts +10 -0
  244. package/fjs/media/proof.f.d.mts +4 -1
  245. package/fjs/media/proof.f.mjs +40 -21
  246. package/fjs/media/revision/module.f.d.mts +83 -7
  247. package/fjs/media/revision/module.f.mjs +127 -23
  248. package/fjs/media/revision/proof.f.d.mts +14 -0
  249. package/fjs/media/revision/proof.f.mjs +93 -2
  250. package/fjs/media/revision/types.d.ts +34 -5
  251. package/fjs/media/type/module.f.d.mts +53 -18
  252. package/fjs/media/type/module.f.mjs +87 -97
  253. package/fjs/media/type/proof.f.d.mts +17 -0
  254. package/fjs/media/type/proof.f.mjs +73 -13
  255. package/fjs/module.d.mts +5 -0
  256. package/fjs/module.f.d.mts +8 -0
  257. package/fjs/module.f.mjs +33 -10
  258. package/fjs/module.mjs +6 -0
  259. package/fjs/nanvm/proof.f.mjs +3 -3
  260. package/fjs/nanvm/rust/module.f.mjs +1 -1
  261. package/fjs/nanvm/update/module.f.d.mts +4 -4
  262. package/fjs/nanvm/update/module.f.mjs +8 -9
  263. package/fjs/nanvm/update/proof.f.mjs +4 -3
  264. package/fjs/path/module.f.d.mts +8 -0
  265. package/fjs/path/module.f.mjs +4 -2
  266. package/fjs/proof.f.d.mts +7 -3
  267. package/fjs/proof.f.mjs +41 -12
  268. package/fjs/protocol/json_rpc/module.f.d.mts +3 -3
  269. package/fjs/protocol/json_rpc/module.f.mjs +10 -7
  270. package/fjs/protocol/json_rpc/proof.f.d.mts +6 -0
  271. package/fjs/protocol/json_rpc/proof.f.mjs +26 -5
  272. package/fjs/protocol/mcp/module.f.d.mts +10 -13
  273. package/fjs/protocol/mcp/module.f.mjs +74 -58
  274. package/fjs/protocol/mcp/proof.f.d.mts +16 -3
  275. package/fjs/protocol/mcp/proof.f.mjs +224 -69
  276. package/fjs/protocol/mcp/stdio/module.f.d.mts +17 -11
  277. package/fjs/protocol/mcp/stdio/module.f.mjs +39 -33
  278. package/fjs/protocol/mcp/stdio/proof.f.d.mts +5 -3
  279. package/fjs/protocol/mcp/stdio/proof.f.mjs +46 -13
  280. package/fjs/protocol/mcp/stdio/types.d.ts +14 -0
  281. package/fjs/protocol/mcp/types.d.ts +17 -6
  282. package/fjs/sul/id/module.f.d.mts +4 -0
  283. package/fjs/sul/id/module.f.mjs +6 -6
  284. package/fjs/sul/level/hash/module.f.d.mts +4 -1
  285. package/fjs/sul/level/hash/module.f.mjs +6 -4
  286. package/fjs/sul/level/hash/proof.f.d.mts +4 -0
  287. package/fjs/sul/level/hash/proof.f.mjs +7 -4
  288. package/fjs/sul/level/literal/module.f.d.mts +4 -1
  289. package/fjs/sul/level/literal/module.f.mjs +4 -3
  290. package/fjs/sul/level/literal/proof.f.d.mts +3 -0
  291. package/fjs/sul/level/literal/proof.f.mjs +4 -1
  292. package/fjs/sul/module.f.d.mts +5 -3
  293. package/fjs/sul/module.f.mjs +23 -17
  294. package/fjs/sul/proof.f.d.mts +4 -0
  295. package/fjs/sul/proof.f.mjs +5 -2
  296. package/fjs/text/ascii/module.f.d.mts +26 -0
  297. package/fjs/text/ascii/module.f.mjs +49 -1
  298. package/fjs/text/ascii/proof.f.d.mts +10 -0
  299. package/fjs/text/ascii/proof.f.mjs +33 -1
  300. package/fjs/text/code_point/module.f.d.mts +29 -3
  301. package/fjs/text/code_point/module.f.mjs +32 -9
  302. package/fjs/text/code_point/proof.f.d.mts +2 -0
  303. package/fjs/text/code_point/proof.f.mjs +39 -0
  304. package/fjs/text/module.f.d.mts +4 -2
  305. package/fjs/text/module.f.mjs +4 -3
  306. package/fjs/text/proof.f.d.mts +3 -0
  307. package/fjs/text/proof.f.mjs +4 -1
  308. package/fjs/text/sgr/module.f.d.mts +6 -5
  309. package/fjs/text/sgr/module.f.mjs +8 -8
  310. package/fjs/text/sgr/proof.f.d.mts +3 -0
  311. package/fjs/text/sgr/proof.f.mjs +4 -1
  312. package/fjs/text/utf16/module.f.d.mts +5 -1
  313. package/fjs/text/utf16/module.f.mjs +38 -35
  314. package/fjs/text/utf16/proof.f.d.mts +3 -1
  315. package/fjs/text/utf16/proof.f.mjs +19 -2
  316. package/fjs/text/utf8/module.f.d.mts +9 -5
  317. package/fjs/text/utf8/module.f.mjs +20 -20
  318. package/fjs/types/array/module.f.d.mts +2 -1
  319. package/fjs/types/array/module.f.mjs +16 -4
  320. package/fjs/types/bigfloat/module.f.d.mts +21 -1
  321. package/fjs/types/bigfloat/module.f.mjs +33 -7
  322. package/fjs/types/bigfloat/proof.f.d.mts +1 -0
  323. package/fjs/types/bigfloat/proof.f.mjs +46 -0
  324. package/fjs/types/bigint/module.f.d.mts +4 -0
  325. package/fjs/types/bigint/module.f.mjs +4 -3
  326. package/fjs/types/bit_vec/module.f.d.mts +10 -16
  327. package/fjs/types/bit_vec/module.f.mjs +99 -95
  328. package/fjs/types/bit_vec/proof.f.d.mts +6 -0
  329. package/fjs/types/bit_vec/proof.f.mjs +31 -5
  330. package/fjs/types/btree/find/module.f.d.mts +5 -1
  331. package/fjs/types/btree/find/module.f.mjs +5 -5
  332. package/fjs/types/btree/find/proof.f.d.mts +5 -1
  333. package/fjs/types/btree/find/proof.f.mjs +5 -3
  334. package/fjs/types/btree/module.f.d.mts +3 -0
  335. package/fjs/types/btree/module.f.mjs +3 -2
  336. package/fjs/types/btree/proof.f.d.mts +5 -1
  337. package/fjs/types/btree/proof.f.mjs +5 -3
  338. package/fjs/types/btree/remove/module.f.d.mts +11 -0
  339. package/fjs/types/btree/remove/module.f.mjs +10 -10
  340. package/fjs/types/btree/remove/proof.f.d.mts +3 -0
  341. package/fjs/types/btree/remove/proof.f.mjs +4 -1
  342. package/fjs/types/btree/set/module.f.d.mts +9 -0
  343. package/fjs/types/btree/set/module.f.mjs +14 -17
  344. package/fjs/types/btree/set/proof.f.d.mts +3 -0
  345. package/fjs/types/btree/set/proof.f.mjs +16 -1
  346. package/fjs/types/btree/types/module.f.d.mts +2 -1
  347. package/fjs/types/btree/types/module.f.mjs +2 -1
  348. package/fjs/types/byte_set/module.f.d.mts +20 -4
  349. package/fjs/types/byte_set/module.f.mjs +18 -8
  350. package/fjs/types/byte_set/proof.f.d.mts +3 -1
  351. package/fjs/types/byte_set/proof.f.mjs +10 -8
  352. package/fjs/types/function/compare/module.f.d.mts +3 -2
  353. package/fjs/types/function/compare/module.f.mjs +13 -5
  354. package/fjs/types/function/module.f.d.mts +8 -1
  355. package/fjs/types/function/module.f.mjs +8 -1
  356. package/fjs/types/function/operator/module.f.d.mts +2 -1
  357. package/fjs/types/function/operator/module.f.mjs +2 -2
  358. package/fjs/types/list/module.f.d.mts +10 -3
  359. package/fjs/types/list/module.f.mjs +6 -4
  360. package/fjs/types/list/proof.f.d.mts +4 -1
  361. package/fjs/types/list/proof.f.mjs +4 -2
  362. package/fjs/types/nibble_set/module.f.d.mts +2 -1
  363. package/fjs/types/nibble_set/module.f.mjs +2 -2
  364. package/fjs/types/nominal/module.f.d.mts +2 -1
  365. package/fjs/types/nominal/module.f.mjs +2 -1
  366. package/fjs/types/nullable/module.f.d.mts +26 -6
  367. package/fjs/types/nullable/module.f.mjs +25 -6
  368. package/fjs/types/nullable/proof.f.d.mts +4 -0
  369. package/fjs/types/nullable/proof.f.mjs +15 -0
  370. package/fjs/types/number/module.f.d.mts +4 -0
  371. package/fjs/types/number/module.f.mjs +4 -3
  372. package/fjs/types/object/module.f.d.mts +26 -4
  373. package/fjs/types/object/module.f.mjs +26 -7
  374. package/fjs/types/object/proof.f.d.mts +5 -2
  375. package/fjs/types/object/proof.f.mjs +6 -3
  376. package/fjs/types/object/structurally_same/module.f.d.mts +39 -0
  377. package/fjs/types/object/structurally_same/module.f.mjs +59 -0
  378. package/fjs/types/object/structurally_same/proof.f.d.mts +6 -0
  379. package/fjs/types/object/structurally_same/proof.f.mjs +73 -0
  380. package/fjs/types/ordered_map/module.f.d.mts +5 -0
  381. package/fjs/types/ordered_map/module.f.mjs +5 -4
  382. package/fjs/types/ordered_map/proof.f.d.mts +3 -0
  383. package/fjs/types/ordered_map/proof.f.mjs +4 -1
  384. package/fjs/types/patricia_trie/module.f.d.mts +2 -1
  385. package/fjs/types/patricia_trie/module.f.mjs +27 -12
  386. package/fjs/types/patricia_trie/proof.f.d.mts +3 -0
  387. package/fjs/types/patricia_trie/proof.f.mjs +4 -1
  388. package/fjs/types/prime_field/module.f.d.mts +3 -1
  389. package/fjs/types/prime_field/module.f.mjs +3 -2
  390. package/fjs/types/range/module.f.d.mts +2 -1
  391. package/fjs/types/range/module.f.mjs +2 -2
  392. package/fjs/types/range_map/module.f.d.mts +6 -0
  393. package/fjs/types/range_map/module.f.mjs +6 -9
  394. package/fjs/types/range_map/proof.f.d.mts +5 -1
  395. package/fjs/types/range_map/proof.f.mjs +5 -3
  396. package/fjs/types/range_set/module.f.d.mts +5 -0
  397. package/fjs/types/range_set/module.f.mjs +1 -0
  398. package/fjs/types/range_set/proof.f.d.mts +3 -0
  399. package/fjs/types/range_set/proof.f.mjs +4 -1
  400. package/fjs/types/result/module.d.mts +8 -0
  401. package/fjs/types/result/module.f.d.mts +37 -7
  402. package/fjs/types/result/module.f.mjs +38 -8
  403. package/fjs/types/result/module.mjs +9 -1
  404. package/fjs/types/result/proof.f.d.mts +5 -0
  405. package/fjs/types/result/proof.f.mjs +22 -2
  406. package/fjs/types/rtti/common/module.f.d.mts +44 -14
  407. package/fjs/types/rtti/common/module.f.mjs +42 -36
  408. package/fjs/types/rtti/common/proof.f.d.mts +4 -0
  409. package/fjs/types/rtti/common/proof.f.mjs +10 -7
  410. package/fjs/types/rtti/data/module.f.d.mts +134 -0
  411. package/fjs/types/rtti/data/module.f.mjs +1053 -0
  412. package/fjs/types/rtti/data/proof.f.d.mts +76 -0
  413. package/fjs/types/rtti/data/proof.f.mjs +589 -0
  414. package/fjs/types/rtti/data/types.d.ts +83 -0
  415. package/fjs/types/rtti/module.f.d.mts +12 -5
  416. package/fjs/types/rtti/module.f.mjs +7 -5
  417. package/fjs/types/rtti/parse/module.f.d.mts +48 -0
  418. package/fjs/types/rtti/parse/module.f.mjs +45 -34
  419. package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
  420. package/fjs/types/rtti/parse/proof.f.mjs +51 -52
  421. package/fjs/types/rtti/proof.f.d.mts +3 -1
  422. package/fjs/types/rtti/proof.f.mjs +6 -2
  423. package/fjs/types/rtti/ts/module.f.d.mts +68 -9
  424. package/fjs/types/rtti/ts/module.f.mjs +258 -30
  425. package/fjs/types/rtti/ts/proof.f.d.mts +44 -0
  426. package/fjs/types/rtti/ts/proof.f.mjs +152 -4
  427. package/fjs/types/rtti/ts/types.d.ts +25 -1
  428. package/fjs/types/rtti/types.d.ts +40 -0
  429. package/fjs/types/sorted_list/module.f.d.mts +4 -1
  430. package/fjs/types/sorted_list/module.f.mjs +21 -12
  431. package/fjs/types/sorted_list/proof.f.d.mts +3 -1
  432. package/fjs/types/sorted_list/proof.f.mjs +3 -1
  433. package/fjs/types/sorted_set/module.f.d.mts +49 -1
  434. package/fjs/types/sorted_set/module.f.mjs +26 -2
  435. package/fjs/types/sorted_set/proof.f.d.mts +4 -1
  436. package/fjs/types/sorted_set/proof.f.mjs +19 -2
  437. package/fjs/types/string/module.f.d.mts +4 -0
  438. package/fjs/types/string/module.f.mjs +4 -3
  439. package/fjs/types/string_set/module.f.d.mts +3 -1
  440. package/fjs/types/string_set/module.f.mjs +3 -2
  441. package/fjs/types/ts/module.f.d.mts +2 -0
  442. package/fjs/types/ts/module.f.mjs +3 -3
  443. package/fjs/types/ts/proof.f.d.mts +1 -0
  444. package/fjs/types/ts/proof.f.mjs +5 -0
  445. package/fjs/types/ts/types.d.ts +6 -1
  446. package/fjs/types/uint8array/module.f.d.mts +13 -2
  447. package/fjs/types/uint8array/module.f.mjs +20 -15
  448. package/fjs/website/module.f.d.mts +5 -2
  449. package/fjs/website/module.f.mjs +6 -8
  450. package/fjs/website/proof.f.mjs +2 -1
  451. package/package.json +3 -3
  452. package/fjs/asn.1/types.js +0 -6
  453. package/fjs/asserts/types.js +0 -6
  454. package/fjs/basen/types.js +0 -6
  455. package/fjs/bnf/data/types.js +0 -6
  456. package/fjs/bnf/descent/types.js +0 -6
  457. package/fjs/bnf/ll1/types.js +0 -6
  458. package/fjs/bnf/token_symbol/types.js +0 -6
  459. package/fjs/bnf/types.js +0 -6
  460. package/fjs/cas/evo/types.js +0 -7
  461. package/fjs/cas/types.js +0 -6
  462. package/fjs/ci/common/types.js +0 -1
  463. package/fjs/ci/nix/types.js +0 -6
  464. package/fjs/ci/types.js +0 -6
  465. package/fjs/cli/types.js +0 -6
  466. package/fjs/common/monoid/types.js +0 -6
  467. package/fjs/crypto/pow/types.js +0 -6
  468. package/fjs/crypto/secp/types.js +0 -6
  469. package/fjs/crypto/sha2/types.js +0 -6
  470. package/fjs/crypto/sign/types.js +0 -6
  471. package/fjs/crypto/vdf/types.js +0 -6
  472. package/fjs/dev/package_json/module.f.d.mts +0 -32
  473. package/fjs/dev/package_json/module.f.mjs +0 -37
  474. package/fjs/dev/package_json/proof.f.d.mts +0 -6
  475. package/fjs/dev/package_json/proof.f.mjs +0 -32
  476. package/fjs/dev/types.js +0 -6
  477. package/fjs/djs/ast/types.js +0 -8
  478. package/fjs/djs/parser/types.js +0 -7
  479. package/fjs/djs/tokenizer/types.js +0 -7
  480. package/fjs/djs/types.js +0 -7
  481. package/fjs/effects/eff/module.f.d.mts +0 -10
  482. package/fjs/effects/eff/module.f.mjs +0 -68
  483. package/fjs/effects/eff/proof.f.d.mts +0 -12
  484. package/fjs/effects/eff/proof.f.mjs +0 -66
  485. package/fjs/effects/eff/types.d.ts +0 -71
  486. package/fjs/effects/eff/types.js +0 -6
  487. package/fjs/effects/list/types.js +0 -6
  488. package/fjs/effects/memory/types.js +0 -6
  489. package/fjs/effects/mock/types.js +0 -6
  490. package/fjs/effects/node/types.js +0 -6
  491. package/fjs/effects/node/virtual/types.js +0 -7
  492. package/fjs/effects/types.js +0 -6
  493. package/fjs/emergent_testing/all.test.d.ts +0 -1
  494. package/fjs/emergent_testing/all.test.js +0 -5
  495. package/fjs/emergent_testing/scenarios/all.d.ts +0 -1
  496. package/fjs/emergent_testing/scenarios/all.js +0 -1
  497. package/fjs/emergent_testing/scenarios/async-subtests.fail.d.ts +0 -6
  498. package/fjs/emergent_testing/scenarios/async-subtests.fail.js +0 -9
  499. package/fjs/emergent_testing/scenarios/async-subtests.pass.d.ts +0 -6
  500. package/fjs/emergent_testing/scenarios/async-subtests.pass.js +0 -9
  501. package/fjs/emergent_testing/scenarios/async.fail.d.ts +0 -3
  502. package/fjs/emergent_testing/scenarios/async.fail.js +0 -6
  503. package/fjs/emergent_testing/scenarios/async.pass.d.ts +0 -3
  504. package/fjs/emergent_testing/scenarios/async.pass.js +0 -5
  505. package/fjs/emergent_testing/scenarios/fail.fail.d.ts +0 -3
  506. package/fjs/emergent_testing/scenarios/fail.fail.js +0 -3
  507. package/fjs/emergent_testing/scenarios/return-value.pass.js +0 -4
  508. package/fjs/emergent_testing/scenarios/thenable.pass.d.ts +0 -5
  509. package/fjs/emergent_testing/scenarios/thenable.pass.js +0 -11
  510. package/fjs/emergent_testing/scenarios/thenable2.pass.d.ts +0 -5
  511. package/fjs/emergent_testing/scenarios/thenable2.pass.js +0 -3
  512. package/fjs/emergent_testing/scenarios/throw.pass.d.ts +0 -5
  513. package/fjs/emergent_testing/scenarios/throw.pass.js +0 -3
  514. package/fjs/emergent_testing/types.js +0 -6
  515. package/fjs/js/tokenizer/types.js +0 -6
  516. package/fjs/media/html/types.js +0 -6
  517. package/fjs/media/json/parser/types.js +0 -6
  518. package/fjs/media/json/tokenizer/types.js +0 -6
  519. package/fjs/media/json/types.js +0 -13
  520. package/fjs/media/nix/types.js +0 -6
  521. package/fjs/media/revision/types.js +0 -8
  522. package/fjs/media/type/types.js +0 -6
  523. package/fjs/media/types.js +0 -7
  524. package/fjs/nanvm/types.js +0 -11
  525. package/fjs/protocol/json_rpc/types.js +0 -8
  526. package/fjs/protocol/mcp/types.js +0 -8
  527. package/fjs/sul/id/types.js +0 -6
  528. package/fjs/sul/level/hash/types.js +0 -6
  529. package/fjs/sul/level/literal/types.js +0 -6
  530. package/fjs/sul/types.js +0 -6
  531. package/fjs/text/sgr/types.js +0 -6
  532. package/fjs/text/types.js +0 -6
  533. package/fjs/text/utf16/types.js +0 -6
  534. package/fjs/text/utf8/types.js +0 -6
  535. package/fjs/types/array/types.js +0 -6
  536. package/fjs/types/bigfloat/types.js +0 -6
  537. package/fjs/types/bigint/types.js +0 -6
  538. package/fjs/types/bit_vec/types.js +0 -6
  539. package/fjs/types/btree/find/types.js +0 -6
  540. package/fjs/types/btree/types/types.js +0 -6
  541. package/fjs/types/byte_set/types.js +0 -6
  542. package/fjs/types/function/compare/types.js +0 -6
  543. package/fjs/types/function/operator/types.js +0 -6
  544. package/fjs/types/function/types.js +0 -6
  545. package/fjs/types/list/types.js +0 -6
  546. package/fjs/types/nibble_set/types.js +0 -6
  547. package/fjs/types/nominal/types.js +0 -6
  548. package/fjs/types/nullable/types.js +0 -6
  549. package/fjs/types/object/types.js +0 -8
  550. package/fjs/types/option/types.js +0 -6
  551. package/fjs/types/ordered_map/types.js +0 -6
  552. package/fjs/types/patricia_trie/types.js +0 -6
  553. package/fjs/types/phantom/types.js +0 -11
  554. package/fjs/types/prime_field/types.js +0 -6
  555. package/fjs/types/range/types.js +0 -6
  556. package/fjs/types/range_map/types.js +0 -6
  557. package/fjs/types/result/types.js +0 -6
  558. package/fjs/types/rtti/common/types.js +0 -1
  559. package/fjs/types/rtti/parse/types.js +0 -1
  560. package/fjs/types/rtti/ts/types.js +0 -1
  561. package/fjs/types/rtti/types.js +0 -1
  562. package/fjs/types/rtti/validate/module.f.d.mts +0 -6
  563. package/fjs/types/rtti/validate/module.f.mjs +0 -169
  564. package/fjs/types/rtti/validate/proof.f.d.mts +0 -125
  565. package/fjs/types/rtti/validate/proof.f.mjs +0 -437
  566. package/fjs/types/rtti/validate/types.d.ts +0 -6
  567. package/fjs/types/rtti/validate/types.js +0 -1
  568. package/fjs/types/sorted_list/types.js +0 -6
  569. package/fjs/types/sorted_set/types.js +0 -6
  570. package/fjs/types/string_set/types.js +0 -6
  571. package/fjs/types/ts/types.js +0 -8
@@ -1,24 +1,33 @@
1
1
  /**
2
2
  * @import { Cas } from '../types.ts'
3
+ * @import { EvoChannel } from './types.ts'
4
+ * @import { Result } from '../../types/result/types.ts'
5
+ * @import { Effect } from '../../effects/types.ts'
6
+ * @import { IoChannel, NodeOp } from '../../effects/node/types.ts'
7
+ * @import { State } from '../../effects/node/virtual/types.ts'
3
8
  * @import { Vec } from '../../types/bit_vec/types.ts'
4
9
  * @import { Ok } from '../../types/result/types.ts'
5
- * @import { IoResult } from '../../effects/node/types.ts'
6
10
  * @import { List } from '../../effects/list/types.ts'
7
11
  * @import { RevisionData } from './types.ts'
8
12
  */
9
13
 
10
- import { assert, assertEq } from '../../asserts/module.f.mjs'
14
+ import {
15
+ assert,
16
+ assertEq,
17
+ assertStructurallySame,
18
+ } from '../../asserts/module.f.mjs'
11
19
  import { pure } from '../../effects/module.f.mjs'
20
+ import { pureError } from '../../effects/module.f.mjs'
21
+ import { ioError } from '../../effects/node/module.f.mjs'
12
22
  import { fileCas } from '../module.f.mjs'
13
23
  import { sha256 } from '../../crypto/sha2/module.f.mjs'
14
24
  import { emptyState, virtual } from '../../effects/node/virtual/module.f.mjs'
15
25
  import { vec, vec8 } from '../../types/bit_vec/module.f.mjs'
16
26
  import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.mjs'
17
27
  import { unwrap } from '../../types/nullable/module.f.mjs'
18
- import { ok, error } from '../../types/result/module.f.mjs'
28
+ import { ok, error, unwrap as unwrapResult } from '../../types/result/module.f.mjs'
19
29
  import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.mjs'
20
30
  import { tryUtf8 } from '../../text/module.f.mjs'
21
-
22
31
  import { dialect as revisionDialect } from '../../media/revision/module.f.mjs'
23
32
  import {
24
33
  buildCache, decodeRevisionBlob, initEvo, evo, emptyCache, syncRevision,
@@ -31,18 +40,18 @@ const home = '.'
31
40
  /** @type {Cas<never>} */
32
41
  const writeFailingCas = {
33
42
  read: () => elEmpty(),
34
- write: () => pure(error('boom')),
35
- list: () => pure([]),
43
+ write: () => pure(error(ioError({ message: 'boom' }))),
44
+ list: () => pure(ok([])),
36
45
  }
37
46
 
38
- // A `Cas<never>` whose `read` yields an error item that is *not* a missing
39
- // shard — what a permission error, a mid-stream I/O failure, or a blob too
40
- // large for `collectRead` to buffer looks like to a caller.
47
+ // A `Cas<never>` whose `read` fails the stream with something that is *not* a
48
+ // missing shard — what a permission error, a mid-stream I/O failure, or a blob
49
+ // too large for `collectRead` to buffer looks like to a caller.
41
50
  /** @type {Cas<never>} */
42
51
  const readFailingCas = {
43
- read: () => nonEmpty(/** @type {IoResult<Vec>} */ (error('boom')), elEmpty()),
44
- write: () => pure(error('write not supported')),
45
- list: () => pure([]),
52
+ read: () => pureError(ioError({ message: 'boom' })),
53
+ write: () => pure(error(ioError({ message: 'write not supported' }))),
54
+ list: () => pure(ok([])),
46
55
  }
47
56
 
48
57
  // A `Cas<never>` backed by a fixed set of (hash, content) entries, returned
@@ -54,45 +63,80 @@ const fixedCas = entries => ({
54
63
  read: hash => {
55
64
  const found = entries.find(([h]) => vecToCBase32(h) === vecToCBase32(hash))
56
65
  return found === undefined
57
- ? nonEmpty(/** @type {IoResult<Vec>} */ (error('not found')), elEmpty())
58
- : nonEmpty(/** @type {IoResult<Vec>} */ (ok(found[1])), elEmpty())
66
+ ? pureError(ioError({ message: 'not found' }))
67
+ : nonEmpty(found[1], elEmpty())
59
68
  },
60
- write: () => pure(error('write not supported')),
61
- list: () => pure(entries.map(([h]) => h)),
69
+ write: () => pure(error(ioError({ message: 'write not supported' }))),
70
+ list: () => pure(ok(entries.map(([h]) => h))),
62
71
  })
63
72
 
73
+ /**
74
+ * Runs `e` against the virtual runner and unwraps the effect channel.
75
+ *
76
+ * These proofs drive a runner that implements every operation they use, so a
77
+ * channel failure here is a broken fixture rather than a case under test, and
78
+ * panicking on one is the wanted outcome.
79
+ *
80
+ * The cases that *are* about failure therefore do not come through here: a
81
+ * rejected revision now travels in the same channel as an undispatchable
82
+ * operation, so unwrapping would panic on the thing under test. Those use the
83
+ * raw `virtual` and `assertEvoError`.
84
+ *
85
+ * @type {(state: State) => <T, E>(e: Effect<NodeOp, T, E>) => readonly [State, T]}
86
+ */
87
+ const virtualOk = state => e => {
88
+ const [s, r] = virtual(state)(e)
89
+ return [s, unwrapResult(r)]
90
+ }
91
+
92
+ /**
93
+ * Asserts that `r` failed with an {@link EvoError} whose message contains
94
+ * `expected`.
95
+ *
96
+ * The tag is checked as well as the text, so a `notImplemented` from a runner
97
+ * that quietly lost a handler cannot pass for a rejected revision. Both now
98
+ * arrive through the same channel, which is the point — telling them apart is
99
+ * what the tag is for.
100
+ *
101
+ * @type {(r: Result<unknown, EvoChannel>, expected: string) => void}
102
+ */
103
+ const assertEvoError = (r, expected) =>
104
+ assert(
105
+ r[0] === 'error' && r[1][0] === 'evoError' && r[1][1].includes(expected),
106
+ ['expected an evo error containing', expected, r])
107
+
64
108
  export const proof = {
65
109
  buildCacheEmptyStoreYieldsEmptyCache: () => {
66
110
  const c = fileCas(sha256)(home)
67
- const [, cache] = virtual(emptyState)(buildCache(c))
68
- assertEq(JSON.stringify(cache), JSON.stringify(emptyCache))
111
+ const [, cache] = virtualOk(emptyState)(buildCache(c))
112
+ assertStructurallySame(cache, emptyCache)
69
113
  },
70
114
  buildCacheSkipsNonRevisionBlob: () => {
71
115
  const c = fileCas(sha256)(home)
72
116
  const content = vec8(0x41n) // 'A' — valid UTF-8, not revision JSON
73
- const [state1] = virtual(emptyState)(c.write(nonEmpty(ok(content), /** @type {List<never, Ok<Vec>>} */ (elEmpty()))))
74
- const [, cache] = virtual(state1)(buildCache(c))
117
+ const [state1] = virtual(emptyState)(c.write(nonEmpty(content, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
118
+ const [, cache] = virtualOk(state1)(buildCache(c))
75
119
  assertEq(Object.keys(cache.bySubject).length, 0)
76
120
  },
77
121
  decodeRevisionBlobMissingHashIsNull: () => {
78
122
  const c = fileCas(sha256)(home)
79
- const [, revision] = virtual(emptyState)(decodeRevisionBlob(c)(vec8(0x99n)))
123
+ const [, revision] = virtualOk(emptyState)(decodeRevisionBlob(c)(vec8(0x99n)))
80
124
  assertEq(revision, null)
81
125
  },
82
126
  decodeRevisionBlobNonUtf8IsNull: () => {
83
127
  const c = fileCas(sha256)(home)
84
128
  const oddVec = vec(5n)(0b10101n) // not a whole number of bytes
85
- const [state1, w] = virtual(emptyState)(c.write(nonEmpty(ok(oddVec), /** @type {List<never, Ok<Vec>>} */ (elEmpty()))))
129
+ const [state1, w] = virtual(emptyState)(c.write(nonEmpty(oddVec, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
86
130
  assert(w[0] === 'ok', ['expected write ok', w])
87
- const [, revision] = virtual(state1)(decodeRevisionBlob(c)(w[1]))
131
+ const [, revision] = virtualOk(state1)(decodeRevisionBlob(c)(w[1]))
88
132
  assertEq(revision, null)
89
133
  },
90
134
  decodeRevisionBlobInvalidJsonIsNull: () => {
91
135
  const c = fileCas(sha256)(home)
92
136
  const content = vec8(0x7bn) // '{' alone: valid UTF-8, not parseable JSON
93
- const [state1, w] = virtual(emptyState)(c.write(nonEmpty(ok(content), /** @type {List<never, Ok<Vec>>} */ (elEmpty()))))
137
+ const [state1, w] = virtual(emptyState)(c.write(nonEmpty(content, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
94
138
  assert(w[0] === 'ok', ['expected write ok', w])
95
- const [, revision] = virtual(state1)(decodeRevisionBlob(c)(w[1]))
139
+ const [, revision] = virtualOk(state1)(decodeRevisionBlob(c)(w[1]))
96
140
  assertEq(revision, null)
97
141
  },
98
142
  decodeRevisionBlobValidRevisionRoundTrips: () => {
@@ -101,9 +145,9 @@ export const proof = {
101
145
  const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`
102
146
  const bytes = tryUtf8(text)
103
147
  assert(bytes !== null, 'expected the sample revision text to encode as UTF-8')
104
- const [state1, w] = virtual(emptyState)(c.write(nonEmpty(ok(bytes), /** @type {List<never, Ok<Vec>>} */ (elEmpty()))))
148
+ const [state1, w] = virtual(emptyState)(c.write(nonEmpty(bytes, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
105
149
  assert(w[0] === 'ok', ['expected write ok', w])
106
- const [, revision] = virtual(state1)(decodeRevisionBlob(c)(w[1]))
150
+ const [, revision] = virtualOk(state1)(decodeRevisionBlob(c)(w[1]))
107
151
  assert(revision !== null, 'expected a decoded revision')
108
152
  assertEq(revision?.subject, subjectHash)
109
153
  },
@@ -117,9 +161,9 @@ export const proof = {
117
161
  const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`
118
162
  const bytes = tryUtf8(text)
119
163
  assert(bytes !== null, 'expected the sample revision text to encode as UTF-8')
120
- const [state1, w] = virtual(emptyState)(fileCas(sha256)(home).write(nonEmpty(ok(bytes), /** @type {List<never, Ok<Vec>>} */ (elEmpty()))))
164
+ const [state1, w] = virtual(emptyState)(fileCas(sha256)(home).write(nonEmpty(bytes, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
121
165
  assert(w[0] === 'ok', ['expected write ok', w])
122
- const [, cache] = virtual(state1)(buildCache(c))
166
+ const [, cache] = virtualOk(state1)(buildCache(c))
123
167
  assertEq(cache.bySubject[subjectHash]?.hashes.length, 1)
124
168
  assertEq(cache.bySubject[subjectHash]?.hashes[0], vecToCBase32(w[1]))
125
169
  },
@@ -141,8 +185,8 @@ export const proof = {
141
185
  const childBytes = tryUtf8(childText)
142
186
  assert(rootBytes !== null && childBytes !== null, 'expected sample revisions to encode as UTF-8')
143
187
  const cas = fixedCas([[childHash, childBytes], [rootHash, rootBytes]])
144
- const [state0, cacheKey] = virtual(emptyState)(initEvo(cas))
145
- const [, heads] = virtual(state0)(evo(cas)(cacheKey).head('doc'))
188
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(cas))
189
+ const [, heads] = virtualOk(state0)(evo(cas)(cacheKey).head('doc'))
146
190
  assertEq(heads.length, 1)
147
191
  assertEq(heads[0], childCBase32)
148
192
  },
@@ -165,8 +209,8 @@ export const proof = {
165
209
  const childBytes = tryUtf8(childText)
166
210
  assert(rootBytes !== null && childBytes !== null, 'expected sample revisions to encode as UTF-8')
167
211
  const cas = fixedCas([[rootHash, rootBytes], [childHash, childBytes]])
168
- const [state0, cacheKey] = virtual(emptyState)(initEvo(cas))
169
- const [, heads] = virtual(state0)(evo(cas)(cacheKey).head('doc'))
212
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(cas))
213
+ const [, heads] = virtualOk(state0)(evo(cas)(cacheKey).head('doc'))
170
214
  assertEq(heads.length, 1)
171
215
  assertEq(heads[0], vecToCBase32(childHash))
172
216
  },
@@ -176,38 +220,35 @@ export const proof = {
176
220
  // set), and a keep (an unrelated existing head surviving the fold).
177
221
  addRevisionBuildsHeadsAcrossChainAndFork: () => {
178
222
  const c = fileCas(sha256)(home)
179
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
223
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
180
224
  const e = evo(c)(cacheKey)
181
225
  const snapshotHash = vecToCBase32(vec8(0x22n))
182
226
 
183
227
  /** @type {RevisionData} */
184
228
  const rev0 = { parents: [], subject: 'doc', snapshot: snapshotHash }
185
- const [state1, rev0Result] = virtual(state0)(e.add(rev0))
186
- assert(rev0Result[0] === 'ok', ['expected rev0 ok', rev0Result])
187
- const rev0Hash = rev0Result[1]
229
+ const [state1, rev0Result] = virtualOk(state0)(e.add(rev0))
230
+ const rev0Hash = rev0Result
188
231
 
189
232
  // rev1 inherits rev0's snapshot; rev2 states a different one — so the
190
233
  // two concurrent children are genuinely distinct blobs (both children
191
234
  // of rev0, same generation), not one deduplicated revision.
192
235
  /** @type {RevisionData} */
193
236
  const rev1 = { parents: [rev0Hash], subject: 'doc' }
194
- const [state2, rev1Result] = virtual(state1)(e.add(rev1))
195
- assert(rev1Result[0] === 'ok', ['expected rev1 ok', rev1Result])
196
- const rev1Hash = rev1Result[1]
237
+ const [state2, rev1Result] = virtualOk(state1)(e.add(rev1))
238
+ const rev1Hash = rev1Result
197
239
 
198
240
  /** @type {RevisionData} */
199
241
  const rev2 = { parents: [rev0Hash], subject: 'doc', snapshot: vecToCBase32(vec8(0x23n)) }
200
- const [state3, rev2Result] = virtual(state2)(e.add(rev2))
201
- assert(rev2Result[0] === 'ok', ['expected rev2 ok', rev2Result])
202
- const rev2Hash = rev2Result[1]
242
+ const [state3, rev2Result] = virtualOk(state2)(e.add(rev2))
243
+ const rev2Hash = rev2Result
203
244
 
204
- const [state4, heads] = virtual(state3)(e.head('doc'))
245
+ const [state4, heads] = virtualOk(state3)(e.head('doc'))
205
246
  assertEq(heads.length, 2)
206
247
  assert(heads.includes(rev1Hash), ['expected rev1 to remain a head', heads])
207
248
  assert(heads.includes(rev2Hash), ['expected rev2 to be a head', heads])
208
249
  assert(!heads.includes(rev0Hash), ['expected rev0 to no longer be a head', heads])
209
250
 
210
- const [, subjects] = virtual(state4)(e.list())
251
+ const [, subjects] = virtualOk(state4)(e.list())
211
252
  assertEq(subjects.length, 1)
212
253
  assertEq(subjects[0], 'doc')
213
254
  },
@@ -217,16 +258,14 @@ export const proof = {
217
258
  // and neither subject appears in the other's result.
218
259
  listPartitionsSubjectsByHeadArchivedFlag: () => {
219
260
  const c = fileCas(sha256)(home)
220
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
261
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
221
262
  const e = evo(c)(cacheKey)
222
- const [state1, live] = virtual(state0)(e.add({ parents: [], subject: 'live', snapshot: vecToCBase32(vec8(0x40n)) }))
223
- assert(live[0] === 'ok', ['expected the active add to succeed', live])
224
- const [state2, gone] = virtual(state1)(e.add({ parents: [], subject: 'gone', snapshot: vecToCBase32(vec8(0x41n)), archived: true }))
225
- assert(gone[0] === 'ok', ['expected the archived add to succeed', gone])
226
- const [state3, active] = virtual(state2)(e.list())
263
+ const [state1, live] = virtualOk(state0)(e.add({ parents: [], subject: 'live', snapshot: vecToCBase32(vec8(0x40n)) }))
264
+ const [state2, gone] = virtualOk(state1)(e.add({ parents: [], subject: 'gone', snapshot: vecToCBase32(vec8(0x41n)), archived: true }))
265
+ const [state3, active] = virtualOk(state2)(e.list())
227
266
  assertEq(active.length, 1)
228
267
  assertEq(active[0], 'live')
229
- const [, archived] = virtual(state3)(e.list(true))
268
+ const [, archived] = virtualOk(state3)(e.list(true))
230
269
  assertEq(archived.length, 1)
231
270
  assertEq(archived[0], 'gone')
232
271
  },
@@ -235,22 +274,19 @@ export const proof = {
235
274
  // — so the subject stays out of the archived-only result.
236
275
  listTreatsDisagreeingHeadsAsActive: () => {
237
276
  const c = fileCas(sha256)(home)
238
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
277
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
239
278
  const e = evo(c)(cacheKey)
240
- const [state1, root] = virtual(state0)(e.add({ parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x42n)) }))
241
- assert(root[0] === 'ok', ['expected root ok', root])
279
+ const [state1, root] = virtualOk(state0)(e.add({ parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x42n)) }))
242
280
  // The two children differ only in `archived`, which is enough to make
243
281
  // them distinct blobs, hence two concurrent heads of one root.
244
- const [state2, kept] = virtual(state1)(e.add({ parents: [root[1]], subject: 'doc' }))
245
- assert(kept[0] === 'ok', ['expected the unarchived child ok', kept])
246
- const [state3, dropped] = virtual(state2)(e.add({ parents: [root[1]], subject: 'doc', archived: true }))
247
- assert(dropped[0] === 'ok', ['expected the archived child ok', dropped])
248
- const [state4, heads] = virtual(state3)(e.head('doc'))
282
+ const [state2] = virtualOk(state1)(e.add({ parents: [root], subject: 'doc' }))
283
+ const [state3] = virtualOk(state2)(e.add({ parents: [root], subject: 'doc', archived: true }))
284
+ const [state4, heads] = virtualOk(state3)(e.head('doc'))
249
285
  assertEq(heads.length, 2)
250
- const [state5, active] = virtual(state4)(e.list())
286
+ const [state5, active] = virtualOk(state4)(e.list())
251
287
  assertEq(active.length, 1)
252
288
  assertEq(active[0], 'doc')
253
- const [, archived] = virtual(state5)(e.list(true))
289
+ const [, archived] = virtualOk(state5)(e.list(true))
254
290
  assertEq(archived.length, 0)
255
291
  },
256
292
  // `archived` is a property of a revision, not of a subject: a subject
@@ -258,16 +294,14 @@ export const proof = {
258
294
  // demotes that revision out of the head set.
259
295
  listIgnoresArchivedRevisionsThatAreNoLongerHeads: () => {
260
296
  const c = fileCas(sha256)(home)
261
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
297
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
262
298
  const e = evo(c)(cacheKey)
263
- const [state1, root] = virtual(state0)(e.add({ parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x45n)), archived: true }))
264
- assert(root[0] === 'ok', ['expected the archived root ok', root])
265
- const [state2, revived] = virtual(state1)(e.add({ parents: [root[1]], subject: 'doc' }))
266
- assert(revived[0] === 'ok', ['expected the unarchived child ok', revived])
267
- const [state3, active] = virtual(state2)(e.list())
299
+ const [state1, root] = virtualOk(state0)(e.add({ parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x45n)), archived: true }))
300
+ const [state2] = virtualOk(state1)(e.add({ parents: [root], subject: 'doc' }))
301
+ const [state3, active] = virtualOk(state2)(e.list())
268
302
  assertEq(active.length, 1)
269
303
  assertEq(active[0], 'doc')
270
- const [, archived] = virtual(state3)(e.list(true))
304
+ const [, archived] = virtualOk(state3)(e.list(true))
271
305
  assertEq(archived.length, 0)
272
306
  },
273
307
  // A subject can end up with no current head at all, and a status is a
@@ -283,29 +317,27 @@ export const proof = {
283
317
  const bytes = tryUtf8(text)
284
318
  assert(bytes !== null, 'expected the sample revision text to encode as UTF-8')
285
319
  const cas = fixedCas([[selfHash, bytes]])
286
- const [state0, cacheKey] = virtual(emptyState)(initEvo(cas))
320
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(cas))
287
321
  const e = evo(cas)(cacheKey)
288
- const [state1, heads] = virtual(state0)(e.head('doc'))
322
+ const [state1, heads] = virtualOk(state0)(e.head('doc'))
289
323
  assertEq(heads.length, 0)
290
- const [state2, active] = virtual(state1)(e.list())
324
+ const [state2, active] = virtualOk(state1)(e.list())
291
325
  assertEq(active.length, 0)
292
- const [, archived] = virtual(state2)(e.list(true))
326
+ const [, archived] = virtualOk(state2)(e.list(true))
293
327
  assertEq(archived.length, 0)
294
328
  },
295
329
  // Adding the exact same revision twice yields the same (deduplicated)
296
330
  // content hash and must not duplicate the head entry.
297
331
  addRevisionIdempotentOnDuplicateContent: () => {
298
332
  const c = fileCas(sha256)(home)
299
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
333
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
300
334
  const e = evo(c)(cacheKey)
301
335
  /** @type {RevisionData} */
302
336
  const input = { parents: [], subject: 'x', snapshot: vecToCBase32(vec8(0x33n)) }
303
- const [state1, r1] = virtual(state0)(e.add(input))
304
- assert(r1[0] === 'ok', ['expected first add ok', r1])
305
- const [state2, r2] = virtual(state1)(e.add(input))
306
- assert(r2[0] === 'ok', ['expected second add ok', r2])
307
- assertEq(r1[1], r2[1])
308
- const [, heads] = virtual(state2)(e.head('x'))
337
+ const [state1, r1] = virtualOk(state0)(e.add(input))
338
+ const [state2, r2] = virtualOk(state1)(e.add(input))
339
+ assertEq(r1, r2)
340
+ const [, heads] = virtualOk(state2)(e.head('x'))
309
341
  assertEq(heads.length, 1)
310
342
  },
311
343
  // Two `add` calls describing the same logical revision but spelling the
@@ -315,44 +347,39 @@ export const proof = {
315
347
  // `canonicalHash` exists to prevent.
316
348
  addRevisionCanonicalizesParentSpellingBeforeSerializing: () => {
317
349
  const c = fileCas(sha256)(home)
318
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
350
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
319
351
  const e = evo(c)(cacheKey)
320
352
  /** @type {RevisionData} */
321
353
  const root = { parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x2en)) }
322
- const [state1, rootResult] = virtual(state0)(e.add(root))
323
- assert(rootResult[0] === 'ok', ['expected root ok', rootResult])
324
- const rootHash = rootResult[1]
354
+ const [state1, rootResult] = virtualOk(state0)(e.add(root))
355
+ const rootHash = rootResult
325
356
  assert(rootHash !== rootHash.toUpperCase(), 'expected the sample hash to contain letters')
326
357
  /** @type {RevisionData} */
327
358
  const child1 = { parents: [rootHash], subject: 'doc' }
328
- const [state2, child1Result] = virtual(state1)(e.add(child1))
329
- assert(child1Result[0] === 'ok', ['expected child1 ok', child1Result])
359
+ const [state2, child1Result] = virtualOk(state1)(e.add(child1))
330
360
  /** @type {RevisionData} */
331
361
  const child2 = { parents: [rootHash.toUpperCase()], subject: 'doc' }
332
- const [state3, child2Result] = virtual(state2)(e.add(child2))
333
- assert(child2Result[0] === 'ok', ['expected child2 ok', child2Result])
334
- assertEq(child1Result[1], child2Result[1])
335
- const [, heads] = virtual(state3)(e.head('doc'))
362
+ const [state3, child2Result] = virtualOk(state2)(e.add(child2))
363
+ assertEq(child1Result, child2Result)
364
+ const [, heads] = virtualOk(state3)(e.head('doc'))
336
365
  assertEq(heads.length, 1)
337
- assertEq(heads[0], child1Result[1])
366
+ assertEq(heads[0], child1Result)
338
367
  },
339
368
  // `subject` omitted with exactly one parent is inherited from that
340
369
  // parent's own `subject`.
341
370
  addRevisionResolvesSubjectFromSingleParent: () => {
342
371
  const c = fileCas(sha256)(home)
343
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
372
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
344
373
  const e = evo(c)(cacheKey)
345
374
  /** @type {RevisionData} */
346
375
  const root = { parents: [], subject: 'inherit-me', snapshot: vecToCBase32(vec8(0x44n)) }
347
- const [state1, rootResult] = virtual(state0)(e.add(root))
348
- assert(rootResult[0] === 'ok', ['expected root ok', rootResult])
349
- const rootHash = rootResult[1]
376
+ const [state1, rootResult] = virtualOk(state0)(e.add(root))
377
+ const rootHash = rootResult
350
378
  /** @type {RevisionData} */
351
379
  const child = { parents: [rootHash] }
352
- const [state2, childResult] = virtual(state1)(e.add(child))
353
- assert(childResult[0] === 'ok', ['expected child ok', childResult])
354
- const childHash = childResult[1]
355
- const [, heads] = virtual(state2)(e.head('inherit-me'))
380
+ const [state2, childResult] = virtualOk(state1)(e.add(child))
381
+ const childHash = childResult
382
+ const [, heads] = virtualOk(state2)(e.head('inherit-me'))
356
383
  assertEq(heads.length, 1)
357
384
  assertEq(heads[0], childHash)
358
385
  },
@@ -362,21 +389,19 @@ export const proof = {
362
389
  // inherits the parent's stored snapshot and gets `generation` 1.
363
390
  addComputesGenerationAndResolvesSnapshot: () => {
364
391
  const c = fileCas(sha256)(home)
365
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
392
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
366
393
  const e = evo(c)(cacheKey)
367
394
  const subjectHash = vecToCBase32(vec8(0x71n))
368
- const [state1, rootResult] = virtual(state0)(e.add({ parents: [], subject: subjectHash }))
369
- assert(rootResult[0] === 'ok', ['expected root ok', rootResult])
370
- const rootHashVec = unwrap(cBase32ToVec(rootResult[1]))
371
- const [state2, root] = virtual(state1)(decodeRevisionBlob(c)(rootHashVec))
395
+ const [state1, rootResult] = virtualOk(state0)(e.add({ parents: [], subject: subjectHash }))
396
+ const rootHashVec = unwrap(cBase32ToVec(rootResult))
397
+ const [state2, root] = virtualOk(state1)(decodeRevisionBlob(c)(rootHashVec))
372
398
  assert(root !== null, 'expected the stored root to decode')
373
399
  assertEq(root?.generation, 0)
374
400
  assertEq(root?.snapshot, subjectHash)
375
401
 
376
- const [state3, childResult] = virtual(state2)(e.add({ parents: [rootResult[1]], subject: subjectHash }))
377
- assert(childResult[0] === 'ok', ['expected child ok', childResult])
378
- const childHashVec = unwrap(cBase32ToVec(childResult[1]))
379
- const [, child] = virtual(state3)(decodeRevisionBlob(c)(childHashVec))
402
+ const [state3, childResult] = virtualOk(state2)(e.add({ parents: [rootResult], subject: subjectHash }))
403
+ const childHashVec = unwrap(cBase32ToVec(childResult))
404
+ const [, child] = virtualOk(state3)(decodeRevisionBlob(c)(childHashVec))
380
405
  assert(child !== null, 'expected the stored child to decode')
381
406
  assertEq(child?.generation, 1)
382
407
  assertEq(child?.snapshot, subjectHash)
@@ -385,24 +410,19 @@ export const proof = {
385
410
  // and one at generation 1 yield generation 3.
386
411
  addComputesMergeGenerationFromMaxOfParents: () => {
387
412
  const c = fileCas(sha256)(home)
388
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
413
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
389
414
  const e = evo(c)(cacheKey)
390
415
  const snap = vecToCBase32(vec8(0x72n))
391
416
  // Mainline chain: root(gen0) → a(gen1) → b(gen2).
392
- const [state1, root] = virtual(state0)(e.add({ parents: [], subject: 'm', snapshot: snap }))
393
- assert(root[0] === 'ok', ['expected root ok', root])
394
- const [state2, a] = virtual(state1)(e.add({ parents: [root[1]], subject: 'm', snapshot: snap }))
395
- assert(a[0] === 'ok', ['expected a ok', a])
396
- const [state3, b] = virtual(state2)(e.add({ parents: [a[1]], subject: 'm', snapshot: snap }))
397
- assert(b[0] === 'ok', ['expected b ok', b])
417
+ const [state1, root] = virtualOk(state0)(e.add({ parents: [], subject: 'm', snapshot: snap }))
418
+ const [state2, a] = virtualOk(state1)(e.add({ parents: [root], subject: 'm', snapshot: snap }))
419
+ const [state3, b] = virtualOk(state2)(e.add({ parents: [a], subject: 'm', snapshot: snap }))
398
420
  // Side branch off root: c(gen1).
399
- const [state4, cRev] = virtual(state3)(e.add({ parents: [root[1]], subject: 'm', snapshot: snap }))
400
- assert(cRev[0] === 'ok', ['expected c ok', cRev])
421
+ const [state4, cRev] = virtualOk(state3)(e.add({ parents: [root], subject: 'm', snapshot: snap }))
401
422
  // Merge of b(gen2) and c(gen1) → gen3.
402
- const [state5, merge] = virtual(state4)(e.add({ parents: [b[1], cRev[1]], subject: 'm', snapshot: snap }))
403
- assert(merge[0] === 'ok', ['expected merge ok', merge])
404
- const mergeHashVec = unwrap(cBase32ToVec(merge[1]))
405
- const [, mergeRev] = virtual(state5)(decodeRevisionBlob(c)(mergeHashVec))
423
+ const [state5, merge] = virtualOk(state4)(e.add({ parents: [b, cRev], subject: 'm', snapshot: snap }))
424
+ const mergeHashVec = unwrap(cBase32ToVec(merge))
425
+ const [, mergeRev] = virtualOk(state5)(decodeRevisionBlob(c)(mergeHashVec))
406
426
  assert(mergeRev !== null, 'expected the stored merge to decode')
407
427
  assertEq(mergeRev?.generation, 3)
408
428
  },
@@ -412,25 +432,22 @@ export const proof = {
412
432
  // byte-identical to the same `add` without it.
413
433
  addIgnoresSuppliedGeneration: () => {
414
434
  const c = fileCas(sha256)(home)
415
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
435
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
416
436
  const e = evo(c)(cacheKey)
417
437
  const subjectHash = vecToCBase32(vec8(0x73n))
418
- const [state1, claimed] = virtual(state0)(e.add({ parents: [], subject: subjectHash, generation: 42 }))
419
- assert(claimed[0] === 'ok', ['expected add ok', claimed])
420
- const [state2, plain] = virtual(state1)(e.add({ parents: [], subject: subjectHash }))
421
- assert(plain[0] === 'ok', ['expected add ok', plain])
422
- assertEq(claimed[1], plain[1])
423
- const [, revision] = virtual(state2)(e.revision(claimed[1]))
438
+ const [state1, claimed] = virtualOk(state0)(e.add({ parents: [], subject: subjectHash, generation: 42 }))
439
+ const [state2, plain] = virtualOk(state1)(e.add({ parents: [], subject: subjectHash }))
440
+ assertEq(claimed, plain)
441
+ const [, revision] = virtual(state2)(e.revision(claimed))
424
442
  assert(revision[0] === 'ok', ['expected revision ok', revision])
425
443
  assertEq(revision[1].generation, 0)
426
444
  },
427
445
  addRevisionSubjectRequiredForZeroParents: () => {
428
446
  const c = fileCas(sha256)(home)
429
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
447
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
430
448
  const e = evo(c)(cacheKey)
431
449
  const [, result] = virtual(state0)(e.add({ parents: [] }))
432
- assertEq(result[0], 'error')
433
- assert(result[0] === 'error' && result[1].includes('subject is required'), ['unexpected message', result])
450
+ assertEvoError(result, 'subject is required')
434
451
  },
435
452
  // Zero parents and no input `snapshot`: `resolveSnapshot` falls back to
436
453
  // `subject` as the reference, which must itself be a hash. A subject with
@@ -439,40 +456,36 @@ export const proof = {
439
456
  // an explicit `snapshot`.
440
457
  addRevisionNonHashSubjectWithoutSnapshotIsError: () => {
441
458
  const c = fileCas(sha256)(home)
442
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
459
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
443
460
  const e = evo(c)(cacheKey)
444
461
  const [, result] = virtual(state0)(e.add({ parents: [], subject: 'not-a-hash!' }))
445
- assertEq(result[0], 'error')
446
- assert(result[0] === 'error' && result[1].includes('subject must be a valid hash'), ['unexpected message', result])
462
+ assertEvoError(result, 'subject must be a valid hash')
447
463
  },
448
464
  addRevisionInvalidParentHashIsError: () => {
449
465
  const c = fileCas(sha256)(home)
450
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
466
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
451
467
  const e = evo(c)(cacheKey)
452
468
  const [, result] = virtual(state0)(e.add({ parents: ['not a valid cbase32!'] }))
453
- assertEq(result[0], 'error')
454
- assert(result[0] === 'error' && result[1].includes('invalid parent hash'), ['unexpected message', result])
469
+ assertEvoError(result, 'invalid parent hash')
455
470
  },
456
471
  addRevisionParentNotARevisionIsError: () => {
457
472
  const c = fileCas(sha256)(home)
458
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
473
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
459
474
  const e = evo(c)(cacheKey)
460
475
  const missingParent = vecToCBase32(vec8(0x55n))
461
476
  const [, result] = virtual(state0)(e.add({ parents: [missingParent] }))
462
- assertEq(result[0], 'error')
463
- assert(result[0] === 'error' && result[1].includes('parent is not a revision blob'), ['unexpected message', result])
477
+ assertEvoError(result, 'parent is not a revision blob')
464
478
  },
465
479
  // An explicit `subject` must not bypass parent validation: a missing
466
480
  // parent is still an error even though `subject` alone would otherwise
467
481
  // resolve without looking at any parent.
468
482
  addRevisionValidatesParentsEvenWithExplicitSubject: () => {
469
483
  const c = fileCas(sha256)(home)
470
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
484
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
471
485
  const e = evo(c)(cacheKey)
472
486
  const missingParent = vecToCBase32(vec8(0x66n))
473
487
  const [, result] = virtual(state0)(e.add({ parents: [missingParent], subject: 'doc' }))
474
- assertEq(result[0], 'error')
475
- assert(result[0] === 'error' && result[1].includes('parent is not a revision blob'), ['unexpected message', result])
488
+ assertEvoError(result, 'parent is not a revision blob')
476
489
  },
477
490
  // A revision models one step in the evolution of a single subject: a
478
491
  // parent that actually belongs to a *different* subject must be
@@ -482,27 +495,24 @@ export const proof = {
482
495
  // never remove the parent from its real subject's head set.
483
496
  addRevisionRejectsCrossSubjectParent: () => {
484
497
  const c = fileCas(sha256)(home)
485
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
498
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
486
499
  const e = evo(c)(cacheKey)
487
500
  /** @type {RevisionData} */
488
501
  const rootA = { parents: [], subject: 'A', snapshot: vecToCBase32(vec8(0x67n)) }
489
- const [state1, rootAResult] = virtual(state0)(e.add(rootA))
490
- assert(rootAResult[0] === 'ok', ['expected rootA ok', rootAResult])
491
- const [, result] = virtual(state1)(e.add({ parents: [rootAResult[1]], subject: 'B' }))
492
- assertEq(result[0], 'error')
493
- assert(result[0] === 'error' && result[1].includes('different subject'), ['unexpected message', result])
502
+ const [state1, rootAResult] = virtualOk(state0)(e.add(rootA))
503
+ const [, result] = virtual(state1)(e.add({ parents: [rootAResult], subject: 'B' }))
504
+ assertEvoError(result, 'different subject')
494
505
  },
495
506
  // With multiple parents, a failure on an earlier one short-circuits the
496
507
  // fold — the second (here, a well-formed but nonexistent) parent is
497
508
  // never even looked up.
498
509
  addRevisionShortCircuitsOnFirstInvalidParentAmongMultiple: () => {
499
510
  const c = fileCas(sha256)(home)
500
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
511
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
501
512
  const e = evo(c)(cacheKey)
502
513
  const missingParent = vecToCBase32(vec8(0x99n))
503
514
  const [, result] = virtual(state0)(e.add({ parents: ['not a valid cbase32!', missingParent], subject: 'doc' }))
504
- assertEq(result[0], 'error')
505
- assert(result[0] === 'error' && result[1].includes('invalid parent hash'), ['unexpected message', result])
515
+ assertEvoError(result, 'invalid parent hash')
506
516
  },
507
517
  // Two *valid* parents without an explicit `snapshot` cannot resolve one
508
518
  // (no single parent snapshot to inherit) — a distinct failure from a
@@ -510,37 +520,35 @@ export const proof = {
510
520
  // boundary now that the format requires an explicit `snapshot`.
511
521
  addRevisionInvalidReferencesIsError: () => {
512
522
  const c = fileCas(sha256)(home)
513
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
523
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
514
524
  const e = evo(c)(cacheKey)
515
525
  /** @type {RevisionData} */
516
526
  const rootA = { parents: [], subject: 'merge', snapshot: vecToCBase32(vec8(0x77n)) }
517
- const [state1, rootAResult] = virtual(state0)(e.add(rootA))
518
- assert(rootAResult[0] === 'ok', ['expected rootA ok', rootAResult])
527
+ const [state1, rootAResult] = virtualOk(state0)(e.add(rootA))
519
528
  /** @type {RevisionData} */
520
529
  const rootB = { parents: [], subject: 'merge', snapshot: vecToCBase32(vec8(0x88n)) }
521
- const [state2, rootBResult] = virtual(state1)(e.add(rootB))
522
- assert(rootBResult[0] === 'ok', ['expected rootB ok', rootBResult])
523
- const [, result] = virtual(state2)(e.add({ parents: [rootAResult[1], rootBResult[1]], subject: 'merge' }))
524
- assertEq(result[0], 'error')
525
- assert(result[0] === 'error' && !result[1].includes('parent is not a revision blob'), ['unexpected message', result])
530
+ const [state2, rootBResult] = virtualOk(state1)(e.add(rootB))
531
+ const [, result] = virtual(state2)(e.add({ parents: [rootAResult, rootBResult], subject: 'merge' }))
532
+ assert(
533
+ result[0] === 'error' && result[1][0] === 'evoError'
534
+ && !result[1][1].includes('parent is not a revision blob'),
535
+ ['unexpected message', result])
526
536
  },
527
537
  addRevisionTooLargeToEncodeIsError: () => {
528
538
  const c = fileCas(sha256)(home)
529
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
539
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
530
540
  const e = evo(c)(cacheKey)
531
541
  const hugeSubject = 'x'.repeat(200_000)
532
542
  /** @type {RevisionData} */
533
543
  const input = { parents: [], subject: hugeSubject, snapshot: vecToCBase32(vec8(0x88n)) }
534
544
  const [, result] = virtual(state0)(e.add(input))
535
- assertEq(result[0], 'error')
536
- assert(result[0] === 'error' && result[1] === 'revision too large to encode', ['unexpected message', result])
545
+ assertEvoError(result, 'revision too large to encode')
537
546
  },
538
547
  addRevisionWriteFailureIsError: () => {
539
- const [, cacheKey] = virtual(emptyState)(initEvo(writeFailingCas))
548
+ const [, cacheKey] = virtualOk(emptyState)(initEvo(writeFailingCas))
540
549
  const e = evo(writeFailingCas)(cacheKey)
541
550
  const [, result] = virtual(emptyState)(e.add({ parents: [], subject: vecToCBase32(vec8(0x99n)) }))
542
- assertEq(result[0], 'error')
543
- assert(result[0] === 'error' && result[1] === 'failed to write revision to CAS', ['unexpected message', result])
551
+ assertEvoError(result, 'failed to write revision to CAS')
544
552
  },
545
553
  // Every way `revision` can fail is its own message, not one `null`: an
546
554
  // undecodable hash, a hash the store has nothing under, a read that failed
@@ -549,41 +557,37 @@ export const proof = {
549
557
  // stores of arbitrary content); this read keeps them apart.
550
558
  revisionInvalidHashIsError: () => {
551
559
  const c = fileCas(sha256)(home)
552
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
560
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
553
561
  const e = evo(c)(cacheKey)
554
562
  const [, result] = virtual(state0)(e.revision('not a valid cbase32!'))
555
- assertEq(result[0], 'error')
556
- assert(result[0] === 'error' && result[1].includes('invalid hash'), ['unexpected message', result])
563
+ assertEvoError(result, 'invalid hash')
557
564
  },
558
565
  revisionMissingHashIsError: () => {
559
566
  const c = fileCas(sha256)(home)
560
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
567
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
561
568
  const e = evo(c)(cacheKey)
562
569
  const [, result] = virtual(state0)(e.revision(vecToCBase32(vec8(0x9an))))
563
- assertEq(result[0], 'error')
564
- assert(result[0] === 'error' && result[1].includes('revision not found'), ['unexpected message', result])
570
+ assertEvoError(result, 'revision not found')
565
571
  },
566
572
  // Regression: a read failure that is not a missing shard — a permission or
567
573
  // mid-stream I/O error, or a blob too large to buffer into one `Vec` —
568
574
  // must not be reported as "not found". The blob may well be there; saying
569
575
  // it is absent would be a false answer, not merely a vague one.
570
576
  revisionReadFailureIsNotReportedAsMissing: () => {
571
- const [state0, cacheKey] = virtual(emptyState)(initEvo(readFailingCas))
577
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(readFailingCas))
572
578
  const e = evo(readFailingCas)(cacheKey)
573
579
  const [, result] = virtual(state0)(e.revision(vecToCBase32(vec8(0x9bn))))
574
- assertEq(result[0], 'error')
575
- assert(result[0] === 'error' && result[1].includes('failed to read revision'), ['unexpected message', result])
580
+ assertEvoError(result, 'failed to read revision')
576
581
  },
577
582
  revisionNonRevisionBlobIsError: () => {
578
583
  const c = fileCas(sha256)(home)
579
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
584
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
580
585
  const e = evo(c)(cacheKey)
581
586
  const content = vec8(0x41n) // 'A' — valid UTF-8, not revision JSON
582
- const [state1, w] = virtual(state0)(c.write(nonEmpty(ok(content), /** @type {List<never, Ok<Vec>>} */ (elEmpty()))))
587
+ const [state1, w] = virtual(state0)(c.write(nonEmpty(content, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
583
588
  assert(w[0] === 'ok', ['expected write ok', w])
584
589
  const [, result] = virtual(state1)(e.revision(vecToCBase32(w[1])))
585
- assertEq(result[0], 'error')
586
- assert(result[0] === 'error' && result[1].includes('not a revision blob'), ['unexpected message', result])
590
+ assertEvoError(result, 'not a revision blob')
587
591
  },
588
592
  // A blob written by somebody else can spell its references in any accepted
589
593
  // cbase32 alias (case, `i`/`l`/`o`). What `revision` returns is
@@ -591,7 +595,7 @@ export const proof = {
591
595
  // directly instead of knowing about cbase32 aliasing.
592
596
  revisionCanonicalizesReferenceSpellings: () => {
593
597
  const c = fileCas(sha256)(home)
594
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
598
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
595
599
  const e = evo(c)(cacheKey)
596
600
  const parentCanonical = vecToCBase32(vec8(0xffn))
597
601
  const snapshotCanonical = vecToCBase32(vec8(0xfen))
@@ -603,7 +607,7 @@ export const proof = {
603
607
  const text = `{"dialect":"${revisionDialect}","subject":"doc","parents":["${parentAlias}"],"snapshot":"${snapshotAlias}","generation":1}`
604
608
  const bytes = tryUtf8(text)
605
609
  assert(bytes !== null, 'expected the sample revision text to encode as UTF-8')
606
- const [state1, w] = virtual(state0)(c.write(nonEmpty(ok(bytes), /** @type {List<never, Ok<Vec>>} */ (elEmpty()))))
610
+ const [state1, w] = virtual(state0)(c.write(nonEmpty(bytes, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
607
611
  assert(w[0] === 'ok', ['expected write ok', w])
608
612
  const [, result] = virtual(state1)(e.revision(vecToCBase32(w[1])))
609
613
  assert(result[0] === 'ok', ['expected revision ok', result])
@@ -618,78 +622,160 @@ export const proof = {
618
622
  // can be added again unchanged — same content, same hash.
619
623
  revisionRoundTripsThroughAdd: () => {
620
624
  const c = fileCas(sha256)(home)
621
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
625
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
622
626
  const e = evo(c)(cacheKey)
623
627
  const subjectHash = vecToCBase32(vec8(0x74n))
624
- const [state1, root] = virtual(state0)(e.add({ parents: [], subject: subjectHash }))
625
- assert(root[0] === 'ok', ['expected root ok', root])
626
- const [state2, child] = virtual(state1)(e.add({ parents: [root[1]], archived: true }))
627
- assert(child[0] === 'ok', ['expected child ok', child])
628
- const [state3, result] = virtual(state2)(e.revision(child[1]))
628
+ const [state1, root] = virtualOk(state0)(e.add({ parents: [], subject: subjectHash }))
629
+ const [state2, child] = virtualOk(state1)(e.add({ parents: [root], archived: true }))
630
+ const [state3, result] = virtual(state2)(e.revision(child))
629
631
  assert(result[0] === 'ok', ['expected revision ok', result])
630
632
  assertEq(result[1].subject, subjectHash)
631
633
  assertEq(result[1].snapshot, subjectHash)
632
634
  assertEq(result[1].generation, 1)
633
635
  assertEq(result[1].archived, true)
634
636
  assertEq(result[1].parents.length, 1)
635
- assertEq(result[1].parents[0], root[1])
636
- const [, readded] = virtual(state3)(e.add(result[1]))
637
- assert(readded[0] === 'ok', ['expected re-add ok', readded])
638
- assertEq(readded[1], child[1])
637
+ assertEq(result[1].parents[0], root)
638
+ const [, readded] = virtualOk(state3)(e.add(result[1]))
639
+ assertEq(readded, child)
639
640
  },
640
641
  revisionLockRoundTripsAndCanonicalizes: () => {
641
642
  const c = fileCas(sha256)(home)
642
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
643
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
643
644
  const e = evo(c)(cacheKey)
644
645
  const canonical = vecToCBase32(vec8(0xffn))
645
646
  const alias = canonical.toUpperCase()
646
- const [state1, added] = virtual(state0)(e.add({
647
+ const [state1, added] = virtualOk(state0)(e.add({
647
648
  parents: [], subject: 'doc', snapshot: canonical,
648
649
  lock: { dependency: alias },
649
650
  }))
650
- assert(added[0] === 'ok', ['expected add ok', added])
651
- const [, result] = virtual(state1)(e.revision(added[1]))
651
+ const [, result] = virtual(state1)(e.revision(added))
652
+ assert(result[0] === 'ok', ['expected revision ok', result])
653
+ const lock = result[1].lock
654
+ assert(typeof lock === 'object', ['expected an inline lock map', lock])
655
+ assertEq(lock.dependency, canonical)
656
+ },
657
+ // A `lock` naming a `vnd.fjs.lock` blob is stored and read back as the
658
+ // reference it is: validated as a hash and re-spelled canonically, like
659
+ // `snapshot`, and never followed — resolving one is a resolver's job.
660
+ revisionSharedLockReferenceRoundTripsAndCanonicalizes: () => {
661
+ const c = fileCas(sha256)(home)
662
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
663
+ const e = evo(c)(cacheKey)
664
+ const canonical = vecToCBase32(vec8(0xffn))
665
+ const alias = canonical.toUpperCase()
666
+ const [state1, added] = virtualOk(state0)(e.add({
667
+ parents: [], subject: 'doc', snapshot: canonical, lock: alias,
668
+ }))
669
+ const [state2, result] = virtual(state1)(e.revision(added))
670
+ assert(result[0] === 'ok', ['expected revision ok', result])
671
+ assertEq(result[1].lock, canonical)
672
+ const [, readded] = virtualOk(state2)(e.add(result[1]))
673
+ assertEq(readded, added)
674
+ },
675
+ // A shared-lock reference is checked as a hash on the way in, so a
676
+ // non-cBase32 one fails the write instead of being stored unresolvable.
677
+ revisionInvalidSharedLockReferenceIsRejected: () => {
678
+ const c = fileCas(sha256)(home)
679
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
680
+ const e = evo(c)(cacheKey)
681
+ const [, result] = virtual(state0)(e.add({
682
+ parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0xffn)),
683
+ lock: 'https://example.com/lock',
684
+ }))
685
+ assertEvoError(result, 'lock reference is not a valid hash')
686
+ },
687
+ // A nested lock map round-trips with its scope structure intact, and
688
+ // canonicalization recurses into it: an alias spelling bound inside a
689
+ // scope comes back canonical, like a direct one.
690
+ revisionNestedLockRoundTripsAndCanonicalizes: () => {
691
+ const c = fileCas(sha256)(home)
692
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
693
+ const e = evo(c)(cacheKey)
694
+ const canonical = vecToCBase32(vec8(0xffn))
695
+ const alias = canonical.toUpperCase()
696
+ const [state1, added] = virtualOk(state0)(e.add({
697
+ parents: [], subject: 'doc', snapshot: canonical,
698
+ lock: { B: { D: alias }, C: canonical },
699
+ }))
700
+ const [state2, result] = virtual(state1)(e.revision(added))
652
701
  assert(result[0] === 'ok', ['expected revision ok', result])
653
- assertEq(result[1].lock?.dependency, canonical)
702
+ const lock = result[1].lock
703
+ assert(typeof lock === 'object', ['expected an inline lock map', lock])
704
+ const scope = lock.B
705
+ assert(typeof scope === 'object', ['expected a nested scope', scope])
706
+ assertEq(scope.D, canonical)
707
+ // The read value is still valid `add` input, and re-adding it reuses
708
+ // the same address — the round trip loses nothing.
709
+ const [, readded] = virtualOk(state2)(e.add(result[1]))
710
+ assertEq(readded, added)
711
+ },
712
+ // Semantic checking recurses: a non-hash string inside a nested scope
713
+ // fails the write, like one at the root.
714
+ revisionInvalidNestedLockValueIsRejected: () => {
715
+ const c = fileCas(sha256)(home)
716
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
717
+ const e = evo(c)(cacheKey)
718
+ const [, result] = virtual(state0)(e.add({
719
+ parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x42n)),
720
+ lock: { B: { dependency: 'not a hash!' } },
721
+ }))
722
+ assertEvoError(result, 'B/dependency')
654
723
  },
655
724
  revisionAbsentAndEmptyLocksRemainDistinct: () => {
656
725
  const c = fileCas(sha256)(home)
657
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
726
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
658
727
  const e = evo(c)(cacheKey)
659
728
  const snapshot = vecToCBase32(vec8(0x42n))
660
- const [state1, absent] = virtual(state0)(e.add({ parents: [], subject: 'absent', snapshot }))
661
- assert(absent[0] === 'ok', ['expected add ok', absent])
662
- const [state2, empty] = virtual(state1)(e.add({ parents: [], subject: 'empty', snapshot, lock: {} }))
663
- assert(empty[0] === 'ok', ['expected add ok', empty])
664
- const [state3, absentRead] = virtual(state2)(e.revision(absent[1]))
729
+ const [state1, absent] = virtualOk(state0)(e.add({ parents: [], subject: 'absent', snapshot }))
730
+ const [state2, empty] = virtualOk(state1)(e.add({ parents: [], subject: 'empty', snapshot, lock: {} }))
731
+ const [state3, absentRead] = virtual(state2)(e.revision(absent))
665
732
  assert(absentRead[0] === 'ok', ['expected revision ok', absentRead])
666
- const [, emptyRead] = virtual(state3)(e.revision(empty[1]))
733
+ const [, emptyRead] = virtual(state3)(e.revision(empty))
667
734
  assert(emptyRead[0] === 'ok', ['expected revision ok', emptyRead])
668
735
  assertEq(absentRead[1].lock, undefined)
669
736
  assertEq(Object.keys(emptyRead[1].lock ?? {}).length, 0)
670
737
  },
671
738
  revisionInvalidLockValueIsRejected: () => {
672
739
  const c = fileCas(sha256)(home)
673
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
740
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
674
741
  const e = evo(c)(cacheKey)
675
742
  const [, result] = virtual(state0)(e.add({
676
743
  parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x42n)),
677
744
  lock: { dependency: 'not a hash!' },
678
745
  }))
679
- assertEq(result[0], 'error')
746
+ assertEvoError(result, 'dependency')
680
747
  },
681
748
  equivalentLockOrdersReuseOneCasAddress: () => {
682
749
  const c = fileCas(sha256)(home)
683
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
750
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
751
+ const e = evo(c)(cacheKey)
752
+ const a = vecToCBase32(vec8(0x1an))
753
+ const b = vecToCBase32(vec8(0x2bn))
754
+ const snapshot = vecToCBase32(vec8(0x3cn))
755
+ const [state1, first] = virtualOk(state0)(e.add({ parents: [], subject: 'doc', snapshot, lock: { '2': b, '10': a } }))
756
+ const [, second] = virtualOk(state1)(e.add({ lock: { '10': a, '2': b }, snapshot, subject: 'doc', parents: [] }))
757
+ assertEq(second, first)
758
+ },
759
+ // Canonical serialization reaches into nested scopes, so two adds
760
+ // differing only in the property order of a nested map — at the root and
761
+ // inside the scope alike — deduplicate to one blob rather than leaving
762
+ // two heads.
763
+ equivalentNestedLockOrdersReuseOneCasAddress: () => {
764
+ const c = fileCas(sha256)(home)
765
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
684
766
  const e = evo(c)(cacheKey)
685
767
  const a = vecToCBase32(vec8(0x1an))
686
768
  const b = vecToCBase32(vec8(0x2bn))
687
769
  const snapshot = vecToCBase32(vec8(0x3cn))
688
- const [state1, first] = virtual(state0)(e.add({ parents: [], subject: 'doc', snapshot, lock: { '2': b, '10': a } }))
689
- assert(first[0] === 'ok', ['expected add ok', first])
690
- const [, second] = virtual(state1)(e.add({ lock: { '10': a, '2': b }, snapshot, subject: 'doc', parents: [] }))
691
- assert(second[0] === 'ok', ['expected add ok', second])
692
- assertEq(second[1], first[1])
770
+ const [state1, first] = virtualOk(state0)(e.add({
771
+ parents: [], subject: 'doc', snapshot,
772
+ lock: { B: { '2': b, '10': a }, A: a },
773
+ }))
774
+ const [, second] = virtualOk(state1)(e.add({
775
+ parents: [], subject: 'doc', snapshot,
776
+ lock: { A: a, B: { '10': a, '2': b } },
777
+ }))
778
+ assertEq(second, first)
693
779
  },
694
780
  // A raw CAS write (e.g. `cas_add`) of valid revision content is folded
695
781
  // into the cache exactly as `addRevision` would, without going through
@@ -697,29 +783,29 @@ export const proof = {
697
783
  // same store consistent (see `fjs/mcp`).
698
784
  syncRevisionFoldsValidRevisionIntoCache: () => {
699
785
  const c = fileCas(sha256)(home)
700
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
786
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
701
787
  const subjectHash = vecToCBase32(vec8(0x13n))
702
788
  const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`
703
789
  const bytes = tryUtf8(text)
704
790
  assert(bytes !== null, 'expected sample revision to encode as UTF-8')
705
791
  const hashVec = vec8(0x14n)
706
- const [state1] = virtual(state0)(syncRevision(cacheKey)(hashVec)(bytes))
707
- const [, heads] = virtual(state1)(evo(c)(cacheKey).head(subjectHash))
792
+ const [state1] = virtualOk(state0)(syncRevision(cacheKey)(hashVec)(bytes))
793
+ const [, heads] = virtualOk(state1)(evo(c)(cacheKey).head(subjectHash))
708
794
  assertEq(heads.length, 1)
709
795
  assertEq(heads[0], vecToCBase32(hashVec))
710
796
  },
711
797
  syncRevisionIgnoresNonRevisionContent: () => {
712
798
  const c = fileCas(sha256)(home)
713
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
714
- const [state1] = virtual(state0)(syncRevision(cacheKey)(vec8(0x15n))(vec8(0x41n)))
715
- const [, subjects] = virtual(state1)(evo(c)(cacheKey).list())
799
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
800
+ const [state1] = virtualOk(state0)(syncRevision(cacheKey)(vec8(0x15n))(vec8(0x41n)))
801
+ const [, subjects] = virtualOk(state1)(evo(c)(cacheKey).list())
716
802
  assertEq(subjects.length, 0)
717
803
  },
718
804
  evoHeadUnknownSubjectIsEmpty: () => {
719
805
  const c = fileCas(sha256)(home)
720
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
806
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
721
807
  const e = evo(c)(cacheKey)
722
- const [, heads] = virtual(state0)(e.head('nope'))
808
+ const [, heads] = virtualOk(state0)(e.head('nope'))
723
809
  assertEq(heads.length, 0)
724
810
  },
725
811
  // Regression: subjects are arbitrary caller-supplied strings, so one can
@@ -729,21 +815,20 @@ export const proof = {
729
815
  // `.hashes` — the cache must use an own-property lookup instead.
730
816
  evoHeadUnknownPrototypeNamedSubjectIsEmpty: () => {
731
817
  const c = fileCas(sha256)(home)
732
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
818
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
733
819
  const e = evo(c)(cacheKey)
734
- const [, heads] = virtual(state0)(e.head('constructor'))
820
+ const [, heads] = virtualOk(state0)(e.head('constructor'))
735
821
  assertEq(heads.length, 0)
736
822
  },
737
823
  evoAddAndHeadHandlePrototypeNamedSubject: () => {
738
824
  const c = fileCas(sha256)(home)
739
- const [state0, cacheKey] = virtual(emptyState)(initEvo(c))
825
+ const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
740
826
  const e = evo(c)(cacheKey)
741
827
  /** @type {RevisionData} */
742
828
  const input = { parents: [], subject: 'toString', snapshot: vecToCBase32(vec8(0x16n)) }
743
- const [state1, result] = virtual(state0)(e.add(input))
744
- assert(result[0] === 'ok', ['expected add ok', result])
745
- const [, heads] = virtual(state1)(e.head('toString'))
829
+ const [state1, result] = virtualOk(state0)(e.add(input))
830
+ const [, heads] = virtualOk(state1)(e.head('toString'))
746
831
  assertEq(heads.length, 1)
747
- assertEq(heads[0], result[1])
832
+ assertEq(heads[0], result)
748
833
  },
749
834
  }