functionalscript 0.45.0 → 0.46.1

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 (296) 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 +10 -0
  4. package/fjs/asn.1/proof.f.mjs +16 -0
  5. package/fjs/basen/base128/module.f.mjs +22 -5
  6. package/fjs/bnf/data/module.f.d.mts +32 -6
  7. package/fjs/bnf/data/module.f.mjs +112 -7
  8. package/fjs/bnf/data/proof.f.d.mts +2 -0
  9. package/fjs/bnf/data/proof.f.mjs +74 -3
  10. package/fjs/bnf/data/types.d.ts +20 -2
  11. package/fjs/bnf/descent/module.f.d.mts +28 -14
  12. package/fjs/bnf/descent/module.f.mjs +122 -77
  13. package/fjs/bnf/descent/proof.f.d.mts +2 -0
  14. package/fjs/bnf/descent/proof.f.mjs +117 -31
  15. package/fjs/bnf/descent/types.d.ts +12 -14
  16. package/fjs/bnf/ll1/module.f.d.mts +64 -22
  17. package/fjs/bnf/ll1/module.f.mjs +214 -154
  18. package/fjs/bnf/ll1/proof.f.d.mts +15 -2
  19. package/fjs/bnf/ll1/proof.f.mjs +323 -149
  20. package/fjs/bnf/ll1/types.d.ts +16 -24
  21. package/fjs/bnf/matcher/module.f.d.mts +66 -0
  22. package/fjs/bnf/matcher/module.f.mjs +81 -0
  23. package/fjs/bnf/matcher/proof.f.d.mts +10 -0
  24. package/fjs/bnf/matcher/proof.f.mjs +79 -0
  25. package/fjs/bnf/matcher/types.d.ts +54 -0
  26. package/fjs/bnf/testlib.f.d.mts +31 -0
  27. package/fjs/bnf/testlib.f.mjs +80 -0
  28. package/fjs/cas/cli/module.f.d.mts +1 -1
  29. package/fjs/cas/cli/module.f.mjs +14 -20
  30. package/fjs/cas/cli/proof.f.d.mts +1 -3
  31. package/fjs/cas/cli/proof.f.mjs +44 -33
  32. package/fjs/cas/evo/module.f.d.mts +64 -18
  33. package/fjs/cas/evo/module.f.mjs +148 -70
  34. package/fjs/cas/evo/proof.f.d.mts +10 -1
  35. package/fjs/cas/evo/proof.f.mjs +305 -223
  36. package/fjs/cas/evo/types.d.ts +45 -9
  37. package/fjs/cas/module.f.d.mts +18 -32
  38. package/fjs/cas/module.f.mjs +129 -128
  39. package/fjs/cas/proof.f.d.mts +11 -8
  40. package/fjs/cas/proof.f.mjs +259 -147
  41. package/fjs/cas/types.d.ts +24 -10
  42. package/fjs/ci/config/module.f.d.mts +2 -2
  43. package/fjs/ci/config/module.f.mjs +2 -2
  44. package/fjs/ci/module.f.d.mts +5 -5
  45. package/fjs/ci/module.f.mjs +8 -7
  46. package/fjs/ci/nix/module.f.d.mts +7 -5
  47. package/fjs/ci/nix/module.f.mjs +13 -12
  48. package/fjs/ci/nix/proof.f.mjs +2 -2
  49. package/fjs/ci/proof.f.mjs +9 -5
  50. package/fjs/cli/module.f.d.mts +4 -6
  51. package/fjs/cli/module.f.mjs +4 -8
  52. package/fjs/cli/proof.f.mjs +17 -16
  53. package/fjs/cli/types.d.ts +2 -3
  54. package/fjs/common/monoid/types.d.ts +1 -1
  55. package/fjs/crypto/hmac/module.f.mjs +2 -2
  56. package/fjs/crypto/sha2/module.f.mjs +3 -1
  57. package/fjs/crypto/sha2/proof.f.d.mts +1 -0
  58. package/fjs/crypto/sha2/proof.f.mjs +24 -0
  59. package/fjs/crypto/sha2/types.d.ts +11 -0
  60. package/fjs/crypto/sign/module.f.mjs +2 -2
  61. package/fjs/dev/module.f.d.mts +13 -4
  62. package/fjs/dev/module.f.mjs +56 -27
  63. package/fjs/dev/update/module.f.d.mts +9 -4
  64. package/fjs/dev/update/module.f.mjs +14 -10
  65. package/fjs/dev/update/proof.f.d.mts +1 -3
  66. package/fjs/dev/update/proof.f.mjs +10 -5
  67. package/fjs/djs/module.f.d.mts +13 -5
  68. package/fjs/djs/module.f.mjs +31 -16
  69. package/fjs/djs/parser/module.f.d.mts +13 -3
  70. package/fjs/djs/parser/module.f.mjs +117 -16
  71. package/fjs/djs/parser/proof.f.d.mts +5 -1
  72. package/fjs/djs/parser/proof.f.mjs +274 -12
  73. package/fjs/djs/parser/types.d.ts +7 -1
  74. package/fjs/djs/proof.f.d.mts +18 -2
  75. package/fjs/djs/proof.f.mjs +187 -12
  76. package/fjs/djs/serializer/module.f.d.mts +25 -8
  77. package/fjs/djs/serializer/module.f.mjs +61 -15
  78. package/fjs/djs/serializer/proof.f.d.mts +5 -0
  79. package/fjs/djs/serializer/proof.f.mjs +23 -1
  80. package/fjs/djs/tokenizer/module.f.d.mts +17 -5
  81. package/fjs/djs/tokenizer/module.f.mjs +99 -60
  82. package/fjs/djs/tokenizer/proof.f.d.mts +1 -1
  83. package/fjs/djs/tokenizer/proof.f.mjs +74 -67
  84. package/fjs/djs/transpiler/module.f.d.mts +12 -7
  85. package/fjs/djs/transpiler/module.f.mjs +82 -56
  86. package/fjs/djs/transpiler/types.d.ts +7 -3
  87. package/fjs/djs/types.d.ts +7 -1
  88. package/fjs/effects/list/module.f.d.mts +14 -11
  89. package/fjs/effects/list/module.f.mjs +13 -11
  90. package/fjs/effects/list/types.d.ts +27 -7
  91. package/fjs/effects/memory/module.f.d.mts +2 -1
  92. package/fjs/effects/memory/module.f.mjs +3 -4
  93. package/fjs/effects/memory/proof.f.mjs +13 -7
  94. package/fjs/effects/memory/types.d.ts +4 -3
  95. package/fjs/effects/mock/module.f.d.mts +20 -4
  96. package/fjs/effects/mock/module.f.mjs +37 -5
  97. package/fjs/effects/mock/types.d.ts +10 -1
  98. package/fjs/effects/module.d.mts +4 -2
  99. package/fjs/effects/module.f.d.mts +466 -280
  100. package/fjs/effects/module.f.mjs +537 -299
  101. package/fjs/effects/module.mjs +2 -1
  102. package/fjs/effects/node/memory/module.d.mts +4 -2
  103. package/fjs/effects/node/memory/module.mjs +6 -3
  104. package/fjs/effects/node/memory/proof.mjs +10 -4
  105. package/fjs/effects/node/module.d.mts +4 -3
  106. package/fjs/effects/node/module.f.d.mts +168 -33
  107. package/fjs/effects/node/module.f.mjs +256 -52
  108. package/fjs/effects/node/module.mjs +57 -34
  109. package/fjs/effects/node/proof.f.d.mts +28 -2
  110. package/fjs/effects/node/proof.f.mjs +161 -42
  111. package/fjs/effects/node/types.d.ts +106 -18
  112. package/fjs/effects/node/virtual/module.f.d.mts +18 -4
  113. package/fjs/effects/node/virtual/module.f.mjs +110 -68
  114. package/fjs/effects/node/virtual/proof.f.d.mts +28 -2
  115. package/fjs/effects/node/virtual/proof.f.mjs +190 -9
  116. package/fjs/effects/proof.f.d.mts +69 -37
  117. package/fjs/effects/proof.f.mjs +410 -130
  118. package/fjs/effects/types.d.ts +161 -33
  119. package/fjs/emergent_testing/module.f.d.mts +19 -12
  120. package/fjs/emergent_testing/module.f.mjs +93 -33
  121. package/fjs/emergent_testing/proof.f.d.mts +21 -7
  122. package/fjs/emergent_testing/proof.f.mjs +166 -32
  123. package/fjs/emergent_testing/types.d.ts +22 -4
  124. package/fjs/fsm/module.f.d.mts +14 -4
  125. package/fjs/fsm/module.f.mjs +54 -37
  126. package/fjs/fsm/proof.f.d.mts +2 -0
  127. package/fjs/fsm/proof.f.mjs +83 -114
  128. package/fjs/js/keywords/module.f.d.mts +52 -0
  129. package/fjs/js/keywords/module.f.mjs +72 -0
  130. package/fjs/js/keywords/proof.f.d.mts +3 -0
  131. package/fjs/js/keywords/proof.f.mjs +13 -0
  132. package/fjs/js/tokenizer/module.f.d.mts +26 -6
  133. package/fjs/js/tokenizer/module.f.mjs +145 -159
  134. package/fjs/js/tokenizer/proof.f.d.mts +1 -0
  135. package/fjs/js/tokenizer/proof.f.mjs +54 -24
  136. package/fjs/js/tokenizer/types.d.ts +33 -24
  137. package/fjs/mcp/cas/module.f.d.mts +1 -6
  138. package/fjs/mcp/cas/module.f.mjs +55 -51
  139. package/fjs/mcp/cas/proof.f.d.mts +15 -0
  140. package/fjs/mcp/cas/proof.f.mjs +174 -0
  141. package/fjs/mcp/evo/module.f.d.mts +19 -10
  142. package/fjs/mcp/evo/module.f.mjs +48 -27
  143. package/fjs/mcp/evo/proof.f.d.mts +6 -1
  144. package/fjs/mcp/evo/proof.f.mjs +115 -31
  145. package/fjs/mcp/module.f.d.mts +4 -4
  146. package/fjs/mcp/module.f.mjs +6 -6
  147. package/fjs/mcp/proof.f.d.mts +5 -3
  148. package/fjs/mcp/proof.f.mjs +112 -46
  149. package/fjs/media/html/module.f.mjs +1 -1
  150. package/fjs/media/json/extended/module.f.d.mts +82 -0
  151. package/fjs/media/json/extended/module.f.mjs +153 -0
  152. package/fjs/media/json/extended/proof.f.d.mts +42 -0
  153. package/fjs/media/json/extended/proof.f.mjs +127 -0
  154. package/fjs/media/json/extended/types.d.ts +23 -0
  155. package/fjs/media/json/module.f.d.mts +8 -2
  156. package/fjs/media/json/module.f.mjs +43 -41
  157. package/fjs/media/json/number/module.f.d.mts +59 -0
  158. package/fjs/media/json/number/module.f.mjs +136 -0
  159. package/fjs/media/json/number/proof.f.d.mts +24 -0
  160. package/fjs/media/json/number/proof.f.mjs +86 -0
  161. package/fjs/media/json/number/types.d.ts +28 -0
  162. package/fjs/media/json/parser/module.f.d.mts +25 -13
  163. package/fjs/media/json/parser/module.f.mjs +114 -70
  164. package/fjs/media/json/parser/proof.f.d.mts +5 -0
  165. package/fjs/media/json/parser/proof.f.mjs +31 -1
  166. package/fjs/media/json/parser/types.d.ts +33 -14
  167. package/fjs/media/json/rtti/module.f.d.mts +1 -1
  168. package/fjs/media/json/rtti/module.f.mjs +1 -1
  169. package/fjs/media/json/rtti/proof.f.mjs +9 -9
  170. package/fjs/media/json/schema/module.f.mjs +3 -13
  171. package/fjs/media/json/schema/proof.f.d.mts +0 -1
  172. package/fjs/media/json/schema/proof.f.mjs +1 -2
  173. package/fjs/media/json/serializer/module.f.d.mts +32 -1
  174. package/fjs/media/json/serializer/module.f.mjs +64 -2
  175. package/fjs/media/json/tokenizer/module.f.mjs +7 -3
  176. package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
  177. package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
  178. package/fjs/media/json/types.d.ts +36 -10
  179. package/fjs/media/lock/module.f.d.mts +100 -0
  180. package/fjs/media/lock/module.f.mjs +125 -0
  181. package/fjs/media/lock/proof.f.d.mts +33 -0
  182. package/fjs/media/lock/proof.f.mjs +196 -0
  183. package/fjs/media/lock/types.d.ts +15 -0
  184. package/fjs/media/module.f.d.mts +6 -5
  185. package/fjs/media/module.f.mjs +8 -7
  186. package/fjs/media/note/module.f.d.mts +121 -0
  187. package/fjs/media/note/module.f.mjs +131 -0
  188. package/fjs/media/note/proof.f.d.mts +29 -0
  189. package/fjs/media/note/proof.f.mjs +150 -0
  190. package/fjs/media/note/types.d.ts +10 -0
  191. package/fjs/media/proof.f.d.mts +4 -1
  192. package/fjs/media/proof.f.mjs +40 -21
  193. package/fjs/media/revision/module.f.d.mts +78 -7
  194. package/fjs/media/revision/module.f.mjs +119 -12
  195. package/fjs/media/revision/proof.f.d.mts +10 -0
  196. package/fjs/media/revision/proof.f.mjs +88 -0
  197. package/fjs/media/revision/types.d.ts +34 -5
  198. package/fjs/media/type/module.f.d.mts +33 -15
  199. package/fjs/media/type/module.f.mjs +35 -29
  200. package/fjs/media/type/proof.f.d.mts +2 -1
  201. package/fjs/media/type/proof.f.mjs +30 -10
  202. package/fjs/module.f.mjs +29 -8
  203. package/fjs/nanvm/proof.f.mjs +3 -3
  204. package/fjs/nanvm/rust/module.f.mjs +1 -1
  205. package/fjs/nanvm/update/module.f.d.mts +4 -4
  206. package/fjs/nanvm/update/module.f.mjs +8 -9
  207. package/fjs/nanvm/update/proof.f.mjs +4 -3
  208. package/fjs/proof.f.d.mts +3 -3
  209. package/fjs/proof.f.mjs +36 -10
  210. package/fjs/protocol/json_rpc/module.f.d.mts +2 -2
  211. package/fjs/protocol/json_rpc/module.f.mjs +3 -3
  212. package/fjs/protocol/json_rpc/proof.f.mjs +4 -4
  213. package/fjs/protocol/mcp/module.f.d.mts +10 -13
  214. package/fjs/protocol/mcp/module.f.mjs +74 -58
  215. package/fjs/protocol/mcp/proof.f.d.mts +13 -4
  216. package/fjs/protocol/mcp/proof.f.mjs +193 -61
  217. package/fjs/protocol/mcp/stdio/module.f.d.mts +15 -7
  218. package/fjs/protocol/mcp/stdio/module.f.mjs +38 -25
  219. package/fjs/protocol/mcp/stdio/proof.f.d.mts +4 -2
  220. package/fjs/protocol/mcp/stdio/proof.f.mjs +45 -12
  221. package/fjs/protocol/mcp/stdio/types.d.ts +1 -1
  222. package/fjs/protocol/mcp/types.d.ts +17 -6
  223. package/fjs/sul/id/module.f.d.mts +0 -1
  224. package/fjs/sul/id/module.f.mjs +2 -3
  225. package/fjs/sul/level/hash/module.f.mjs +2 -1
  226. package/fjs/sul/level/hash/proof.f.mjs +2 -2
  227. package/fjs/sul/module.f.mjs +18 -13
  228. package/fjs/text/code_point/module.f.d.mts +8 -0
  229. package/fjs/text/code_point/module.f.mjs +8 -1
  230. package/fjs/text/code_point/proof.f.d.mts +1 -0
  231. package/fjs/text/code_point/proof.f.mjs +11 -0
  232. package/fjs/text/sgr/module.f.d.mts +4 -6
  233. package/fjs/text/sgr/module.f.mjs +6 -7
  234. package/fjs/text/utf16/module.f.mjs +7 -2
  235. package/fjs/text/utf16/proof.f.mjs +3 -3
  236. package/fjs/text/utf8/module.f.mjs +4 -2
  237. package/fjs/types/array/module.f.mjs +14 -2
  238. package/fjs/types/bit_vec/module.f.d.mts +0 -2
  239. package/fjs/types/bit_vec/module.f.mjs +46 -32
  240. package/fjs/types/bit_vec/proof.f.mjs +2 -2
  241. package/fjs/types/btree/remove/module.f.mjs +1 -1
  242. package/fjs/types/btree/set/module.f.mjs +9 -11
  243. package/fjs/types/btree/set/proof.f.mjs +12 -0
  244. package/fjs/types/byte_set/module.f.d.mts +11 -4
  245. package/fjs/types/byte_set/module.f.mjs +14 -6
  246. package/fjs/types/byte_set/proof.f.mjs +7 -7
  247. package/fjs/types/function/compare/module.f.mjs +10 -3
  248. package/fjs/types/list/module.f.d.mts +1 -1
  249. package/fjs/types/list/module.f.mjs +1 -1
  250. package/fjs/types/nullable/module.f.d.mts +18 -4
  251. package/fjs/types/nullable/module.f.mjs +21 -4
  252. package/fjs/types/nullable/proof.f.d.mts +4 -0
  253. package/fjs/types/nullable/proof.f.mjs +15 -0
  254. package/fjs/types/object/module.f.d.mts +12 -2
  255. package/fjs/types/object/module.f.mjs +11 -1
  256. package/fjs/types/patricia_trie/module.f.mjs +26 -11
  257. package/fjs/types/result/module.f.d.mts +3 -3
  258. package/fjs/types/result/module.f.mjs +3 -3
  259. package/fjs/types/rtti/common/module.f.d.mts +25 -28
  260. package/fjs/types/rtti/common/module.f.mjs +36 -32
  261. package/fjs/types/rtti/common/proof.f.mjs +5 -5
  262. package/fjs/types/rtti/data/module.f.d.mts +14 -0
  263. package/fjs/types/rtti/data/module.f.mjs +25 -2
  264. package/fjs/types/rtti/data/proof.f.d.mts +1 -0
  265. package/fjs/types/rtti/data/proof.f.mjs +25 -2
  266. package/fjs/types/rtti/parse/module.f.d.mts +24 -14
  267. package/fjs/types/rtti/parse/module.f.mjs +37 -28
  268. package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
  269. package/fjs/types/rtti/parse/proof.f.mjs +33 -14
  270. package/fjs/types/rtti/proof.f.mjs +3 -1
  271. package/fjs/types/rtti/ts/module.f.mjs +13 -17
  272. package/fjs/types/rtti/ts/types.d.ts +14 -1
  273. package/fjs/types/rtti/validate/module.f.d.mts +81 -19
  274. package/fjs/types/rtti/validate/module.f.mjs +107 -61
  275. package/fjs/types/rtti/validate/proof.f.d.mts +13 -10
  276. package/fjs/types/rtti/validate/proof.f.mjs +186 -197
  277. package/fjs/types/sorted_set/module.f.d.mts +18 -0
  278. package/fjs/types/sorted_set/module.f.mjs +22 -0
  279. package/fjs/types/sorted_set/proof.f.d.mts +1 -0
  280. package/fjs/types/sorted_set/proof.f.mjs +16 -1
  281. package/fjs/types/uint8array/module.f.d.mts +1 -1
  282. package/fjs/types/uint8array/module.f.mjs +1 -1
  283. package/fjs/website/module.f.d.mts +3 -3
  284. package/fjs/website/module.f.mjs +4 -7
  285. package/fjs/website/proof.f.mjs +2 -1
  286. package/package.json +2 -2
  287. package/fjs/dev/package_json/module.f.d.mts +0 -39
  288. package/fjs/dev/package_json/module.f.mjs +0 -40
  289. package/fjs/dev/package_json/proof.f.d.mts +0 -6
  290. package/fjs/dev/package_json/proof.f.mjs +0 -32
  291. package/fjs/effects/eff/module.f.d.mts +0 -20
  292. package/fjs/effects/eff/module.f.mjs +0 -70
  293. package/fjs/effects/eff/proof.f.d.mts +0 -15
  294. package/fjs/effects/eff/proof.f.mjs +0 -69
  295. package/fjs/effects/eff/types.d.ts +0 -71
  296. package/fjs/types/rtti/validate/types.d.ts +0 -6
@@ -0,0 +1,174 @@
1
+ /**
2
+ * @import { Effect } from '../../effects/types.ts'
3
+ * @import { Result } from '../../types/result/types.ts'
4
+ * @import { FileCasOperation } from '../../cas/types.ts'
5
+ * @import { MemOp } from '../../effects/memory/types.ts'
6
+ * @import { Cache } from '../../cas/evo/types.ts'
7
+ * @import { Key } from '../../effects/memory/types.ts'
8
+ * @import { ToolsCallResult } from '../../protocol/mcp/types.ts'
9
+ */
10
+
11
+ import { casToolRegistry } from './module.f.mjs'
12
+ import { match } from '../../effects/module.f.mjs'
13
+ import { error, ok } from '../../types/result/module.f.mjs'
14
+ import { ioError } from '../../effects/node/module.f.mjs'
15
+ import { vec, vec8 } from '../../types/bit_vec/module.f.mjs'
16
+ import { vecToCBase32 } from '../../basen/cbase32/module.f.mjs'
17
+ import { assert, assertEq } from '../../asserts/module.f.mjs'
18
+ import { number as rttiNumber, string as rttiString } from '../../types/rtti/module.f.mjs'
19
+ import { parse as rttiParse } from '../../types/rtti/parse/module.f.mjs'
20
+ import { unwrap } from '../../types/result/module.f.mjs'
21
+ import { parse as parseJson } from '../../media/json/module.f.mjs'
22
+
23
+ // A harmless "always succeeds" response for a command, used by `drive` once a
24
+ // test's overrides for that command are exhausted — same technique as
25
+ // `fjs/cas/proof.f.mjs`'s `drive`, extended with `MemOp` since `cas_add` also
26
+ // touches the Evo cache on its success path.
27
+ /** @type {(cmd: string) => unknown} */
28
+ const defaultResponse = cmd => {
29
+ switch (cmd) {
30
+ case 'now': return ok(0)
31
+ case 'randomInt': return ok(0)
32
+ case 'mkdir': case 'createExclusive': case 'rename': case 'rm':
33
+ case 'writeBytes': case 'access':
34
+ return ok(undefined)
35
+ case 'readdir': return ok(/** @type {readonly unknown[]} */ ([]))
36
+ case 'stat': return ok({ size: 0 })
37
+ // An empty chunk reads as end-of-stream, so a `readBytes` call with no
38
+ // override reads as an immediately-empty file by default.
39
+ case 'readBytes': return ok(vec(0n)(0n))
40
+ case 'memCreate': return ok(/** @type {unknown} */ ('mem-key'))
41
+ case 'memRead': return ok(undefined)
42
+ case 'memWrite': return ok(undefined)
43
+ default: return ok(undefined)
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Drives a `FileCasOperation | MemOp` effect to completion with synthetic op
49
+ * responses instead of a filesystem. `overrides[cmd]` is a queue consumed in
50
+ * call order; once a command's queue is empty (or was never given),
51
+ * `defaultResponse` supplies an always-succeeds value.
52
+ *
53
+ * `cas_add`'s write-failure branch and `cas_get`'s "hash vanished between
54
+ * reads" branches are real only under a race (a failing disk, a concurrent
55
+ * writer, a GC sweep) between two of the tool's own steps — the same shape of
56
+ * branch `fjs/cas/proof.f.mjs` reaches with its own `drive` helper, applied
57
+ * here one layer up at the MCP tool boundary.
58
+ *
59
+ * The result is unwrapped: a tool handler answers
60
+ * `Effect<…, ToolsCallResult, never>`, having absorbed its failures into
61
+ * `isError`, so callers want the answer rather than the `ok` around it. The
62
+ * `never` channel is what makes that unwrap total.
63
+ *
64
+ * @type {(overrides: Partial<Record<string, unknown[]>>) => (e: Effect<FileCasOperation | MemOp, unknown, never>) => unknown}
65
+ */
66
+ const drive = overrides => {
67
+ /** @type {(cmd: string) => unknown} */
68
+ const next = cmd => {
69
+ const queue = overrides[cmd]
70
+ return queue !== undefined && queue.length > 0 ? queue.shift() : defaultResponse(cmd)
71
+ }
72
+ const handlers = /** @type {Parameters<typeof match>[0]} */ ({
73
+ access: () => next('access'),
74
+ createExclusive: () => next('createExclusive'),
75
+ mkdir: () => next('mkdir'),
76
+ now: () => next('now'),
77
+ randomInt: () => next('randomInt'),
78
+ readBytes: () => next('readBytes'),
79
+ readdir: () => next('readdir'),
80
+ rename: () => next('rename'),
81
+ rm: () => next('rm'),
82
+ stat: () => next('stat'),
83
+ writeBytes: () => next('writeBytes'),
84
+ memCreate: () => next('memCreate'),
85
+ memRead: () => next('memRead'),
86
+ memWrite: () => next('memWrite'),
87
+ })
88
+ const matcher = match(handlers)
89
+ /** @type {(e: Effect<FileCasOperation | MemOp, unknown, unknown>) => Result<unknown, unknown>} */
90
+ const run_ = e => {
91
+ const m = matcher(e)
92
+ return m[0] === 'done' ? m[1] : run_(m[2](/** @type {any} */ (m[1])))
93
+ }
94
+ return e => unwrap(run_(e))
95
+ }
96
+
97
+ // `syncRevision` is only reached on a *successful* write, which none of these
98
+ // cases exercise, so the cache key's actual identity never matters — only its
99
+ // type does.
100
+ const cacheKey = /** @type {Key<Cache>} */ (/** @type {any} */ ('unused-cache-key'))
101
+
102
+ const registry = casToolRegistry('.')(cacheKey)
103
+
104
+ /** @type {(name: string) => (args: any) => Effect<FileCasOperation | MemOp, ToolsCallResult, never>} */
105
+ const toolHandle = name => {
106
+ const entry = registry.find(t => t.name === name)
107
+ assert(entry !== undefined, `no such tool: ${name}`)
108
+ return /** @type {NonNullable<typeof entry>} */ (entry).handle
109
+ }
110
+
111
+ // Any well-formed cBase32 hash works: none of these cases resolve it against a
112
+ // real store, since every filesystem op is driven synthetically.
113
+ const someHash = vecToCBase32(vec(256n)(0n))
114
+
115
+ const meta = /** @type {const} */ ({
116
+ length: rttiNumber,
117
+ mimeType: rttiString,
118
+ type: rttiString,
119
+ uri: rttiString,
120
+ })
121
+ const parseMeta = rttiParse(meta)
122
+
123
+ export const proof = {
124
+ // cas_list: a store that exists but cannot be walked is a tool-level error
125
+ // carrying the host's own words — neither a panic (which is what it used
126
+ // to be, one `unwrap` deep) nor an empty listing, which would tell the
127
+ // client the store holds nothing.
128
+ casListStorageErrorReturnsError: () => {
129
+ const result = /** @type {ToolsCallResult} */ (
130
+ drive({ access: [error(ioError({ code: 'EACCES', message: 'permission denied' }))] })(toolHandle('cas_list')({}))
131
+ )
132
+ assert(result.isError === true, ['expected isError', result])
133
+ },
134
+ // cas_add: a writeBytes failure mid-upload (disk full, permissions) is
135
+ // reported as a tool-level error, not a thrown exception or a silent
136
+ // success.
137
+ casAddWriteErrorReturnsError: () => {
138
+ const result = /** @type {ToolsCallResult} */ (
139
+ drive({ writeBytes: [error(ioError({ message: 'disk full' }))] })(toolHandle('cas_add')({ content: 'hello' }))
140
+ )
141
+ assert(result.isError === true, ['expected isError', result])
142
+ },
143
+ // cas_get, content:false: the streaming metadata pass finds a small
144
+ // whole-blob-text hash, so it starts a second, independent read to refine
145
+ // `mimeType` via the dialect-aware detector. If that second read finds the
146
+ // hash gone (a GC sweep raced it away), the code falls back to the
147
+ // streaming verdict rather than fail the whole request.
148
+ casGetMetadataRefineHashVanishesFallsBackToStreamingVerdict: () => {
149
+ const result = /** @type {ToolsCallResult} */ (
150
+ drive({ readBytes: [ok(vec8(0x41n)), ok(vec(0n)(0n)), error(ioError({ message: 'vanished' }))] })
151
+ (toolHandle('cas_get')({ hash: someHash, content: false }))
152
+ )
153
+ assert(result.isError !== true, ['expected ok result', result])
154
+ const text = result.content[0]
155
+ assert(text.type === 'text', ['expected text content', text])
156
+ const parsed = unwrap(parseMeta(unwrap(parseJson(text.text))))
157
+ assertEq(parsed.type, 'text')
158
+ assertEq(parsed.mimeType, 'text/plain')
159
+ assertEq(parsed.length, 1)
160
+ },
161
+ // cas_get, content:true: the streaming metadata pass succeeds and the blob
162
+ // fits inline, so a second read materializes it. If that second read finds
163
+ // the hash gone, the whole request fails — there is no streaming verdict
164
+ // to fall back to once inline content was actually promised.
165
+ casGetContentFetchHashVanishesReturnsError: () => {
166
+ const result = /** @type {ToolsCallResult} */ (
167
+ drive({ readBytes: [ok(vec8(0x41n)), ok(vec(0n)(0n)), error(ioError({ message: 'vanished' }))] })
168
+ (toolHandle('cas_get')({ hash: someHash, content: true }))
169
+ )
170
+ assert(result.isError === true, ['expected isError', result])
171
+ const text = result.content[0]
172
+ assert(text.type === 'text' && text.text.includes('no such hash'), ['expected no-such-hash message', text])
173
+ },
174
+ }
@@ -8,16 +8,16 @@
8
8
  *
9
9
  * | Tool | args | action | result |
10
10
  * |----------------|-----------------------------------------------|-------------------|--------------------------------------|
11
- * | `evo_list` | `{ archived? }` | `e.list(...)` | subjects, as a JSON array of strings |
12
- * | `evo_head` | `{ subject }` | `e.head(...)` | head hashes, one per line |
13
- * | `evo_revision` | `{ hash }` | `e.revision(...)` | the revision, as JSON `RevisionData` |
14
- * | `evo_add` | `{ parents, snapshot?, subject?, archived? }` | `e.add(...)` | hash (cBase32) |
11
+ * | `evo_list` | `{ archived? }` | `e.list(...)` | subjects, as a JSON array of strings |
12
+ * | `evo_head` | `{ subject }` | `e.head(...)` | head hashes, one per line |
13
+ * | `evo_revision` | `{ hash }` | `e.revision(...)` | the revision, as JSON `RevisionData` |
14
+ * | `evo_add` | `{ parents, snapshot?, subject?, archived?, lock? }` | `e.add(...)` | hash (cBase32) |
15
15
  *
16
16
  * `evo_add` and `evo_revision` speak the same structure — `fjs/cas/evo`'s
17
17
  * `RevisionData` — in opposite directions, so a revision read back can be
18
- * added again as-is. `evo_add`'s advertised arguments stay as they are: the
19
- * one field `evo_revision` returns that `evo_add` does not accept is
20
- * `generation`, which the server computes, and rtti's struct validation
18
+ * added again as-is, `lock` scopes and all. The one field `evo_revision`
19
+ * returns that `evo_add` does not accept is `generation`, which the server
20
+ * computes, and rtti's struct validation
21
21
  * ignores properties the schema does not name, so a whole `evo_revision`
22
22
  * result can be passed straight back to `evo_add`.
23
23
  *
@@ -43,11 +43,10 @@
43
43
  *
44
44
  * @module
45
45
  *
46
- * @import { Effect, Operation } from '../../effects/types.ts'
46
+ * @import { Operation } from '../../effects/types.ts'
47
47
  * @import { MemOp } from '../../effects/memory/types.ts'
48
- * @import { ToolEntry, ToolsCallResult } from '../../protocol/mcp/types.ts'
48
+ * @import { ToolEntry } from '../../protocol/mcp/types.ts'
49
49
  * @import { Evo } from '../../cas/evo/types.ts'
50
- * @import { Ts } from '../../types/rtti/ts/types.ts'
51
50
  */
52
51
  import type { Operation } from '../../effects/types.ts';
53
52
  import type { MemOp } from '../../effects/memory/types.ts';
@@ -72,12 +71,22 @@ export declare const evoRevisionArgs: {
72
71
  * Arguments for `evo_add`: a new revision, per `fjs/cas/evo`'s
73
72
  * `RevisionData` — every field of it the caller supplies, i.e. all but
74
73
  * `generation`, which the server computes.
74
+ *
75
+ * `lock` is the media format's own field schema
76
+ * (`fjs/media/revision`'s `lockField`), not a restatement of it, so the
77
+ * advertised `inputSchema` and what the server accepts cannot drift apart. It
78
+ * therefore accepts either form the format does — the bindings inline, or the
79
+ * hash of a `vnd.fjs.lock` blob holding them. The map half is recursive, which
80
+ * makes it the one argument whose JSON Schema reaches a named `$defs` rule
81
+ * through a local `$ref` rather than being inlined whole — see
82
+ * `fjs/media/json/schema`.
75
83
  */
76
84
  export declare const evoAddArgs: {
77
85
  readonly parents: import("../../types/rtti/types.ts").Type1<"array", import("../../types/rtti/types.ts")._Type0<"string">>;
78
86
  readonly snapshot: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, undefined]>;
79
87
  readonly subject: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, undefined]>;
80
88
  readonly archived: import("../../types/rtti/types.ts").Or<readonly [true, undefined]>;
89
+ readonly lock: import("../../types/rtti/types.ts").Or<readonly [import("../../media/revision/types.ts").LockFieldSchema, undefined]>;
81
90
  };
82
91
  /**
83
92
  * Registry of all Evo tools, bound to an `Evo<O>`.
@@ -8,16 +8,16 @@
8
8
  *
9
9
  * | Tool | args | action | result |
10
10
  * |----------------|-----------------------------------------------|-------------------|--------------------------------------|
11
- * | `evo_list` | `{ archived? }` | `e.list(...)` | subjects, as a JSON array of strings |
12
- * | `evo_head` | `{ subject }` | `e.head(...)` | head hashes, one per line |
13
- * | `evo_revision` | `{ hash }` | `e.revision(...)` | the revision, as JSON `RevisionData` |
14
- * | `evo_add` | `{ parents, snapshot?, subject?, archived? }` | `e.add(...)` | hash (cBase32) |
11
+ * | `evo_list` | `{ archived? }` | `e.list(...)` | subjects, as a JSON array of strings |
12
+ * | `evo_head` | `{ subject }` | `e.head(...)` | head hashes, one per line |
13
+ * | `evo_revision` | `{ hash }` | `e.revision(...)` | the revision, as JSON `RevisionData` |
14
+ * | `evo_add` | `{ parents, snapshot?, subject?, archived?, lock? }` | `e.add(...)` | hash (cBase32) |
15
15
  *
16
16
  * `evo_add` and `evo_revision` speak the same structure — `fjs/cas/evo`'s
17
17
  * `RevisionData` — in opposite directions, so a revision read back can be
18
- * added again as-is. `evo_add`'s advertised arguments stay as they are: the
19
- * one field `evo_revision` returns that `evo_add` does not accept is
20
- * `generation`, which the server computes, and rtti's struct validation
18
+ * added again as-is, `lock` scopes and all. The one field `evo_revision`
19
+ * returns that `evo_add` does not accept is `generation`, which the server
20
+ * computes, and rtti's struct validation
21
21
  * ignores properties the schema does not name, so a whole `evo_revision`
22
22
  * result can be passed straight back to `evo_add`.
23
23
  *
@@ -43,15 +43,16 @@
43
43
  *
44
44
  * @module
45
45
  *
46
- * @import { Effect, Operation } from '../../effects/types.ts'
46
+ * @import { Operation } from '../../effects/types.ts'
47
47
  * @import { MemOp } from '../../effects/memory/types.ts'
48
- * @import { ToolEntry, ToolsCallResult } from '../../protocol/mcp/types.ts'
48
+ * @import { ToolEntry } from '../../protocol/mcp/types.ts'
49
49
  * @import { Evo } from '../../cas/evo/types.ts'
50
- * @import { Ts } from '../../types/rtti/ts/types.ts'
51
50
  */
52
51
 
53
52
  import { string, option, array } from '../../types/rtti/module.f.mjs'
54
- import { pure, step } from '../../effects/module.f.mjs'
53
+ import { lockField } from '../../media/revision/module.f.mjs'
54
+ import { pureOk, resultStep } from '../../effects/module.f.mjs'
55
+ import { evoSummary } from '../../cas/evo/module.f.mjs'
55
56
  import {
56
57
  toolEntry, errorResult, okResult,
57
58
  } from '../../protocol/mcp/module.f.mjs'
@@ -82,12 +83,22 @@ export const evoRevisionArgs = /** @type {const} */ ({
82
83
  * Arguments for `evo_add`: a new revision, per `fjs/cas/evo`'s
83
84
  * `RevisionData` — every field of it the caller supplies, i.e. all but
84
85
  * `generation`, which the server computes.
86
+ *
87
+ * `lock` is the media format's own field schema
88
+ * (`fjs/media/revision`'s `lockField`), not a restatement of it, so the
89
+ * advertised `inputSchema` and what the server accepts cannot drift apart. It
90
+ * therefore accepts either form the format does — the bindings inline, or the
91
+ * hash of a `vnd.fjs.lock` blob holding them. The map half is recursive, which
92
+ * makes it the one argument whose JSON Schema reaches a named `$defs` rule
93
+ * through a local `$ref` rather than being inlined whole — see
94
+ * `fjs/media/json/schema`.
85
95
  */
86
96
  export const evoAddArgs = /** @type {const} */ ({
87
97
  parents: array(string),
88
98
  snapshot: option(string),
89
99
  subject: option(string),
90
100
  archived: option(true),
101
+ lock: option(lockField),
91
102
  })
92
103
 
93
104
  // ── Tool registry ────────────────────────────────────────────────────────────────
@@ -110,44 +121,54 @@ export const evoToolRegistry = e => [
110
121
  // constrained to a newline-free alphabet), so a `join('\n')` line
111
122
  // format could not represent an empty subject or one containing a
112
123
  // newline without ambiguity — JSON encoding can.
113
- /** @type {(args: Ts<typeof evoListArgs>) => Effect<MemOp, ToolsCallResult>} */
114
- (({ archived }) => step(
124
+
125
+ // A cache slot the runner cannot reach is a tool-level error, not a
126
+ // panic: the client asked a question the server could not answer, and
127
+ // saying so is an ordinary `isError` result.
128
+ (({ archived }) => resultStep(
115
129
  e.list(archived),
116
- subjects => pure(okResult(toJson(subjects)))
130
+ r => pureOk(r[0] === 'error'
131
+ ? errorResult(evoSummary(r[1]))
132
+ : okResult(toJson(r[1])))
117
133
  )),
118
134
  ),
119
135
  toolEntry(
120
136
  'evo_head',
121
137
  'List the current head hashes (cBase32) of a subject, one per line. Empty when the subject is unknown.',
122
138
  evoHeadArgs,
123
- /** @type {(args: Ts<typeof evoHeadArgs>) => Effect<MemOp, ToolsCallResult>} */
124
- (({ subject }) => step(
139
+ ({ subject }) => resultStep(
125
140
  e.head(subject),
126
- heads => pure(okResult(heads.join('\n'))),
127
- )),
141
+ r => pureOk(r[0] === 'error'
142
+ ? errorResult(evoSummary(r[1]))
143
+ : okResult(r[1].join('\n'))),
144
+ ),
128
145
  ),
129
146
  toolEntry(
130
147
  'evo_revision',
131
- 'Read one revision by hash, as JSON: `{ subject, parents, snapshot, generation, archived? }`. `parents[0]` is the mainline parent and every further entry is a merged-in branch; `parents` and `snapshot` come back in their canonical cBase32 spelling, so they compare directly against `evo_head` output. Errors when the hash is not cBase32, is not present in the store, could not be read, or does not hold a `vnd.fjs.revision` blob — use `cas_get` for raw bytes of non-revision content.',
148
+ 'Read one revision by hash, as JSON: `{ subject, parents, snapshot, generation, archived?, lock? }`. `parents[0]` is the mainline parent and every further entry is a merged-in branch; `parents` and `snapshot` come back in their canonical cBase32 spelling, so they compare directly against `evo_head` output. Errors when the hash is not cBase32, is not present in the store, could not be read, or does not hold a `vnd.fjs.revision` blob — use `cas_get` for raw bytes of non-revision content.',
132
149
  evoRevisionArgs,
133
150
  // The revision goes out as JSON in a text content item, like
134
151
  // `evo_list`'s. An encoded response that outgrows the transport cap is
135
152
  // the transport's `-32603`, not a tool-level error — see "Result size"
136
153
  // in the module doc.
137
- /** @type {(args: Ts<typeof evoRevisionArgs>) => Effect<O | MemOp, ToolsCallResult>} */
138
- (({ hash }) => step(
154
+
155
+ (({ hash }) => resultStep(
139
156
  e.revision(hash),
140
- result => pure(result[0] === 'error' ? errorResult(result[1]) : okResult(toJson(result[1])))
157
+ r => pureOk(r[0] === 'error' ? errorResult(evoSummary(r[1])) : okResult(toJson(r[1])))
141
158
  )),
142
159
  ),
143
160
  toolEntry(
144
161
  'evo_add',
145
- 'Add a new revision (a `vnd.fjs.revision` blob) and return its hash (cBase32). `subject` is required unless there is exactly one parent, from which it is inherited. `snapshot`, when omitted, is resolved from the parents (zero parents → `subject`, one parent → the parent\'s snapshot; a merge requires an explicit `snapshot`) and written explicitly. `generation` is computed by the server.',
162
+ 'Add a new revision (a `vnd.fjs.revision` blob) and return its hash (cBase32). `subject` is required unless there is exactly one parent, from which it is inherited. `snapshot`, when omitted, is resolved from the parents (zero parents → `subject`, one parent → the parent\'s snapshot; a merge requires an explicit `snapshot`) and written explicitly. `generation` is computed by the server. `lock` is optional resolver input: a map from dependency subject to the cBase32 hash of the content it resolves to, or to a nested map scoping further bindings under that subject (use nesting only for conflicting choices a flat map cannot express, e.g. two dependencies needing different versions of a third). Pass a cBase32 hash instead of a map to share one already stored as a `vnd.fjs.lock` blob; the server records the reference and does not follow it.',
146
163
  evoAddArgs,
147
- /** @type {(input: Ts<typeof evoAddArgs>) => Effect<O | MemOp, ToolsCallResult>} */
148
- (input => step(
164
+ // Two failures, one MCP shape: a rejected revision and an unreachable
165
+ // cache slot are both `isError` to the client, and both carry a message
166
+ // it can read. They stay distinguishable by tag — `evoSummary` is the
167
+ // renderer that switches on it — so telling them apart never needed
168
+ // them to arrive in separate layers.
169
+ input => resultStep(
149
170
  e.add(input),
150
- result => pure(result[0] === 'error' ? errorResult(result[1]) : okResult(result[1]))
151
- )),
171
+ r => pureOk(r[0] === 'error' ? errorResult(evoSummary(r[1])) : okResult(r[1]))
172
+ ),
152
173
  ),
153
174
  ]
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * @import { Evo } from '../../cas/evo/types.ts'
3
3
  * @import { ToolEntry, ToolsCallResult } from '../../protocol/mcp/types.ts'
4
- * @import { Operation } from '../../effects/types.ts'
4
+ * @import { NotImplemented, Operation } from '../../effects/types.ts'
5
+ * @import { Result } from '../../types/result/types.ts'
6
+ * @import { State } from '../../effects/node/virtual/types.ts'
5
7
  */
6
8
  export declare const proof: {
9
+ channelFailureIsToolError: () => void;
7
10
  toolNamesMatchTheDesign: () => void;
8
11
  evoListReflectsTheCache: () => void;
9
12
  evoListEncodesArbitrarySubjectsAsJson: () => void;
@@ -13,5 +16,7 @@ export declare const proof: {
13
16
  evoRevisionReturnsRevisionJson: () => void;
14
17
  evoRevisionDomainErrorIsError: () => void;
15
18
  evoAddSuccessReturnsHash: () => void;
19
+ evoAddAndRevisionCarryNestedLocks: () => void;
20
+ evoAddInputSchemaPublishesTheLockRecursion: () => void;
16
21
  evoAddDomainErrorIsError: () => void;
17
22
  };