functionalscript 0.37.0 → 0.39.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 (517) hide show
  1. package/README.md +1 -1
  2. package/{fs → fjs}/asn.1/proof.f.js +32 -94
  3. package/fjs/asserts/module.f.d.ts +36 -0
  4. package/fjs/asserts/module.f.js +39 -0
  5. package/fjs/asserts/proof.f.d.ts +11 -0
  6. package/fjs/asserts/proof.f.js +18 -0
  7. package/{fs → fjs}/base_n/proof.f.js +12 -36
  8. package/{fs → fjs}/basen/base64/proof.f.d.ts +1 -0
  9. package/{fs → fjs}/basen/base64/proof.f.js +15 -1
  10. package/{fs → fjs}/basen/cbase32/proof.f.js +15 -42
  11. package/fjs/bnf/data/module.f.d.ts +52 -0
  12. package/{fs → fjs}/bnf/data/module.f.js +53 -3
  13. package/{fs → fjs}/bnf/data/proof.f.d.ts +1 -0
  14. package/{fs → fjs}/bnf/data/proof.f.js +54 -19
  15. package/{fs → fjs}/bnf/descent/module.f.d.ts +4 -14
  16. package/fjs/bnf/descent/module.f.js +124 -0
  17. package/{fs → fjs}/bnf/descent/proof.f.d.ts +1 -0
  18. package/{fs → fjs}/bnf/descent/proof.f.js +55 -24
  19. package/{fs → fjs}/bnf/ll1/module.f.d.ts +4 -4
  20. package/{fs → fjs}/bnf/ll1/module.f.js +7 -5
  21. package/{fs → fjs}/bnf/ll1/proof.f.d.ts +1 -0
  22. package/{fs → fjs}/bnf/ll1/proof.f.js +27 -10
  23. package/{fs → fjs}/bnf/module.f.d.ts +19 -5
  24. package/{fs → fjs}/bnf/module.f.js +10 -11
  25. package/fjs/bnf/proof.f.js +69 -0
  26. package/{fs → fjs}/bnf/testlib.f.js +2 -2
  27. package/fjs/bnf/token_symbol/module.f.d.ts +38 -0
  28. package/fjs/bnf/token_symbol/module.f.js +48 -0
  29. package/fjs/bnf/token_symbol/proof.f.d.ts +10 -0
  30. package/fjs/bnf/token_symbol/proof.f.js +37 -0
  31. package/{fs → fjs}/cas/cli/module.f.js +6 -9
  32. package/{fs → fjs}/cas/cli/proof.f.d.ts +4 -1
  33. package/{fs → fjs}/cas/cli/proof.f.js +29 -66
  34. package/fjs/cas/evo/mcp/module.f.d.ts +27 -0
  35. package/fjs/cas/evo/mcp/module.f.js +94 -0
  36. package/fjs/cas/evo/mcp/proof.f.d.ts +11 -0
  37. package/fjs/cas/evo/mcp/proof.f.js +126 -0
  38. package/fjs/cas/evo/module.f.d.ts +202 -0
  39. package/fjs/cas/evo/module.f.js +421 -0
  40. package/fjs/cas/evo/proof.f.d.ts +39 -0
  41. package/fjs/cas/evo/proof.f.js +572 -0
  42. package/fjs/cas/mcp/module.f.d.ts +49 -0
  43. package/{fs → fjs}/cas/mcp/module.f.js +61 -81
  44. package/{fs → fjs}/cas/mcp/proof.f.d.ts +4 -1
  45. package/{fs → fjs}/cas/mcp/proof.f.js +45 -17
  46. package/{fs → fjs}/cas/module.f.d.ts +10 -0
  47. package/{fs → fjs}/cas/module.f.js +89 -36
  48. package/fjs/cas/proof.f.d.ts +18 -0
  49. package/fjs/cas/proof.f.js +305 -0
  50. package/{fs → fjs}/ci/bun/module.f.js +3 -3
  51. package/{fs → fjs}/ci/common/module.f.d.ts +1 -2
  52. package/{fs → fjs}/ci/common/module.f.js +1 -5
  53. package/{fs → fjs}/ci/config/module.f.d.ts +8 -8
  54. package/{fs → fjs}/ci/config/module.f.js +8 -8
  55. package/{fs → fjs}/ci/deno/module.f.js +3 -3
  56. package/{fs → fjs}/ci/module.f.js +3 -4
  57. package/{fs → fjs}/ci/node/module.f.js +11 -9
  58. package/{fs → fjs}/ci/proof.f.js +16 -9
  59. package/{fs → fjs}/ci/rust/module.f.js +9 -1
  60. package/{fs → fjs}/cli/module.f.js +2 -2
  61. package/{fs → fjs}/cli/proof.f.js +22 -63
  62. package/{fs → fjs}/common/monoid/module.f.d.ts +46 -2
  63. package/fjs/common/monoid/module.f.js +88 -0
  64. package/fjs/common/monoid/proof.f.d.ts +9 -0
  65. package/fjs/common/monoid/proof.f.js +48 -0
  66. package/{fs → fjs}/crypto/hmac/proof.f.js +4 -12
  67. package/fjs/crypto/pow/proof.f.js +111 -0
  68. package/{fs → fjs}/crypto/secp/proof.f.js +12 -33
  69. package/{fs → fjs}/crypto/sha2/proof.f.js +18 -49
  70. package/{fs → fjs}/crypto/sign/module.f.d.ts +0 -5
  71. package/{fs → fjs}/crypto/sign/module.f.js +7 -4
  72. package/{fs → fjs}/crypto/sign/proof.f.js +36 -105
  73. package/{fs → fjs}/crypto/vdf/proof.f.js +22 -63
  74. package/{fs → fjs}/dev/module.f.js +25 -25
  75. package/{fs → fjs}/dev/package_json/proof.f.js +7 -7
  76. package/{fs → fjs}/dev/proof.f.js +2 -4
  77. package/{fs → fjs}/djs/ast/proof.f.js +4 -12
  78. package/{fs → fjs}/djs/module.f.js +5 -8
  79. package/{fs → fjs}/djs/parser/module.f.d.ts +8 -0
  80. package/{fs → fjs}/djs/parser/module.f.js +37 -0
  81. package/{fs → fjs}/djs/parser/proof.f.js +138 -400
  82. package/{fs → fjs}/djs/proof.f.js +13 -36
  83. package/{fs → fjs}/djs/serializer/proof.f.js +14 -39
  84. package/fjs/djs/tokenizer/module.f.d.ts +24 -0
  85. package/fjs/djs/tokenizer/module.f.js +472 -0
  86. package/{fs/djs/tokenizer-new → fjs/djs/tokenizer}/proof.f.d.ts +4 -3
  87. package/{fs/djs/tokenizer-new → fjs/djs/tokenizer}/proof.f.js +446 -445
  88. package/{fs → fjs}/djs/transpiler/module.f.js +6 -6
  89. package/{fs → fjs}/djs/transpiler/proof.f.d.ts +3 -0
  90. package/{fs → fjs}/djs/transpiler/proof.f.js +29 -30
  91. package/fjs/effects/eff/module.f.d.ts +72 -0
  92. package/fjs/effects/eff/module.f.js +46 -0
  93. package/fjs/effects/eff/proof.f.d.ts +11 -0
  94. package/fjs/effects/eff/proof.f.js +58 -0
  95. package/{fs → fjs}/effects/list/module.f.d.ts +11 -5
  96. package/{fs → fjs}/effects/list/module.f.js +2 -1
  97. package/{fs → fjs}/effects/memory/proof.f.js +15 -29
  98. package/fjs/effects/module.f.d.ts +388 -0
  99. package/fjs/effects/module.f.js +288 -0
  100. package/{fs → fjs}/effects/node/memory/proof.js +7 -13
  101. package/{fs → fjs}/effects/node/module.d.ts +1 -1
  102. package/{fs → fjs}/effects/node/module.f.d.ts +2 -2
  103. package/{fs → fjs}/effects/node/module.f.js +9 -11
  104. package/{fs → fjs}/effects/node/module.js +3 -3
  105. package/{fs → fjs}/effects/node/proof.f.d.ts +1 -0
  106. package/{fs → fjs}/effects/node/proof.f.js +121 -299
  107. package/{fs → fjs}/effects/node/virtual/proof.f.d.ts +1 -0
  108. package/{fs → fjs}/effects/node/virtual/proof.f.js +36 -29
  109. package/fjs/effects/proof.f.d.ts +51 -0
  110. package/fjs/effects/proof.f.js +178 -0
  111. package/{fs → fjs}/emergent_testing/module.f.js +55 -39
  112. package/{fs → fjs}/emergent_testing/proof.f.d.ts +4 -0
  113. package/{fs → fjs}/emergent_testing/proof.f.js +60 -27
  114. package/{fs → fjs}/fsc/proof.f.js +12 -34
  115. package/{fs → fjs}/fsm/proof.f.js +5 -12
  116. package/{fs → fjs}/mcp/module.f.d.ts +7 -0
  117. package/{fs → fjs}/mcp/module.f.js +17 -10
  118. package/{fs → fjs}/mcp/proof.f.js +17 -16
  119. package/{fs → fjs}/mcp/stdio/module.f.d.ts +4 -4
  120. package/{fs → fjs}/mcp/stdio/module.f.js +14 -14
  121. package/{fs → fjs}/media/html/proof.f.js +12 -33
  122. package/{fs → fjs}/media/json/parser/module.f.d.ts +5 -0
  123. package/{fs → fjs}/media/json/parser/module.f.js +14 -0
  124. package/{fs → fjs}/media/json/parser/proof.f.js +39 -114
  125. package/{fs → fjs}/media/json/rpc/module.f.js +1 -1
  126. package/{fs → fjs}/media/json/schema/module.f.d.ts +2 -2
  127. package/{fs → fjs}/media/json/schema/module.f.js +23 -39
  128. package/{fs → fjs}/media/json/schema/proof.f.js +3 -6
  129. package/{fs → fjs}/media/json/serializer/proof.f.js +10 -27
  130. package/{fs → fjs}/media/module.f.d.ts +6 -6
  131. package/{fs → fjs}/media/module.f.js +3 -4
  132. package/{fs → fjs}/media/proof.f.js +5 -4
  133. package/{fs → fjs}/media/revision/module.f.d.ts +31 -4
  134. package/{fs → fjs}/media/revision/module.f.js +35 -23
  135. package/{fs → fjs}/media/revision/proof.f.d.ts +10 -8
  136. package/fjs/media/revision/proof.f.js +140 -0
  137. package/{fs → fjs}/media/type/module.f.js +2 -2
  138. package/{fs → fjs}/media/type/proof.f.js +8 -15
  139. package/{fs → fjs}/module.f.js +5 -9
  140. package/{fs → fjs}/path/proof.f.js +27 -78
  141. package/{fs → fjs}/proof.f.js +2 -0
  142. package/{fs → fjs}/sul/id/proof.f.js +7 -21
  143. package/{fs → fjs}/sul/level/hash/proof.f.js +5 -5
  144. package/{fs → fjs}/sul/level/literal/proof.f.js +12 -26
  145. package/fjs/text/ascii/proof.f.d.ts +6 -0
  146. package/{fs → fjs}/text/ascii/proof.f.js +4 -14
  147. package/{fs → fjs}/text/code_point/module.f.js +10 -10
  148. package/{fs → fjs}/text/proof.f.js +4 -9
  149. package/{fs → fjs}/text/sgr/proof.f.js +6 -15
  150. package/{fs → fjs}/text/utf16/module.f.js +1 -1
  151. package/{fs → fjs}/text/utf16/proof.f.js +23 -66
  152. package/{fs → fjs}/text/utf8/proof.f.js +56 -125
  153. package/{fs → fjs}/types/array/proof.f.js +18 -52
  154. package/fjs/types/bigfloat/proof.f.js +217 -0
  155. package/{fs → fjs}/types/bigint/module.f.js +4 -3
  156. package/{fs → fjs}/types/bigint/proof.f.js +49 -145
  157. package/{fs → fjs}/types/bit_vec/module.f.js +5 -15
  158. package/{fs → fjs}/types/bit_vec/proof.f.js +72 -214
  159. package/{fs → fjs}/types/btree/find/proof.f.js +23 -66
  160. package/{fs → fjs}/types/btree/proof.f.js +5 -12
  161. package/{fs → fjs}/types/btree/remove/module.f.d.ts +5 -0
  162. package/{fs → fjs}/types/btree/remove/module.f.js +12 -0
  163. package/{fs → fjs}/types/btree/remove/proof.f.d.ts +2 -0
  164. package/{fs → fjs}/types/btree/remove/proof.f.js +83 -230
  165. package/{fs → fjs}/types/btree/set/module.f.js +5 -2
  166. package/{fs → fjs}/types/btree/set/proof.f.js +35 -24
  167. package/{fs → fjs}/types/byte_set/proof.f.js +22 -63
  168. package/{fs → fjs}/types/function/compare/proof.f.js +13 -36
  169. package/{fs → fjs}/types/function/operator/proof.f.js +17 -48
  170. package/{fs → fjs}/types/function/proof.f.js +2 -3
  171. package/{fs → fjs}/types/list/module.f.d.ts +21 -0
  172. package/{fs → fjs}/types/list/module.f.js +17 -0
  173. package/{fs → fjs}/types/list/proof.f.d.ts +1 -0
  174. package/{fs → fjs}/types/list/proof.f.js +79 -164
  175. package/{fs → fjs}/types/map/proof.f.js +4 -12
  176. package/{fs → fjs}/types/nibble_set/proof.f.js +16 -45
  177. package/{fs → fjs}/types/nominal/proof.f.js +5 -10
  178. package/fjs/types/nullable/proof.f.js +29 -0
  179. package/{fs → fjs}/types/number/module.f.js +2 -1
  180. package/{fs → fjs}/types/number/proof.f.js +9 -24
  181. package/{fs → fjs}/types/object/proof.f.js +3 -6
  182. package/{fs → fjs}/types/patricia_trie/proof.f.js +21 -21
  183. package/{fs → fjs}/types/prime_field/module.f.js +2 -7
  184. package/{fs → fjs}/types/range/module.f.d.ts +1 -1
  185. package/fjs/types/range/module.f.js +2 -0
  186. package/fjs/types/range/proof.f.js +9 -0
  187. package/{fs → fjs}/types/range_map/proof.f.js +28 -81
  188. package/{fs → fjs}/types/result/proof.f.d.ts +3 -2
  189. package/fjs/types/result/proof.f.js +31 -0
  190. package/{fs → fjs}/types/rtti/common/module.f.d.ts +36 -1
  191. package/{fs → fjs}/types/rtti/common/module.f.js +44 -0
  192. package/fjs/types/rtti/common/proof.f.d.ts +8 -0
  193. package/fjs/types/rtti/common/proof.f.js +51 -0
  194. package/{fs → fjs}/types/rtti/parse/module.f.js +13 -22
  195. package/{fs → fjs}/types/rtti/parse/proof.f.js +7 -18
  196. package/{fs → fjs}/types/rtti/ts/module.f.d.ts +10 -4
  197. package/{fs → fjs}/types/rtti/validate/module.f.js +8 -29
  198. package/{fs → fjs}/types/rtti/validate/proof.f.js +4 -9
  199. package/{fs → fjs}/types/sorted_list/proof.f.js +8 -21
  200. package/{fs → fjs}/types/sorted_set/proof.f.js +14 -39
  201. package/{fs → fjs}/types/string/module.f.js +10 -2
  202. package/fjs/types/string/proof.f.js +77 -0
  203. package/fjs/types/string_set/proof.f.js +43 -0
  204. package/{fs → fjs}/types/ts/proof.f.js +15 -42
  205. package/{fs → fjs}/types/uint8array/module.f.js +7 -7
  206. package/{fs → fjs}/types/uint8array/proof.f.d.ts +4 -1
  207. package/{fs → fjs}/types/uint8array/proof.f.js +7 -7
  208. package/{fs → fjs}/website/module.f.js +2 -3
  209. package/{fs → fjs}/website/proof.f.js +3 -6
  210. package/package.json +8 -8
  211. package/fs/asserts/module.f.d.ts +0 -4
  212. package/fs/asserts/module.f.js +0 -9
  213. package/fs/asserts/proof.f.d.ts +0 -8
  214. package/fs/asserts/proof.f.js +0 -51
  215. package/fs/bnf/data/module.f.d.ts +0 -30
  216. package/fs/bnf/descent/module.f.js +0 -119
  217. package/fs/bnf/proof.f.js +0 -104
  218. package/fs/cas/mcp/module.f.d.ts +0 -36
  219. package/fs/cas/proof.f.d.ts +0 -20
  220. package/fs/cas/proof.f.js +0 -294
  221. package/fs/common/monoid/module.f.js +0 -44
  222. package/fs/common/monoid/proof.f.d.ts +0 -4
  223. package/fs/common/monoid/proof.f.js +0 -27
  224. package/fs/crypto/pow/proof.f.js +0 -161
  225. package/fs/djs/tokenizer/module.f.d.ts +0 -12
  226. package/fs/djs/tokenizer/module.f.js +0 -62
  227. package/fs/djs/tokenizer/proof.f.d.ts +0 -7
  228. package/fs/djs/tokenizer/proof.f.js +0 -543
  229. package/fs/djs/tokenizer-new/module.f.d.ts +0 -3
  230. package/fs/djs/tokenizer-new/module.f.js +0 -155
  231. package/fs/effects/module.f.d.ts +0 -106
  232. package/fs/effects/module.f.js +0 -80
  233. package/fs/effects/proof.f.d.ts +0 -25
  234. package/fs/effects/proof.f.js +0 -115
  235. package/fs/media/revision/proof.f.js +0 -119
  236. package/fs/text/ascii/proof.f.d.ts +0 -4
  237. package/fs/types/bigfloat/proof.f.js +0 -348
  238. package/fs/types/nullable/proof.f.js +0 -50
  239. package/fs/types/range/module.f.js +0 -2
  240. package/fs/types/range/proof.f.js +0 -18
  241. package/fs/types/result/proof.f.js +0 -52
  242. package/fs/types/string/proof.f.js +0 -118
  243. package/fs/types/string_set/proof.f.js +0 -82
  244. /package/{fs → fjs}/asn.1/module.f.d.ts +0 -0
  245. /package/{fs → fjs}/asn.1/module.f.js +0 -0
  246. /package/{fs → fjs}/asn.1/proof.f.d.ts +0 -0
  247. /package/{fs → fjs}/base_n/module.f.d.ts +0 -0
  248. /package/{fs → fjs}/base_n/module.f.js +0 -0
  249. /package/{fs → fjs}/base_n/proof.f.d.ts +0 -0
  250. /package/{fs → fjs}/basen/base128/module.f.d.ts +0 -0
  251. /package/{fs → fjs}/basen/base128/module.f.js +0 -0
  252. /package/{fs → fjs}/basen/base128/proof.f.d.ts +0 -0
  253. /package/{fs → fjs}/basen/base128/proof.f.js +0 -0
  254. /package/{fs → fjs}/basen/base64/module.f.d.ts +0 -0
  255. /package/{fs → fjs}/basen/base64/module.f.js +0 -0
  256. /package/{fs → fjs}/basen/cbase32/module.f.d.ts +0 -0
  257. /package/{fs → fjs}/basen/cbase32/module.f.js +0 -0
  258. /package/{fs → fjs}/basen/cbase32/proof.f.d.ts +0 -0
  259. /package/{fs → fjs}/bnf/proof.f.d.ts +0 -0
  260. /package/{fs → fjs}/bnf/testlib.f.d.ts +0 -0
  261. /package/{fs → fjs}/cas/cli/module.f.d.ts +0 -0
  262. /package/{fs → fjs}/ci/bun/module.f.d.ts +0 -0
  263. /package/{fs → fjs}/ci/deno/module.f.d.ts +0 -0
  264. /package/{fs → fjs}/ci/module.f.d.ts +0 -0
  265. /package/{fs → fjs}/ci/node/module.f.d.ts +0 -0
  266. /package/{fs → fjs}/ci/playwright/module.f.d.ts +0 -0
  267. /package/{fs → fjs}/ci/playwright/module.f.js +0 -0
  268. /package/{fs → fjs}/ci/proof.f.d.ts +0 -0
  269. /package/{fs → fjs}/ci/rust/module.f.d.ts +0 -0
  270. /package/{fs → fjs}/cli/module.f.d.ts +0 -0
  271. /package/{fs → fjs}/cli/proof.f.d.ts +0 -0
  272. /package/{fs → fjs}/crypto/hmac/module.f.d.ts +0 -0
  273. /package/{fs → fjs}/crypto/hmac/module.f.js +0 -0
  274. /package/{fs → fjs}/crypto/hmac/proof.f.d.ts +0 -0
  275. /package/{fs → fjs}/crypto/pow/module.f.d.ts +0 -0
  276. /package/{fs → fjs}/crypto/pow/module.f.js +0 -0
  277. /package/{fs → fjs}/crypto/pow/proof.f.d.ts +0 -0
  278. /package/{fs → fjs}/crypto/secp/module.f.d.ts +0 -0
  279. /package/{fs → fjs}/crypto/secp/module.f.js +0 -0
  280. /package/{fs → fjs}/crypto/secp/proof.f.d.ts +0 -0
  281. /package/{fs → fjs}/crypto/sha2/module.f.d.ts +0 -0
  282. /package/{fs → fjs}/crypto/sha2/module.f.js +0 -0
  283. /package/{fs → fjs}/crypto/sha2/proof.f.d.ts +0 -0
  284. /package/{fs → fjs}/crypto/sign/proof.f.d.ts +0 -0
  285. /package/{fs → fjs}/crypto/vdf/module.f.d.ts +0 -0
  286. /package/{fs → fjs}/crypto/vdf/module.f.js +0 -0
  287. /package/{fs → fjs}/crypto/vdf/proof.f.d.ts +0 -0
  288. /package/{fs → fjs}/dev/module.f.d.ts +0 -0
  289. /package/{fs → fjs}/dev/package_json/module.f.d.ts +0 -0
  290. /package/{fs → fjs}/dev/package_json/module.f.js +0 -0
  291. /package/{fs → fjs}/dev/package_json/proof.f.d.ts +0 -0
  292. /package/{fs → fjs}/dev/proof.f.d.ts +0 -0
  293. /package/{fs → fjs}/djs/ast/module.f.d.ts +0 -0
  294. /package/{fs → fjs}/djs/ast/module.f.js +0 -0
  295. /package/{fs → fjs}/djs/ast/proof.f.d.ts +0 -0
  296. /package/{fs → fjs}/djs/examples/input.f.d.ts +0 -0
  297. /package/{fs → fjs}/djs/examples/input.f.js +0 -0
  298. /package/{fs → fjs}/djs/examples/m.f.d.ts +0 -0
  299. /package/{fs → fjs}/djs/examples/m.f.js +0 -0
  300. /package/{fs → fjs}/djs/module.f.d.ts +0 -0
  301. /package/{fs → fjs}/djs/parser/proof.f.d.ts +0 -0
  302. /package/{fs → fjs}/djs/proof.f.d.ts +0 -0
  303. /package/{fs → fjs}/djs/serializer/module.f.d.ts +0 -0
  304. /package/{fs → fjs}/djs/serializer/module.f.js +0 -0
  305. /package/{fs → fjs}/djs/serializer/proof.f.d.ts +0 -0
  306. /package/{fs → fjs}/djs/transpiler/module.f.d.ts +0 -0
  307. /package/{fs → fjs}/effects/memory/module.f.d.ts +0 -0
  308. /package/{fs → fjs}/effects/memory/module.f.js +0 -0
  309. /package/{fs → fjs}/effects/memory/proof.f.d.ts +0 -0
  310. /package/{fs → fjs}/effects/mock/module.f.d.ts +0 -0
  311. /package/{fs → fjs}/effects/mock/module.f.js +0 -0
  312. /package/{fs → fjs}/effects/module.d.ts +0 -0
  313. /package/{fs → fjs}/effects/module.js +0 -0
  314. /package/{fs → fjs}/effects/node/memory/module.d.ts +0 -0
  315. /package/{fs → fjs}/effects/node/memory/module.js +0 -0
  316. /package/{fs → fjs}/effects/node/memory/proof.d.ts +0 -0
  317. /package/{fs → fjs}/effects/node/virtual/module.f.d.ts +0 -0
  318. /package/{fs → fjs}/effects/node/virtual/module.f.js +0 -0
  319. /package/{fs → fjs}/emergent_testing/all.test.d.ts +0 -0
  320. /package/{fs → fjs}/emergent_testing/all.test.js +0 -0
  321. /package/{fs → fjs}/emergent_testing/example.f.d.ts +0 -0
  322. /package/{fs → fjs}/emergent_testing/example.f.js +0 -0
  323. /package/{fs → fjs}/emergent_testing/module.f.d.ts +0 -0
  324. /package/{fs → fjs}/emergent_testing/scenarios/all.d.ts +0 -0
  325. /package/{fs → fjs}/emergent_testing/scenarios/all.js +0 -0
  326. /package/{fs → fjs}/emergent_testing/scenarios/async-subtests.fail.d.ts +0 -0
  327. /package/{fs → fjs}/emergent_testing/scenarios/async-subtests.fail.js +0 -0
  328. /package/{fs → fjs}/emergent_testing/scenarios/async-subtests.pass.d.ts +0 -0
  329. /package/{fs → fjs}/emergent_testing/scenarios/async-subtests.pass.js +0 -0
  330. /package/{fs → fjs}/emergent_testing/scenarios/async.fail.d.ts +0 -0
  331. /package/{fs → fjs}/emergent_testing/scenarios/async.fail.js +0 -0
  332. /package/{fs → fjs}/emergent_testing/scenarios/async.pass.d.ts +0 -0
  333. /package/{fs → fjs}/emergent_testing/scenarios/async.pass.js +0 -0
  334. /package/{fs → fjs}/emergent_testing/scenarios/fail.fail.d.ts +0 -0
  335. /package/{fs → fjs}/emergent_testing/scenarios/fail.fail.js +0 -0
  336. /package/{fs → fjs}/emergent_testing/scenarios/return-value.pass.d.ts +0 -0
  337. /package/{fs → fjs}/emergent_testing/scenarios/return-value.pass.js +0 -0
  338. /package/{fs → fjs}/emergent_testing/scenarios/thenable.pass.d.ts +0 -0
  339. /package/{fs → fjs}/emergent_testing/scenarios/thenable.pass.js +0 -0
  340. /package/{fs → fjs}/emergent_testing/scenarios/thenable2.pass.d.ts +0 -0
  341. /package/{fs → fjs}/emergent_testing/scenarios/thenable2.pass.js +0 -0
  342. /package/{fs → fjs}/emergent_testing/scenarios/throw.pass.d.ts +0 -0
  343. /package/{fs → fjs}/emergent_testing/scenarios/throw.pass.js +0 -0
  344. /package/{fs → fjs}/fsc/bnf.f.d.ts +0 -0
  345. /package/{fs → fjs}/fsc/bnf.f.js +0 -0
  346. /package/{fs → fjs}/fsc/json.f.d.ts +0 -0
  347. /package/{fs → fjs}/fsc/json.f.js +0 -0
  348. /package/{fs → fjs}/fsc/module.f.d.ts +0 -0
  349. /package/{fs → fjs}/fsc/module.f.js +0 -0
  350. /package/{fs → fjs}/fsc/proof.f.d.ts +0 -0
  351. /package/{fs → fjs}/fsm/module.f.d.ts +0 -0
  352. /package/{fs → fjs}/fsm/module.f.js +0 -0
  353. /package/{fs → fjs}/fsm/proof.f.d.ts +0 -0
  354. /package/{fs → fjs}/js/tokenizer/module.f.d.ts +0 -0
  355. /package/{fs → fjs}/js/tokenizer/module.f.js +0 -0
  356. /package/{fs → fjs}/js/tokenizer/proof.f.d.ts +0 -0
  357. /package/{fs → fjs}/js/tokenizer/proof.f.js +0 -0
  358. /package/{fs → fjs}/mcp/proof.f.d.ts +0 -0
  359. /package/{fs → fjs}/mcp/stdio/proof.f.d.ts +0 -0
  360. /package/{fs → fjs}/mcp/stdio/proof.f.js +0 -0
  361. /package/{fs → fjs}/media/html/module.f.d.ts +0 -0
  362. /package/{fs → fjs}/media/html/module.f.js +0 -0
  363. /package/{fs → fjs}/media/html/proof.f.d.ts +0 -0
  364. /package/{fs → fjs}/media/json/module.f.d.ts +0 -0
  365. /package/{fs → fjs}/media/json/module.f.js +0 -0
  366. /package/{fs → fjs}/media/json/parser/proof.f.d.ts +0 -0
  367. /package/{fs → fjs}/media/json/proof.f.d.ts +0 -0
  368. /package/{fs → fjs}/media/json/proof.f.js +0 -0
  369. /package/{fs → fjs}/media/json/rpc/module.f.d.ts +0 -0
  370. /package/{fs → fjs}/media/json/rpc/proof.f.d.ts +0 -0
  371. /package/{fs → fjs}/media/json/rpc/proof.f.js +0 -0
  372. /package/{fs → fjs}/media/json/schema/proof.f.d.ts +0 -0
  373. /package/{fs → fjs}/media/json/serializer/module.f.d.ts +0 -0
  374. /package/{fs → fjs}/media/json/serializer/module.f.js +0 -0
  375. /package/{fs → fjs}/media/json/serializer/proof.f.d.ts +0 -0
  376. /package/{fs → fjs}/media/json/tokenizer/module.f.d.ts +0 -0
  377. /package/{fs → fjs}/media/json/tokenizer/module.f.js +0 -0
  378. /package/{fs → fjs}/media/json/tokenizer/proof.f.d.ts +0 -0
  379. /package/{fs → fjs}/media/json/tokenizer/proof.f.js +0 -0
  380. /package/{fs → fjs}/media/proof.f.d.ts +0 -0
  381. /package/{fs → fjs}/media/type/module.f.d.ts +0 -0
  382. /package/{fs → fjs}/media/type/proof.f.d.ts +0 -0
  383. /package/{fs → fjs}/module.d.ts +0 -0
  384. /package/{fs → fjs}/module.f.d.ts +0 -0
  385. /package/{fs → fjs}/module.js +0 -0
  386. /package/{fs → fjs}/path/module.f.d.ts +0 -0
  387. /package/{fs → fjs}/path/module.f.js +0 -0
  388. /package/{fs → fjs}/path/proof.f.d.ts +0 -0
  389. /package/{fs → fjs}/proof.f.d.ts +0 -0
  390. /package/{fs → fjs}/sul/id/module.f.d.ts +0 -0
  391. /package/{fs → fjs}/sul/id/module.f.js +0 -0
  392. /package/{fs → fjs}/sul/id/proof.f.d.ts +0 -0
  393. /package/{fs → fjs}/sul/level/hash/module.f.d.ts +0 -0
  394. /package/{fs → fjs}/sul/level/hash/module.f.js +0 -0
  395. /package/{fs → fjs}/sul/level/hash/proof.f.d.ts +0 -0
  396. /package/{fs → fjs}/sul/level/literal/module.f.d.ts +0 -0
  397. /package/{fs → fjs}/sul/level/literal/module.f.js +0 -0
  398. /package/{fs → fjs}/sul/level/literal/proof.f.d.ts +0 -0
  399. /package/{fs → fjs}/sul/module.f.d.ts +0 -0
  400. /package/{fs → fjs}/sul/module.f.js +0 -0
  401. /package/{fs → fjs}/sul/proof.f.d.ts +0 -0
  402. /package/{fs → fjs}/sul/proof.f.js +0 -0
  403. /package/{fs → fjs}/text/ascii/module.f.d.ts +0 -0
  404. /package/{fs → fjs}/text/ascii/module.f.js +0 -0
  405. /package/{fs → fjs}/text/code_point/module.f.d.ts +0 -0
  406. /package/{fs → fjs}/text/code_point/proof.f.d.ts +0 -0
  407. /package/{fs → fjs}/text/code_point/proof.f.js +0 -0
  408. /package/{fs → fjs}/text/module.f.d.ts +0 -0
  409. /package/{fs → fjs}/text/module.f.js +0 -0
  410. /package/{fs → fjs}/text/proof.f.d.ts +0 -0
  411. /package/{fs → fjs}/text/sgr/module.f.d.ts +0 -0
  412. /package/{fs → fjs}/text/sgr/module.f.js +0 -0
  413. /package/{fs → fjs}/text/sgr/proof.f.d.ts +0 -0
  414. /package/{fs → fjs}/text/utf16/module.f.d.ts +0 -0
  415. /package/{fs → fjs}/text/utf16/proof.f.d.ts +0 -0
  416. /package/{fs → fjs}/text/utf8/module.f.d.ts +0 -0
  417. /package/{fs → fjs}/text/utf8/module.f.js +0 -0
  418. /package/{fs → fjs}/text/utf8/proof.f.d.ts +0 -0
  419. /package/{fs → fjs}/types/array/module.f.d.ts +0 -0
  420. /package/{fs → fjs}/types/array/module.f.js +0 -0
  421. /package/{fs → fjs}/types/array/proof.f.d.ts +0 -0
  422. /package/{fs → fjs}/types/bigfloat/module.f.d.ts +0 -0
  423. /package/{fs → fjs}/types/bigfloat/module.f.js +0 -0
  424. /package/{fs → fjs}/types/bigfloat/proof.f.d.ts +0 -0
  425. /package/{fs → fjs}/types/bigint/module.f.d.ts +0 -0
  426. /package/{fs → fjs}/types/bigint/proof.f.d.ts +0 -0
  427. /package/{fs → fjs}/types/bit_vec/module.f.d.ts +0 -0
  428. /package/{fs → fjs}/types/bit_vec/proof.f.d.ts +0 -0
  429. /package/{fs → fjs}/types/btree/find/module.f.d.ts +0 -0
  430. /package/{fs → fjs}/types/btree/find/module.f.js +0 -0
  431. /package/{fs → fjs}/types/btree/find/proof.f.d.ts +0 -0
  432. /package/{fs → fjs}/types/btree/module.f.d.ts +0 -0
  433. /package/{fs → fjs}/types/btree/module.f.js +0 -0
  434. /package/{fs → fjs}/types/btree/proof.f.d.ts +0 -0
  435. /package/{fs → fjs}/types/btree/set/module.f.d.ts +0 -0
  436. /package/{fs → fjs}/types/btree/set/proof.f.d.ts +0 -0
  437. /package/{fs → fjs}/types/btree/types/module.f.d.ts +0 -0
  438. /package/{fs → fjs}/types/btree/types/module.f.js +0 -0
  439. /package/{fs → fjs}/types/byte_set/module.f.d.ts +0 -0
  440. /package/{fs → fjs}/types/byte_set/module.f.js +0 -0
  441. /package/{fs → fjs}/types/byte_set/proof.f.d.ts +0 -0
  442. /package/{fs → fjs}/types/function/compare/module.f.d.ts +0 -0
  443. /package/{fs → fjs}/types/function/compare/module.f.js +0 -0
  444. /package/{fs → fjs}/types/function/compare/proof.f.d.ts +0 -0
  445. /package/{fs → fjs}/types/function/module.f.d.ts +0 -0
  446. /package/{fs → fjs}/types/function/module.f.js +0 -0
  447. /package/{fs → fjs}/types/function/operator/module.f.d.ts +0 -0
  448. /package/{fs → fjs}/types/function/operator/module.f.js +0 -0
  449. /package/{fs → fjs}/types/function/operator/proof.f.d.ts +0 -0
  450. /package/{fs → fjs}/types/function/proof.f.d.ts +0 -0
  451. /package/{fs → fjs}/types/map/module.f.d.ts +0 -0
  452. /package/{fs → fjs}/types/map/module.f.js +0 -0
  453. /package/{fs → fjs}/types/map/proof.f.d.ts +0 -0
  454. /package/{fs → fjs}/types/nibble_set/module.f.d.ts +0 -0
  455. /package/{fs → fjs}/types/nibble_set/module.f.js +0 -0
  456. /package/{fs → fjs}/types/nibble_set/proof.f.d.ts +0 -0
  457. /package/{fs → fjs}/types/nominal/module.f.d.ts +0 -0
  458. /package/{fs → fjs}/types/nominal/module.f.js +0 -0
  459. /package/{fs → fjs}/types/nominal/proof.f.d.ts +0 -0
  460. /package/{fs → fjs}/types/nullable/module.f.d.ts +0 -0
  461. /package/{fs → fjs}/types/nullable/module.f.js +0 -0
  462. /package/{fs → fjs}/types/nullable/proof.f.d.ts +0 -0
  463. /package/{fs → fjs}/types/number/module.f.d.ts +0 -0
  464. /package/{fs → fjs}/types/number/proof.f.d.ts +0 -0
  465. /package/{fs → fjs}/types/object/module.f.d.ts +0 -0
  466. /package/{fs → fjs}/types/object/module.f.js +0 -0
  467. /package/{fs → fjs}/types/object/proof.f.d.ts +0 -0
  468. /package/{fs → fjs}/types/option/module.f.d.ts +0 -0
  469. /package/{fs → fjs}/types/option/module.f.js +0 -0
  470. /package/{fs → fjs}/types/ordered_map/module.f.d.ts +0 -0
  471. /package/{fs → fjs}/types/ordered_map/module.f.js +0 -0
  472. /package/{fs → fjs}/types/ordered_map/proof.f.d.ts +0 -0
  473. /package/{fs → fjs}/types/ordered_map/proof.f.js +0 -0
  474. /package/{fs → fjs}/types/patricia_trie/module.f.d.ts +0 -0
  475. /package/{fs → fjs}/types/patricia_trie/module.f.js +0 -0
  476. /package/{fs → fjs}/types/patricia_trie/proof.f.d.ts +0 -0
  477. /package/{fs → fjs}/types/phantom/module.f.d.ts +0 -0
  478. /package/{fs → fjs}/types/phantom/module.f.js +0 -0
  479. /package/{fs → fjs}/types/prime_field/module.f.d.ts +0 -0
  480. /package/{fs → fjs}/types/prime_field/proof.f.d.ts +0 -0
  481. /package/{fs → fjs}/types/prime_field/proof.f.js +0 -0
  482. /package/{fs → fjs}/types/range/proof.f.d.ts +0 -0
  483. /package/{fs → fjs}/types/range_map/module.f.d.ts +0 -0
  484. /package/{fs → fjs}/types/range_map/module.f.js +0 -0
  485. /package/{fs → fjs}/types/range_map/proof.f.d.ts +0 -0
  486. /package/{fs → fjs}/types/result/module.d.ts +0 -0
  487. /package/{fs → fjs}/types/result/module.f.d.ts +0 -0
  488. /package/{fs → fjs}/types/result/module.f.js +0 -0
  489. /package/{fs → fjs}/types/result/module.js +0 -0
  490. /package/{fs → fjs}/types/rtti/module.f.d.ts +0 -0
  491. /package/{fs → fjs}/types/rtti/module.f.js +0 -0
  492. /package/{fs → fjs}/types/rtti/parse/module.f.d.ts +0 -0
  493. /package/{fs → fjs}/types/rtti/parse/proof.f.d.ts +0 -0
  494. /package/{fs → fjs}/types/rtti/proof.f.d.ts +0 -0
  495. /package/{fs → fjs}/types/rtti/proof.f.js +0 -0
  496. /package/{fs → fjs}/types/rtti/ts/module.f.js +0 -0
  497. /package/{fs → fjs}/types/rtti/ts/proof.f.d.ts +0 -0
  498. /package/{fs → fjs}/types/rtti/ts/proof.f.js +0 -0
  499. /package/{fs → fjs}/types/rtti/validate/module.f.d.ts +0 -0
  500. /package/{fs → fjs}/types/rtti/validate/proof.f.d.ts +0 -0
  501. /package/{fs → fjs}/types/sorted_list/module.f.d.ts +0 -0
  502. /package/{fs → fjs}/types/sorted_list/module.f.js +0 -0
  503. /package/{fs → fjs}/types/sorted_list/proof.f.d.ts +0 -0
  504. /package/{fs → fjs}/types/sorted_set/module.f.d.ts +0 -0
  505. /package/{fs → fjs}/types/sorted_set/module.f.js +0 -0
  506. /package/{fs → fjs}/types/sorted_set/proof.f.d.ts +0 -0
  507. /package/{fs → fjs}/types/string/module.f.d.ts +0 -0
  508. /package/{fs → fjs}/types/string/proof.f.d.ts +0 -0
  509. /package/{fs → fjs}/types/string_set/module.f.d.ts +0 -0
  510. /package/{fs → fjs}/types/string_set/module.f.js +0 -0
  511. /package/{fs → fjs}/types/string_set/proof.f.d.ts +0 -0
  512. /package/{fs → fjs}/types/ts/module.f.d.ts +0 -0
  513. /package/{fs → fjs}/types/ts/module.f.js +0 -0
  514. /package/{fs → fjs}/types/ts/proof.f.d.ts +0 -0
  515. /package/{fs → fjs}/types/uint8array/module.f.d.ts +0 -0
  516. /package/{fs → fjs}/website/module.f.d.ts +0 -0
  517. /package/{fs → fjs}/website/proof.f.d.ts +0 -0
@@ -0,0 +1,572 @@
1
+ import { assert, assertEq } from '../../asserts/module.f.js';
2
+ import { pure } from '../../effects/module.f.js';
3
+ import { fileCas } from '../module.f.js';
4
+ import { sha256 } from '../../crypto/sha2/module.f.js';
5
+ import { emptyState, virtual } from '../../effects/node/virtual/module.f.js';
6
+ import { vec, vec8 } from '../../types/bit_vec/module.f.js';
7
+ import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.js';
8
+ import { unwrap } from '../../types/nullable/module.f.js';
9
+ import { ok, error } from '../../types/result/module.f.js';
10
+ import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.js';
11
+ import { tryUtf8 } from '../../text/module.f.js';
12
+ import { dialect as revisionDialect } from '../../media/revision/module.f.js';
13
+ import { buildCache, decodeRevisionBlob, initEvo, evo, emptyCache, syncRevision, } from './module.f.js';
14
+ const home = '.';
15
+ // A `Cas<never>` whose `write` always fails — used to reach `addRevision`'s
16
+ // store-write-failure branch, which real `fileCas` has no easy way to trigger.
17
+ const writeFailingCas = {
18
+ read: () => elEmpty(),
19
+ write: () => pure(error('boom')),
20
+ list: () => pure([]),
21
+ };
22
+ // A `Cas<never>` whose `read` yields an error item that is *not* a missing
23
+ // shard — what a permission error, a mid-stream I/O failure, or a blob too
24
+ // large for `collectRead` to buffer looks like to a caller.
25
+ const readFailingCas = {
26
+ read: () => nonEmpty(error('boom'), elEmpty()),
27
+ write: () => pure(error('write not supported')),
28
+ list: () => pure([]),
29
+ };
30
+ // A `Cas<never>` backed by a fixed set of (hash, content) entries, returned
31
+ // from `list()` in exactly the given order — used to control the order
32
+ // `buildCache` sees hashes in, independent of any real filesystem's
33
+ // (hash-lexical, not causal) directory-listing order.
34
+ const fixedCas = (entries) => ({
35
+ read: hash => {
36
+ const found = entries.find(([h]) => vecToCBase32(h) === vecToCBase32(hash));
37
+ return found === undefined
38
+ ? nonEmpty(error('not found'), elEmpty())
39
+ : nonEmpty(ok(found[1]), elEmpty());
40
+ },
41
+ write: () => pure(error('write not supported')),
42
+ list: () => pure(entries.map(([h]) => h)),
43
+ });
44
+ export const proof = {
45
+ buildCacheEmptyStoreYieldsEmptyCache: () => {
46
+ const c = fileCas(sha256)(home);
47
+ const [, cache] = virtual(emptyState)(buildCache(c));
48
+ assertEq(JSON.stringify(cache), JSON.stringify(emptyCache));
49
+ },
50
+ buildCacheSkipsNonRevisionBlob: () => {
51
+ const c = fileCas(sha256)(home);
52
+ const content = vec8(0x41n); // 'A' — valid UTF-8, not revision JSON
53
+ const [state1] = virtual(emptyState)(c.write(nonEmpty(ok(content), elEmpty())));
54
+ const [, cache] = virtual(state1)(buildCache(c));
55
+ assertEq(Object.keys(cache.bySubject).length, 0);
56
+ },
57
+ decodeRevisionBlobMissingHashIsNull: () => {
58
+ const c = fileCas(sha256)(home);
59
+ const [, revision] = virtual(emptyState)(decodeRevisionBlob(c)(vec8(0x99n)));
60
+ assertEq(revision, null);
61
+ },
62
+ decodeRevisionBlobNonUtf8IsNull: () => {
63
+ const c = fileCas(sha256)(home);
64
+ const oddVec = vec(5n)(21n); // not a whole number of bytes
65
+ const [state1, w] = virtual(emptyState)(c.write(nonEmpty(ok(oddVec), elEmpty())));
66
+ assert(w[0] === 'ok', ['expected write ok', w]);
67
+ const [, revision] = virtual(state1)(decodeRevisionBlob(c)(w[1]));
68
+ assertEq(revision, null);
69
+ },
70
+ decodeRevisionBlobInvalidJsonIsNull: () => {
71
+ const c = fileCas(sha256)(home);
72
+ const content = vec8(0x7bn); // '{' alone: valid UTF-8, not parseable JSON
73
+ const [state1, w] = virtual(emptyState)(c.write(nonEmpty(ok(content), elEmpty())));
74
+ assert(w[0] === 'ok', ['expected write ok', w]);
75
+ const [, revision] = virtual(state1)(decodeRevisionBlob(c)(w[1]));
76
+ assertEq(revision, null);
77
+ },
78
+ decodeRevisionBlobValidRevisionRoundTrips: () => {
79
+ const c = fileCas(sha256)(home);
80
+ const subjectHash = vecToCBase32(vec8(0x11n));
81
+ const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`;
82
+ const bytes = tryUtf8(text);
83
+ assert(bytes !== null, 'expected the sample revision text to encode as UTF-8');
84
+ const [state1, w] = virtual(emptyState)(c.write(nonEmpty(ok(bytes), elEmpty())));
85
+ assert(w[0] === 'ok', ['expected write ok', w]);
86
+ const [, revision] = virtual(state1)(decodeRevisionBlob(c)(w[1]));
87
+ assert(revision !== null, 'expected a decoded revision');
88
+ assertEq(revision?.subject, subjectHash);
89
+ },
90
+ buildCacheIncludesScannedRevision: () => {
91
+ // Unlike the `add`-driven tests below (which fold one revision at a
92
+ // time), this seeds a `vnd.fjs.revision` blob directly into the store
93
+ // and scans it with `buildCache`, covering the "found a revision"
94
+ // branch of the full-store fold.
95
+ const c = fileCas(sha256)(home);
96
+ const subjectHash = vecToCBase32(vec8(0x12n));
97
+ const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`;
98
+ const bytes = tryUtf8(text);
99
+ assert(bytes !== null, 'expected the sample revision text to encode as UTF-8');
100
+ const [state1, w] = virtual(emptyState)(fileCas(sha256)(home).write(nonEmpty(ok(bytes), elEmpty())));
101
+ assert(w[0] === 'ok', ['expected write ok', w]);
102
+ const [, cache] = virtual(state1)(buildCache(c));
103
+ assertEq(cache.bySubject[subjectHash]?.hashes.length, 1);
104
+ assertEq(cache.bySubject[subjectHash]?.hashes[0], vecToCBase32(w[1]));
105
+ },
106
+ // Regression: `cas.list()` enumerates a real store by hash-sharded path,
107
+ // not revision ancestry, so a child's hash can sort (and therefore be
108
+ // scanned) before its parent's. `buildCache` must still end up with only
109
+ // the child as a head — the whole point of tracking `hashes`/`parents`
110
+ // as separate sets (see the module doc) rather than an incremental head
111
+ // list.
112
+ buildCacheOrderIndependentWhenChildScannedBeforeParent: () => {
113
+ const rootHash = vec8(0xaan);
114
+ const childHash = vec8(0xbbn);
115
+ const rootCBase32 = vecToCBase32(rootHash);
116
+ const childCBase32 = vecToCBase32(childHash);
117
+ const snapshotHash = vecToCBase32(vec8(0xccn));
118
+ const rootText = `{"dialect":"${revisionDialect}","subject":"doc","parents":[],"snapshot":"${snapshotHash}","generation":0}`;
119
+ const childText = `{"dialect":"${revisionDialect}","subject":"doc","parents":["${rootCBase32}"],"snapshot":"${snapshotHash}","generation":1}`;
120
+ const rootBytes = tryUtf8(rootText);
121
+ const childBytes = tryUtf8(childText);
122
+ assert(rootBytes !== null && childBytes !== null, 'expected sample revisions to encode as UTF-8');
123
+ const cas = fixedCas([[childHash, childBytes], [rootHash, rootBytes]]);
124
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(cas));
125
+ const [, heads] = virtual(state0)(evo(cas)(cacheKey).head('doc'));
126
+ assertEq(heads.length, 1);
127
+ assertEq(heads[0], childCBase32);
128
+ },
129
+ // Regression: `cBase32ToVec` accepts more than one spelling of the same
130
+ // hash (case, and the `i`/`l`/`o` alphabet aliases). A parent reference
131
+ // recorded in a non-canonical spelling must still demote the parent from
132
+ // the head set — `headsOf` compares hash strings directly, so caching
133
+ // the parent verbatim (instead of its canonical `vecToCBase32` spelling)
134
+ // would leave the parent listed as a head alongside its child.
135
+ buildCacheCanonicalizesNonCanonicalParentHashes: () => {
136
+ const rootHash = vec8(0xffn);
137
+ const rootCanonical = vecToCBase32(rootHash); // "zy" — has letters to uppercase
138
+ const rootNonCanonical = rootCanonical.toUpperCase();
139
+ assert(rootNonCanonical !== rootCanonical, 'expected the sample hash to contain letters');
140
+ const childHash = vec8(0xeen);
141
+ const snapshotHash = vecToCBase32(vec8(0xddn));
142
+ const rootText = `{"dialect":"${revisionDialect}","subject":"doc","parents":[],"snapshot":"${snapshotHash}","generation":0}`;
143
+ const childText = `{"dialect":"${revisionDialect}","subject":"doc","parents":["${rootNonCanonical}"],"snapshot":"${snapshotHash}","generation":1}`;
144
+ const rootBytes = tryUtf8(rootText);
145
+ const childBytes = tryUtf8(childText);
146
+ assert(rootBytes !== null && childBytes !== null, 'expected sample revisions to encode as UTF-8');
147
+ const cas = fixedCas([[rootHash, rootBytes], [childHash, childBytes]]);
148
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(cas));
149
+ const [, heads] = virtual(state0)(evo(cas)(cacheKey).head('doc'));
150
+ assertEq(heads.length, 1);
151
+ assertEq(heads[0], vecToCBase32(childHash));
152
+ },
153
+ // Two concurrent children of one root: both become heads, the root stops
154
+ // being one. Exercises addRevisionToCache's fold across a first insert
155
+ // (no prior heads for the subject), a removal (a parent leaving the head
156
+ // set), and a keep (an unrelated existing head surviving the fold).
157
+ addRevisionBuildsHeadsAcrossChainAndFork: () => {
158
+ const c = fileCas(sha256)(home);
159
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
160
+ const e = evo(c)(cacheKey);
161
+ const snapshotHash = vecToCBase32(vec8(0x22n));
162
+ const rev0 = { parents: [], subject: 'doc', snapshot: snapshotHash };
163
+ const [state1, rev0Result] = virtual(state0)(e.add(rev0));
164
+ assert(rev0Result[0] === 'ok', ['expected rev0 ok', rev0Result]);
165
+ const rev0Hash = rev0Result[1];
166
+ // rev1 inherits rev0's snapshot; rev2 states a different one — so the
167
+ // two concurrent children are genuinely distinct blobs (both children
168
+ // of rev0, same generation), not one deduplicated revision.
169
+ const rev1 = { parents: [rev0Hash], subject: 'doc' };
170
+ const [state2, rev1Result] = virtual(state1)(e.add(rev1));
171
+ assert(rev1Result[0] === 'ok', ['expected rev1 ok', rev1Result]);
172
+ const rev1Hash = rev1Result[1];
173
+ const rev2 = { parents: [rev0Hash], subject: 'doc', snapshot: vecToCBase32(vec8(0x23n)) };
174
+ const [state3, rev2Result] = virtual(state2)(e.add(rev2));
175
+ assert(rev2Result[0] === 'ok', ['expected rev2 ok', rev2Result]);
176
+ const rev2Hash = rev2Result[1];
177
+ const [state4, heads] = virtual(state3)(e.head('doc'));
178
+ assertEq(heads.length, 2);
179
+ assert(heads.includes(rev1Hash), ['expected rev1 to remain a head', heads]);
180
+ assert(heads.includes(rev2Hash), ['expected rev2 to be a head', heads]);
181
+ assert(!heads.includes(rev0Hash), ['expected rev0 to no longer be a head', heads]);
182
+ const [, subjects] = virtual(state4)(e.list());
183
+ assertEq(subjects.length, 1);
184
+ assertEq(subjects[0], 'doc');
185
+ },
186
+ // Adding the exact same revision twice yields the same (deduplicated)
187
+ // content hash and must not duplicate the head entry.
188
+ addRevisionIdempotentOnDuplicateContent: () => {
189
+ const c = fileCas(sha256)(home);
190
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
191
+ const e = evo(c)(cacheKey);
192
+ const input = { parents: [], subject: 'x', snapshot: vecToCBase32(vec8(0x33n)) };
193
+ const [state1, r1] = virtual(state0)(e.add(input));
194
+ assert(r1[0] === 'ok', ['expected first add ok', r1]);
195
+ const [state2, r2] = virtual(state1)(e.add(input));
196
+ assert(r2[0] === 'ok', ['expected second add ok', r2]);
197
+ assertEq(r1[1], r2[1]);
198
+ const [, heads] = virtual(state2)(e.head('x'));
199
+ assertEq(heads.length, 1);
200
+ },
201
+ // Two `add` calls describing the same logical revision but spelling the
202
+ // parent reference differently (canonical vs. an accepted uppercase
203
+ // alias) must serialize identically and dedupe to one head — otherwise
204
+ // `addRevision` itself would be the source of the very spelling drift
205
+ // `canonicalHash` exists to prevent.
206
+ addRevisionCanonicalizesParentSpellingBeforeSerializing: () => {
207
+ const c = fileCas(sha256)(home);
208
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
209
+ const e = evo(c)(cacheKey);
210
+ const root = { parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x2en)) };
211
+ const [state1, rootResult] = virtual(state0)(e.add(root));
212
+ assert(rootResult[0] === 'ok', ['expected root ok', rootResult]);
213
+ const rootHash = rootResult[1];
214
+ assert(rootHash !== rootHash.toUpperCase(), 'expected the sample hash to contain letters');
215
+ const child1 = { parents: [rootHash], subject: 'doc' };
216
+ const [state2, child1Result] = virtual(state1)(e.add(child1));
217
+ assert(child1Result[0] === 'ok', ['expected child1 ok', child1Result]);
218
+ const child2 = { parents: [rootHash.toUpperCase()], subject: 'doc' };
219
+ const [state3, child2Result] = virtual(state2)(e.add(child2));
220
+ assert(child2Result[0] === 'ok', ['expected child2 ok', child2Result]);
221
+ assertEq(child1Result[1], child2Result[1]);
222
+ const [, heads] = virtual(state3)(e.head('doc'));
223
+ assertEq(heads.length, 1);
224
+ assertEq(heads[0], child1Result[1]);
225
+ },
226
+ // `subject` omitted with exactly one parent is inherited from that
227
+ // parent's own `subject`.
228
+ addRevisionResolvesSubjectFromSingleParent: () => {
229
+ const c = fileCas(sha256)(home);
230
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
231
+ const e = evo(c)(cacheKey);
232
+ const root = { parents: [], subject: 'inherit-me', snapshot: vecToCBase32(vec8(0x44n)) };
233
+ const [state1, rootResult] = virtual(state0)(e.add(root));
234
+ assert(rootResult[0] === 'ok', ['expected root ok', rootResult]);
235
+ const rootHash = rootResult[1];
236
+ const child = { parents: [rootHash] };
237
+ const [state2, childResult] = virtual(state1)(e.add(child));
238
+ assert(childResult[0] === 'ok', ['expected child ok', childResult]);
239
+ const childHash = childResult[1];
240
+ const [, heads] = virtual(state2)(e.head('inherit-me'));
241
+ assertEq(heads.length, 1);
242
+ assertEq(heads[0], childHash);
243
+ },
244
+ // `add` writes both inference-derived fields explicitly: a root gets
245
+ // `generation` 0 and, with no input `snapshot`, its `subject` (a hash) as
246
+ // the snapshot reference; a single-parent child with no input `snapshot`
247
+ // inherits the parent's stored snapshot and gets `generation` 1.
248
+ addComputesGenerationAndResolvesSnapshot: () => {
249
+ const c = fileCas(sha256)(home);
250
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
251
+ const e = evo(c)(cacheKey);
252
+ const subjectHash = vecToCBase32(vec8(0x71n));
253
+ const [state1, rootResult] = virtual(state0)(e.add({ parents: [], subject: subjectHash }));
254
+ assert(rootResult[0] === 'ok', ['expected root ok', rootResult]);
255
+ const rootHashVec = unwrap(cBase32ToVec(rootResult[1]));
256
+ const [state2, root] = virtual(state1)(decodeRevisionBlob(c)(rootHashVec));
257
+ assert(root !== null, 'expected the stored root to decode');
258
+ assertEq(root?.generation, 0);
259
+ assertEq(root?.snapshot, subjectHash);
260
+ const [state3, childResult] = virtual(state2)(e.add({ parents: [rootResult[1]], subject: subjectHash }));
261
+ assert(childResult[0] === 'ok', ['expected child ok', childResult]);
262
+ const childHashVec = unwrap(cBase32ToVec(childResult[1]));
263
+ const [, child] = virtual(state3)(decodeRevisionBlob(c)(childHashVec));
264
+ assert(child !== null, 'expected the stored child to decode');
265
+ assertEq(child?.generation, 1);
266
+ assertEq(child?.snapshot, subjectHash);
267
+ },
268
+ // A merge takes `1 + max(parents' generations)`: a parent at generation 2
269
+ // and one at generation 1 yield generation 3.
270
+ addComputesMergeGenerationFromMaxOfParents: () => {
271
+ const c = fileCas(sha256)(home);
272
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
273
+ const e = evo(c)(cacheKey);
274
+ const snap = vecToCBase32(vec8(0x72n));
275
+ // Mainline chain: root(gen0) → a(gen1) → b(gen2).
276
+ const [state1, root] = virtual(state0)(e.add({ parents: [], subject: 'm', snapshot: snap }));
277
+ assert(root[0] === 'ok', ['expected root ok', root]);
278
+ const [state2, a] = virtual(state1)(e.add({ parents: [root[1]], subject: 'm', snapshot: snap }));
279
+ assert(a[0] === 'ok', ['expected a ok', a]);
280
+ const [state3, b] = virtual(state2)(e.add({ parents: [a[1]], subject: 'm', snapshot: snap }));
281
+ assert(b[0] === 'ok', ['expected b ok', b]);
282
+ // Side branch off root: c(gen1).
283
+ const [state4, cRev] = virtual(state3)(e.add({ parents: [root[1]], subject: 'm', snapshot: snap }));
284
+ assert(cRev[0] === 'ok', ['expected c ok', cRev]);
285
+ // Merge of b(gen2) and c(gen1) → gen3.
286
+ const [state5, merge] = virtual(state4)(e.add({ parents: [b[1], cRev[1]], subject: 'm', snapshot: snap }));
287
+ assert(merge[0] === 'ok', ['expected merge ok', merge]);
288
+ const mergeHashVec = unwrap(cBase32ToVec(merge[1]));
289
+ const [, mergeRev] = virtual(state5)(decodeRevisionBlob(c)(mergeHashVec));
290
+ assert(mergeRev !== null, 'expected the stored merge to decode');
291
+ assertEq(mergeRev?.generation, 3);
292
+ },
293
+ // `generation` is an input field only so a value read back by
294
+ // `revision` round-trips into `add` unchanged: `add` never reads it, and
295
+ // supplying a wrong one must not reach the stored blob — the revision is
296
+ // byte-identical to the same `add` without it.
297
+ addIgnoresSuppliedGeneration: () => {
298
+ const c = fileCas(sha256)(home);
299
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
300
+ const e = evo(c)(cacheKey);
301
+ const subjectHash = vecToCBase32(vec8(0x73n));
302
+ const [state1, claimed] = virtual(state0)(e.add({ parents: [], subject: subjectHash, generation: 42 }));
303
+ assert(claimed[0] === 'ok', ['expected add ok', claimed]);
304
+ const [state2, plain] = virtual(state1)(e.add({ parents: [], subject: subjectHash }));
305
+ assert(plain[0] === 'ok', ['expected add ok', plain]);
306
+ assertEq(claimed[1], plain[1]);
307
+ const [, revision] = virtual(state2)(e.revision(claimed[1]));
308
+ assert(revision[0] === 'ok', ['expected revision ok', revision]);
309
+ assertEq(revision[1].generation, 0);
310
+ },
311
+ addRevisionSubjectRequiredForZeroParents: () => {
312
+ const c = fileCas(sha256)(home);
313
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
314
+ const e = evo(c)(cacheKey);
315
+ const [, result] = virtual(state0)(e.add({ parents: [] }));
316
+ assertEq(result[0], 'error');
317
+ assert(result[0] === 'error' && result[1].includes('subject is required'), ['unexpected message', result]);
318
+ },
319
+ // Zero parents and no input `snapshot`: `resolveSnapshot` falls back to
320
+ // `subject` as the reference, which must itself be a hash. A subject with
321
+ // characters outside the cbase32 alphabet ('not-a-hash!') therefore has
322
+ // nothing to resolve the snapshot to and is an error — the format requires
323
+ // an explicit `snapshot`.
324
+ addRevisionNonHashSubjectWithoutSnapshotIsError: () => {
325
+ const c = fileCas(sha256)(home);
326
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
327
+ const e = evo(c)(cacheKey);
328
+ const [, result] = virtual(state0)(e.add({ parents: [], subject: 'not-a-hash!' }));
329
+ assertEq(result[0], 'error');
330
+ assert(result[0] === 'error' && result[1].includes('subject must be a valid hash'), ['unexpected message', result]);
331
+ },
332
+ addRevisionInvalidParentHashIsError: () => {
333
+ const c = fileCas(sha256)(home);
334
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
335
+ const e = evo(c)(cacheKey);
336
+ const [, result] = virtual(state0)(e.add({ parents: ['not a valid cbase32!'] }));
337
+ assertEq(result[0], 'error');
338
+ assert(result[0] === 'error' && result[1].includes('invalid parent hash'), ['unexpected message', result]);
339
+ },
340
+ addRevisionParentNotARevisionIsError: () => {
341
+ const c = fileCas(sha256)(home);
342
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
343
+ const e = evo(c)(cacheKey);
344
+ const missingParent = vecToCBase32(vec8(0x55n));
345
+ const [, result] = virtual(state0)(e.add({ parents: [missingParent] }));
346
+ assertEq(result[0], 'error');
347
+ assert(result[0] === 'error' && result[1].includes('parent is not a revision blob'), ['unexpected message', result]);
348
+ },
349
+ // An explicit `subject` must not bypass parent validation: a missing
350
+ // parent is still an error even though `subject` alone would otherwise
351
+ // resolve without looking at any parent.
352
+ addRevisionValidatesParentsEvenWithExplicitSubject: () => {
353
+ const c = fileCas(sha256)(home);
354
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
355
+ const e = evo(c)(cacheKey);
356
+ const missingParent = vecToCBase32(vec8(0x66n));
357
+ const [, result] = virtual(state0)(e.add({ parents: [missingParent], subject: 'doc' }));
358
+ assertEq(result[0], 'error');
359
+ assert(result[0] === 'error' && result[1].includes('parent is not a revision blob'), ['unexpected message', result]);
360
+ },
361
+ // A revision models one step in the evolution of a single subject: a
362
+ // parent that actually belongs to a *different* subject must be
363
+ // rejected, even though it exists and is a valid revision — otherwise
364
+ // the new revision would silently graft onto an unrelated object's
365
+ // history, and head demotion (scoped to the child's own subject) would
366
+ // never remove the parent from its real subject's head set.
367
+ addRevisionRejectsCrossSubjectParent: () => {
368
+ const c = fileCas(sha256)(home);
369
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
370
+ const e = evo(c)(cacheKey);
371
+ const rootA = { parents: [], subject: 'A', snapshot: vecToCBase32(vec8(0x67n)) };
372
+ const [state1, rootAResult] = virtual(state0)(e.add(rootA));
373
+ assert(rootAResult[0] === 'ok', ['expected rootA ok', rootAResult]);
374
+ const [, result] = virtual(state1)(e.add({ parents: [rootAResult[1]], subject: 'B' }));
375
+ assertEq(result[0], 'error');
376
+ assert(result[0] === 'error' && result[1].includes('different subject'), ['unexpected message', result]);
377
+ },
378
+ // With multiple parents, a failure on an earlier one short-circuits the
379
+ // fold — the second (here, a well-formed but nonexistent) parent is
380
+ // never even looked up.
381
+ addRevisionShortCircuitsOnFirstInvalidParentAmongMultiple: () => {
382
+ const c = fileCas(sha256)(home);
383
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
384
+ const e = evo(c)(cacheKey);
385
+ const missingParent = vecToCBase32(vec8(0x99n));
386
+ const [, result] = virtual(state0)(e.add({ parents: ['not a valid cbase32!', missingParent], subject: 'doc' }));
387
+ assertEq(result[0], 'error');
388
+ assert(result[0] === 'error' && result[1].includes('invalid parent hash'), ['unexpected message', result]);
389
+ },
390
+ // Two *valid* parents without an explicit `snapshot` cannot resolve one
391
+ // (no single parent snapshot to inherit) — a distinct failure from a
392
+ // missing/invalid parent, reported by `resolveSnapshot` at the write
393
+ // boundary now that the format requires an explicit `snapshot`.
394
+ addRevisionInvalidReferencesIsError: () => {
395
+ const c = fileCas(sha256)(home);
396
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
397
+ const e = evo(c)(cacheKey);
398
+ const rootA = { parents: [], subject: 'merge', snapshot: vecToCBase32(vec8(0x77n)) };
399
+ const [state1, rootAResult] = virtual(state0)(e.add(rootA));
400
+ assert(rootAResult[0] === 'ok', ['expected rootA ok', rootAResult]);
401
+ const rootB = { parents: [], subject: 'merge', snapshot: vecToCBase32(vec8(0x88n)) };
402
+ const [state2, rootBResult] = virtual(state1)(e.add(rootB));
403
+ assert(rootBResult[0] === 'ok', ['expected rootB ok', rootBResult]);
404
+ const [, result] = virtual(state2)(e.add({ parents: [rootAResult[1], rootBResult[1]], subject: 'merge' }));
405
+ assertEq(result[0], 'error');
406
+ assert(result[0] === 'error' && !result[1].includes('parent is not a revision blob'), ['unexpected message', result]);
407
+ },
408
+ addRevisionTooLargeToEncodeIsError: () => {
409
+ const c = fileCas(sha256)(home);
410
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
411
+ const e = evo(c)(cacheKey);
412
+ const hugeSubject = 'x'.repeat(200_000);
413
+ const input = { parents: [], subject: hugeSubject, snapshot: vecToCBase32(vec8(0x88n)) };
414
+ const [, result] = virtual(state0)(e.add(input));
415
+ assertEq(result[0], 'error');
416
+ assert(result[0] === 'error' && result[1] === 'revision too large to encode', ['unexpected message', result]);
417
+ },
418
+ addRevisionWriteFailureIsError: () => {
419
+ const [, cacheKey] = virtual(emptyState)(initEvo(writeFailingCas));
420
+ const e = evo(writeFailingCas)(cacheKey);
421
+ const [, result] = virtual(emptyState)(e.add({ parents: [], subject: vecToCBase32(vec8(0x99n)) }));
422
+ assertEq(result[0], 'error');
423
+ assert(result[0] === 'error' && result[1] === 'failed to write revision to CAS', ['unexpected message', result]);
424
+ },
425
+ // Every way `revision` can fail is its own message, not one `null`: an
426
+ // undecodable hash, a hash the store has nothing under, a read that failed
427
+ // for any other reason, and a blob that is not a revision.
428
+ // `decodeRevisionBlob` collapses all but the first (it exists to scan
429
+ // stores of arbitrary content); this read keeps them apart.
430
+ revisionInvalidHashIsError: () => {
431
+ const c = fileCas(sha256)(home);
432
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
433
+ const e = evo(c)(cacheKey);
434
+ const [, result] = virtual(state0)(e.revision('not a valid cbase32!'));
435
+ assertEq(result[0], 'error');
436
+ assert(result[0] === 'error' && result[1].includes('invalid hash'), ['unexpected message', result]);
437
+ },
438
+ revisionMissingHashIsError: () => {
439
+ const c = fileCas(sha256)(home);
440
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
441
+ const e = evo(c)(cacheKey);
442
+ const [, result] = virtual(state0)(e.revision(vecToCBase32(vec8(0x9an))));
443
+ assertEq(result[0], 'error');
444
+ assert(result[0] === 'error' && result[1].includes('revision not found'), ['unexpected message', result]);
445
+ },
446
+ // Regression: a read failure that is not a missing shard — a permission or
447
+ // mid-stream I/O error, or a blob too large to buffer into one `Vec` —
448
+ // must not be reported as "not found". The blob may well be there; saying
449
+ // it is absent would be a false answer, not merely a vague one.
450
+ revisionReadFailureIsNotReportedAsMissing: () => {
451
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(readFailingCas));
452
+ const e = evo(readFailingCas)(cacheKey);
453
+ const [, result] = virtual(state0)(e.revision(vecToCBase32(vec8(0x9bn))));
454
+ assertEq(result[0], 'error');
455
+ assert(result[0] === 'error' && result[1].includes('failed to read revision'), ['unexpected message', result]);
456
+ },
457
+ revisionNonRevisionBlobIsError: () => {
458
+ const c = fileCas(sha256)(home);
459
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
460
+ const e = evo(c)(cacheKey);
461
+ const content = vec8(0x41n); // 'A' — valid UTF-8, not revision JSON
462
+ const [state1, w] = virtual(state0)(c.write(nonEmpty(ok(content), elEmpty())));
463
+ assert(w[0] === 'ok', ['expected write ok', w]);
464
+ const [, result] = virtual(state1)(e.revision(vecToCBase32(w[1])));
465
+ assertEq(result[0], 'error');
466
+ assert(result[0] === 'error' && result[1].includes('not a revision blob'), ['unexpected message', result]);
467
+ },
468
+ // A blob written by somebody else can spell its references in any accepted
469
+ // cbase32 alias (case, `i`/`l`/`o`). What `revision` returns is
470
+ // canonicalized, so a client can compare it against `head` output
471
+ // directly instead of knowing about cbase32 aliasing.
472
+ revisionCanonicalizesReferenceSpellings: () => {
473
+ const c = fileCas(sha256)(home);
474
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
475
+ const e = evo(c)(cacheKey);
476
+ const parentCanonical = vecToCBase32(vec8(0xffn));
477
+ const snapshotCanonical = vecToCBase32(vec8(0xfen));
478
+ const parentAlias = parentCanonical.toUpperCase();
479
+ const snapshotAlias = snapshotCanonical.toUpperCase();
480
+ assert(parentAlias !== parentCanonical && snapshotAlias !== snapshotCanonical, 'expected the sample hashes to contain letters');
481
+ const text = `{"dialect":"${revisionDialect}","subject":"doc","parents":["${parentAlias}"],"snapshot":"${snapshotAlias}","generation":1}`;
482
+ const bytes = tryUtf8(text);
483
+ assert(bytes !== null, 'expected the sample revision text to encode as UTF-8');
484
+ const [state1, w] = virtual(state0)(c.write(nonEmpty(ok(bytes), elEmpty())));
485
+ assert(w[0] === 'ok', ['expected write ok', w]);
486
+ const [, result] = virtual(state1)(e.revision(vecToCBase32(w[1])));
487
+ assert(result[0] === 'ok', ['expected revision ok', result]);
488
+ assertEq(result[1].parents.length, 1);
489
+ assertEq(result[1].parents[0], parentCanonical);
490
+ assertEq(result[1].snapshot, snapshotCanonical);
491
+ assertEq(result[1].subject, 'doc');
492
+ assertEq(result[1].archived, undefined);
493
+ },
494
+ // What you add is what you get back: every field of a stored revision
495
+ // comes back in the same `RevisionData` shape `add` takes, so the value
496
+ // can be added again unchanged — same content, same hash.
497
+ revisionRoundTripsThroughAdd: () => {
498
+ const c = fileCas(sha256)(home);
499
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
500
+ const e = evo(c)(cacheKey);
501
+ const subjectHash = vecToCBase32(vec8(0x74n));
502
+ const [state1, root] = virtual(state0)(e.add({ parents: [], subject: subjectHash }));
503
+ assert(root[0] === 'ok', ['expected root ok', root]);
504
+ const [state2, child] = virtual(state1)(e.add({ parents: [root[1]], archived: true }));
505
+ assert(child[0] === 'ok', ['expected child ok', child]);
506
+ const [state3, result] = virtual(state2)(e.revision(child[1]));
507
+ assert(result[0] === 'ok', ['expected revision ok', result]);
508
+ assertEq(result[1].subject, subjectHash);
509
+ assertEq(result[1].snapshot, subjectHash);
510
+ assertEq(result[1].generation, 1);
511
+ assertEq(result[1].archived, true);
512
+ assertEq(result[1].parents.length, 1);
513
+ assertEq(result[1].parents[0], root[1]);
514
+ const [, readded] = virtual(state3)(e.add(result[1]));
515
+ assert(readded[0] === 'ok', ['expected re-add ok', readded]);
516
+ assertEq(readded[1], child[1]);
517
+ },
518
+ // A raw CAS write (e.g. `cas_add`) of valid revision content is folded
519
+ // into the cache exactly as `addRevision` would, without going through
520
+ // `evo.add` — this is what keeps `cas_add` and `evo_add` writes to the
521
+ // same store consistent (see `fjs/cas/mcp`).
522
+ syncRevisionFoldsValidRevisionIntoCache: () => {
523
+ const c = fileCas(sha256)(home);
524
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
525
+ const subjectHash = vecToCBase32(vec8(0x13n));
526
+ const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`;
527
+ const bytes = tryUtf8(text);
528
+ assert(bytes !== null, 'expected sample revision to encode as UTF-8');
529
+ const hashVec = vec8(0x14n);
530
+ const [state1] = virtual(state0)(syncRevision(cacheKey)(hashVec)(bytes));
531
+ const [, heads] = virtual(state1)(evo(c)(cacheKey).head(subjectHash));
532
+ assertEq(heads.length, 1);
533
+ assertEq(heads[0], vecToCBase32(hashVec));
534
+ },
535
+ syncRevisionIgnoresNonRevisionContent: () => {
536
+ const c = fileCas(sha256)(home);
537
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
538
+ const [state1] = virtual(state0)(syncRevision(cacheKey)(vec8(0x15n))(vec8(0x41n)));
539
+ const [, subjects] = virtual(state1)(evo(c)(cacheKey).list());
540
+ assertEq(subjects.length, 0);
541
+ },
542
+ evoHeadUnknownSubjectIsEmpty: () => {
543
+ const c = fileCas(sha256)(home);
544
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
545
+ const e = evo(c)(cacheKey);
546
+ const [, heads] = virtual(state0)(e.head('nope'));
547
+ assertEq(heads.length, 0);
548
+ },
549
+ // Regression: subjects are arbitrary caller-supplied strings, so one can
550
+ // collide with an inherited `Object.prototype` name. Plain bracket
551
+ // indexing on the `bySubject` map would return the inherited value
552
+ // (e.g. the `toString` function) instead of "no entry yet", crashing on
553
+ // `.hashes` — the cache must use an own-property lookup instead.
554
+ evoHeadUnknownPrototypeNamedSubjectIsEmpty: () => {
555
+ const c = fileCas(sha256)(home);
556
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
557
+ const e = evo(c)(cacheKey);
558
+ const [, heads] = virtual(state0)(e.head('constructor'));
559
+ assertEq(heads.length, 0);
560
+ },
561
+ evoAddAndHeadHandlePrototypeNamedSubject: () => {
562
+ const c = fileCas(sha256)(home);
563
+ const [state0, cacheKey] = virtual(emptyState)(initEvo(c));
564
+ const e = evo(c)(cacheKey);
565
+ const input = { parents: [], subject: 'toString', snapshot: vecToCBase32(vec8(0x16n)) };
566
+ const [state1, result] = virtual(state0)(e.add(input));
567
+ assert(result[0] === 'ok', ['expected add ok', result]);
568
+ const [, heads] = virtual(state1)(e.head('toString'));
569
+ assertEq(heads.length, 1);
570
+ assertEq(heads[0], result[1]);
571
+ },
572
+ };
@@ -0,0 +1,49 @@
1
+ import { type Effect } from '../../effects/module.f.ts';
2
+ import { type MemOp } from '../../effects/memory/module.f.ts';
3
+ import { type Read, type Write } from '../../effects/node/module.f.ts';
4
+ import { type McpConfig, type McpHandlers, type ToolEntry } from '../../mcp/module.f.ts';
5
+ import { type FileCasOperation } from '../module.f.ts';
6
+ import { type Cache } from '../evo/module.f.ts';
7
+ import type { Key } from '../../effects/memory/module.f.ts';
8
+ /** Arguments for `cas_add`: content to store, with optional encoding type. */
9
+ export declare const casAddArgs: {
10
+ readonly content: import("../../types/rtti/module.f.ts").String;
11
+ readonly type: import("../../types/rtti/module.f.ts").Or<["text", "base64", undefined]>;
12
+ };
13
+ /** Arguments for `cas_get`: the cBase32 hash to look up; optionally request inline content. */
14
+ export declare const casGetArgs: {
15
+ readonly hash: import("../../types/rtti/module.f.ts").String;
16
+ readonly content: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Boolean, undefined]>;
17
+ };
18
+ /** Arguments for `cas_list`: none. */
19
+ export declare const casListArgs: {};
20
+ /**
21
+ * Registry of all CAS tools, bound to `home` and the Evo cache at
22
+ * `cacheKey`. `cas_add` is the only tool that writes, so it is the only one
23
+ * that needs `cacheKey`: a successfully stored blob is folded into the Evo
24
+ * cache via `syncRevision` when it happens to be a `vnd.fjs.revision` — a
25
+ * plain `cas_add` and `evo_add` are two ways to reach the same store, and
26
+ * this keeps `evo_list`/`evo_head` honest about either one without a
27
+ * rescan.
28
+ */
29
+ export declare const casToolRegistry: (home: string) => (cacheKey: Key<Cache>) => readonly ToolEntry<FileCasOperation | MemOp>[];
30
+ /**
31
+ * MCP handlers for `FileCas` plus the Evo API (`fjs/cas/evo`) layered on it,
32
+ * bound to `home` and an already-built Evo cache slot (see `initEvo`).
33
+ */
34
+ export declare const casMcpHandlers: (home: string) => (cacheKey: Key<Cache>) => McpHandlers<FileCasOperation | MemOp>;
35
+ /**
36
+ * Static MCP configuration for the CAS server: advertises the `tools`
37
+ * capability, identifies the server, and pins the protocol version.
38
+ */
39
+ export declare const casConfig: McpConfig;
40
+ /**
41
+ * Runs the combined CAS + Evo MCP server over stdio: scans `~/.cas/` once to
42
+ * build the Evo subject/head cache (`initEvo`), allocates the session-state
43
+ * slot, builds the `mcpStep` for the merged tool registry, and drives the
44
+ * read → parse → dispatch → write loop until stdin EOF.
45
+ */
46
+ export declare const casMcpServer: (home: string) => Effect<Read | Write | MemOp | FileCasOperation, void>;
47
+ export declare const proof: {
48
+ casMcpServer: () => void;
49
+ };