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
@@ -4,11 +4,32 @@
4
4
  *
5
5
  * @module
6
6
  */
7
- import type { Operation, Effect } from '../../effects/types.ts';
7
+ import type { Effect, NotImplemented, Operation } from '../../effects/types.ts';
8
8
  import type { MemOp } from '../../effects/memory/types.ts';
9
- import type { Result } from '../../types/result/types.ts';
10
9
  import type { StringMap } from '../../types/object/types.ts';
11
- import type { LockMap } from '../../media/revision/types.ts';
10
+ import type { LockField } from '../../media/revision/types.ts';
11
+ /**
12
+ * A revision was rejected on its own terms: a parent that does not resolve, a
13
+ * hash that is not cBase32, a blob that holds something other than a
14
+ * `vnd.fjs.revision`, a store that would not take the write.
15
+ *
16
+ * A tagged tuple rather than a bare `string` so it joins {@link NotImplemented}
17
+ * and `IoError` as a discriminated union rather than a structural one — a
18
+ * renderer switches on the tag it already switches on, instead of testing
19
+ * `typeof`.
20
+ */
21
+ export type EvoError = readonly ['evoError', string];
22
+ /**
23
+ * The error channel of the Evo API: the revision was rejected, or the runner
24
+ * could not dispatch an operation.
25
+ *
26
+ * Host IO failures do **not** appear here as `IoError`. A store that cannot be
27
+ * read or written is reported as an {@link EvoError} whose message says so,
28
+ * because from this API's outside a blob that cannot be delivered and a blob
29
+ * that is not a revision are the same answer — "no revision at that hash" —
30
+ * and the caller has the same recourse to both.
31
+ */
32
+ export type EvoChannel = EvoError | NotImplemented;
12
33
  /** A cBase32 content hash, as accepted/returned by `Cas<O>`. */
13
34
  export type Hash = string;
14
35
  /** The identity of a mutable object whose revisions are being evolved. */
@@ -38,6 +59,12 @@ export type Subject = string;
38
59
  * - `generation` — input: **ignored**, {@link computeGeneration} derives the
39
60
  * authoritative value from the parents; output: always present. It exists as
40
61
  * an input field only so a read value round-trips into `add` as-is.
62
+ * - `lock` — the format's own field, so it is an inline map or the hash of a
63
+ * `vnd.fjs.lock` blob (`fjs/media/lock`) holding one. Both directions
64
+ * validate and canonicalize every hash it names — the reference itself, or
65
+ * each direct value at every depth of a map. Following a reference is not
66
+ * done here: evo stores and reads revisions, it does not resolve
67
+ * dependencies.
41
68
  *
42
69
  * Relaxing what the format requires is the point, not an oversight. The stored
43
70
  * `vnd.fjs.revision` blob requires `subject`, `snapshot` and `generation`, and
@@ -58,7 +85,7 @@ export type RevisionData = {
58
85
  readonly subject?: Subject | undefined;
59
86
  readonly archived?: true | undefined;
60
87
  readonly generation?: number | undefined;
61
- readonly lock?: LockMap | undefined;
88
+ readonly lock?: LockField | undefined;
62
89
  };
63
90
  /**
64
91
  * Per-subject bookkeeping: every revision hash seen for the subject, every
@@ -94,11 +121,20 @@ export type Evo<O extends Operation> = {
94
121
  * adding it later is a compatible extension of this parameter, while
95
122
  * removing it would not be.
96
123
  */
97
- readonly list: (archived?: true) => Effect<MemOp, readonly Subject[]>;
124
+ readonly list: (archived?: true) => Effect<MemOp, readonly Subject[], NotImplemented>;
98
125
  /** Returns the current head hashes of `subject` (empty if unknown). */
99
- readonly head: (subject: Subject) => Effect<MemOp, readonly Hash[]>;
100
- /** Adds a new head; see {@link addRevision}. */
101
- readonly add: (input: RevisionData) => Effect<O | MemOp, Result<Hash, string>>;
126
+ readonly head: (subject: Subject) => Effect<MemOp, readonly Hash[], NotImplemented>;
127
+ /**
128
+ * Adds a new head; see {@link addRevision}.
129
+ *
130
+ * A rejected revision and an undispatchable operation are both failures and
131
+ * share the channel, as {@link EvoChannel}'s two cases. They stay tellable
132
+ * apart — that is what the tags are for, and a caller that renders them
133
+ * differently switches on one — but they do not need separate *layers* to
134
+ * be told apart, and giving them separate layers costs every intermediate
135
+ * step a hand-written short-circuit that `step` would otherwise do.
136
+ */
137
+ readonly add: (input: RevisionData) => Effect<O | MemOp, Hash, EvoChannel>;
102
138
  /**
103
139
  * The revision at `hash`, decoded, validated, and canonicalized; see
104
140
  * {@link readRevision}. Served from the store today, so the `MemOp` in the
@@ -107,5 +143,5 @@ export type Evo<O extends Operation> = {
107
143
  * operations read, which must not become a breaking change to this type
108
144
  * when it happens.
109
145
  */
110
- readonly revision: (hash: Hash) => Effect<O | MemOp, Result<RevisionData, string>>;
146
+ readonly revision: (hash: Hash) => Effect<O | MemOp, RevisionData, EvoChannel>;
111
147
  };
@@ -5,24 +5,19 @@
5
5
  *
6
6
  * @import { Sha2, State as Sha2State } from '../crypto/sha2/types.ts'
7
7
  * @import { Vec } from '../types/bit_vec/types.ts'
8
- * @import { Effect, Operation } from '../effects/types.ts'
9
- * @import {
10
- * IoResult,
11
- * Now,
12
- * RandomInt,
13
- * ReadBytes,
14
- * Readdir,
15
- * Rm,
16
- * } from '../effects/node/types.ts'
8
+ * @import { Operation } from '../effects/types.ts'
9
+ * @import { Effect, NotImplemented } from '../effects/types.ts'
10
+ * @import { IoChannel, Now, RandomInt, ReadBytes, Readdir, Rm } from '../effects/node/types.ts'
17
11
  * @import { List } from '../effects/list/types.ts'
18
12
  * @import { Cas, FileCas, FileCasOperation } from './types.ts'
19
13
  */
20
14
  import type { Sha2 } from '../crypto/sha2/types.ts';
21
15
  import type { Vec } from '../types/bit_vec/types.ts';
22
- import type { Effect, Operation } from '../effects/types.ts';
23
- import type { IoResult, ReadBytes } from '../effects/node/types.ts';
16
+ import type { Operation } from '../effects/types.ts';
17
+ import type { Effect } from '../effects/types.ts';
18
+ import type { IoChannel, ReadBytes } from '../effects/node/types.ts';
24
19
  import type { List } from '../effects/list/types.ts';
25
- import type { Cas, FileCas, FileCasOperation } from './types.ts';
20
+ import type { Cas, FileCas } from './types.ts';
26
21
  /** Converts a content key to its sharded relative CAS file path.
27
22
  *
28
23
  * @type {(key: Vec) => string}
@@ -31,17 +26,16 @@ export declare const toPath: (key: Vec) => string;
31
26
  /**
32
27
  * Drains a `Cas<O>.read` chunk stream into a single `Vec`. Used by any caller
33
28
  * that needs the whole blob at once (an MCP `content: true` fetch, an Evo
34
- * revision decode): the chunk stream is concatenated and an error item is
35
- * surfaced as the result. A single `Vec` cannot exceed `maxLength` bits;
36
- * concatenating past it would overflow the runtime's `bigint` constraint, so
37
- * that case is surfaced as an error item too, rather than crashing the
38
- * process.
29
+ * revision decode): the chunk stream is concatenated, and a stream that fails
30
+ * fails this. A single `Vec` cannot exceed `maxLength` bits; concatenating past
31
+ * it would overflow the runtime's `bigint` constraint, so that case fails here
32
+ * too rather than crashing the process.
39
33
  *
40
34
  * @template {Operation} O
41
- * @param {List<O, IoResult<Vec>>} stream
42
- * @returns {Effect<O, IoResult<Vec>>}
35
+ * @param {List<O, Vec, IoChannel>} stream
36
+ * @returns {Effect<O, Vec, IoChannel>}
43
37
  */
44
- export declare const collectRead: <O extends Operation>(stream: List<O, IoResult<Vec>>) => Effect<O, IoResult<Vec>>;
38
+ export declare const collectRead: <O extends Operation>(stream: List<O, Vec, IoChannel>) => Effect<O, Vec, IoChannel>;
45
39
  /**
46
40
  * Builds a content-addressable storage facade from a SHA-2 implementation.
47
41
  *
@@ -50,19 +44,11 @@ export declare const collectRead: <O extends Operation>(stream: List<O, IoResult
50
44
  export declare const fileCas: (sha2: Sha2) => (path: string) => FileCas;
51
45
  /**
52
46
  * Streams the file at `path` through `cas.write`, returning the content hash.
53
- * Both the CLI `cas add` and the MCP `add` delegate to this; the MCP layer
54
- * additionally deletes the source file on success.
47
+ * Its one consumer is the CLI's `cas add`; the MCP `cas_add` tool takes inline
48
+ * content and calls `write` itself, so it does not come through here.
55
49
  *
56
50
  * @template {Operation} O
57
51
  * @param {Cas<O>} cas
58
- * @returns {(path: string) => Effect<O | ReadBytes, IoResult<Vec>>}
52
+ * @returns {(path: string) => Effect<O | ReadBytes, Vec, IoChannel>}
59
53
  */
60
- export declare const casAddFile: <O extends Operation>(cas: Cas<O>) => (path: string) => Effect<O | ReadBytes, IoResult<Vec>>;
61
- /**
62
- * Upload pipeline: streams `fileName` from `~/cas_upload/` through `casAddFile`,
63
- * then deletes the source on success. On failure the source is left in place so
64
- * the upload can be retried; `write` already cleans up its own partial staging file.
65
- *
66
- * @type {(home: string) => (fileName: string) => Effect<FileCasOperation, IoResult<Vec>>}
67
- */
68
- export declare const casUpload: (home: string) => (fileName: string) => Effect<FileCasOperation, IoResult<Vec>>;
54
+ export declare const casAddFile: <O extends Operation>(cas: Cas<O>) => (path: string) => Effect<O | ReadBytes, Vec, IoChannel>;
@@ -5,27 +5,33 @@
5
5
  *
6
6
  * @import { Sha2, State as Sha2State } from '../crypto/sha2/types.ts'
7
7
  * @import { Vec } from '../types/bit_vec/types.ts'
8
- * @import { Effect, Operation } from '../effects/types.ts'
9
- * @import {
10
- * IoResult,
11
- * Now,
12
- * RandomInt,
13
- * ReadBytes,
14
- * Readdir,
15
- * Rm,
16
- * } from '../effects/node/types.ts'
8
+ * @import { Operation } from '../effects/types.ts'
9
+ * @import { Effect, NotImplemented } from '../effects/types.ts'
10
+ * @import { IoChannel, Now, RandomInt, ReadBytes, Readdir, Rm } from '../effects/node/types.ts'
17
11
  * @import { List } from '../effects/list/types.ts'
18
12
  * @import { Cas, FileCas, FileCasOperation } from './types.ts'
19
13
  */
20
14
 
21
- import { sha256 } from '../crypto/sha2/module.f.mjs'
22
15
  import { join, normalize, parse } from '../path/module.f.mjs'
23
16
  import { empty, length, maxLength, maxLengthBytes, msb, vec } from '../types/bit_vec/module.f.mjs'
24
17
  import { cBase32ToVec, vecToCBase32 } from '../basen/cbase32/module.f.mjs'
25
- import { foldStep, forEachStep, history, historyStep, mapStep, okStep, pure, step } from '../effects/module.f.mjs'
18
+ import {
19
+ catchStep,
20
+ foldStep,
21
+ forEachStep,
22
+ history,
23
+ historyStep,
24
+ mapStep as ioMapStep,
25
+ pureError,
26
+ pureOk,
27
+ resultMapStep,
28
+ resultStep,
29
+ step as ioStep,
30
+ } from '../effects/module.f.mjs'
26
31
  import {
27
32
  access,
28
33
  createExclusive,
34
+ ioError,
29
35
  isNotFound,
30
36
  mkdir,
31
37
  now,
@@ -38,7 +44,7 @@ import {
38
44
  writeBytes,
39
45
  } from '../effects/node/module.f.mjs'
40
46
  import { toOption } from '../types/nullable/module.f.mjs'
41
- import { error, ok, unwrap } from '../types/result/module.f.mjs'
47
+ import { error, ok } from '../types/result/module.f.mjs'
42
48
  import { splitAt } from '../types/string/module.f.mjs'
43
49
  import { nonEmpty, empty as elEmpty } from '../effects/list/module.f.mjs'
44
50
 
@@ -60,28 +66,25 @@ export const toPath = key => {
60
66
  /**
61
67
  * Drains a `Cas<O>.read` chunk stream into a single `Vec`. Used by any caller
62
68
  * that needs the whole blob at once (an MCP `content: true` fetch, an Evo
63
- * revision decode): the chunk stream is concatenated and an error item is
64
- * surfaced as the result. A single `Vec` cannot exceed `maxLength` bits;
65
- * concatenating past it would overflow the runtime's `bigint` constraint, so
66
- * that case is surfaced as an error item too, rather than crashing the
67
- * process.
69
+ * revision decode): the chunk stream is concatenated, and a stream that fails
70
+ * fails this. A single `Vec` cannot exceed `maxLength` bits; concatenating past
71
+ * it would overflow the runtime's `bigint` constraint, so that case fails here
72
+ * too rather than crashing the process.
68
73
  *
69
74
  * @template {Operation} O
70
- * @param {List<O, IoResult<Vec>>} stream
71
- * @returns {Effect<O, IoResult<Vec>>}
75
+ * @param {List<O, Vec, IoChannel>} stream
76
+ * @returns {Effect<O, Vec, IoChannel>}
72
77
  */
73
78
  export const collectRead = stream => {
74
- /** @type {(acc: Vec) => (s: List<O, IoResult<Vec>>) => Effect<O, IoResult<Vec>>} */
79
+ /** @type {(acc: Vec) => (s: List<O, Vec, IoChannel>) => Effect<O, Vec, IoChannel>} */
75
80
  const loop = acc => s =>
76
- step(s, node => {
77
- if (node === undefined) { return pure(ok(acc)) }
81
+ ioStep(s, node => {
82
+ if (node === undefined) { return pureOk(acc) }
78
83
  const { first, tail } = node
79
- const [t, v] = first
80
- if (t === 'error') { return pure(first) }
81
- if (length(acc) + length(v) > maxLength) {
82
- return pure(error(`cas blob exceeds maximum vector length of ${maxLength} bits`))
84
+ if (length(acc) + length(first) > maxLength) {
85
+ return pureError(ioError({ message: `cas blob exceeds maximum vector length of ${maxLength} bits` }))
83
86
  }
84
- return loop(msb.concat(acc)(v))(tail)
87
+ return loop(msb.concat(acc)(first))(tail)
85
88
  })
86
89
  return loop(empty)(stream)
87
90
  }
@@ -126,9 +129,11 @@ const deadlineOf = name => Number(name.slice(0, name.indexOf('-')))
126
129
  * still-live lease is left alone; the fencing rename keeps even a misjudged
127
130
  * reclaim fail-safe (worst case: that upload restarts).
128
131
  *
129
- * @template {Now | Readdir | Rm} O
130
- * @param {string} stageDir
131
- * @returns {Effect<O, void>}
132
+ * The error channel is `never`, and that is the claim this function makes: it
133
+ * catches every failure it can produce, so an upload composing it cannot fail
134
+ * because of a sweep.
135
+ *
136
+ * @type {(stageDir: string) => Effect<Now | Readdir | Rm, void, never>}
132
137
  */
133
138
  const gcStage = stageDir => {
134
139
  // The `readdir` result and the `now()` timestamp are both needed by the last
@@ -137,16 +142,20 @@ const gcStage = stageDir => {
137
142
  const listed = historyStep(
138
143
  history(now()),
139
144
  () => readdir(stageDir, {}))
140
- return step(
145
+ const swept = ioStep(
141
146
  listed,
142
- ([[k, v], t]) => {
143
- if (k === 'error') { return pure(undefined) }
144
- const expired = v.flatMap(d =>
147
+ ([dirents, t]) => {
148
+ const expired = dirents.flatMap(d =>
145
149
  d.isFile && deadlineOf(d.name) < t ? [d.name] : [])
150
+ // Each `rm` recovers on its own, so one undeletable file does not
151
+ // stop the sweep at the file before it.
146
152
  return forEachStep(
147
- pure(expired),
148
- name => mapStep(rm(join(stageDir, name)), () => undefined))
153
+ pureOk(expired),
154
+ name => catchStep(rm(join(stageDir, name)), () => pureOk(undefined)))
149
155
  })
156
+ // A fresh store has no `_stage/` yet, and a sweep that cannot run must not
157
+ // fail the upload it is piggy-backed on.
158
+ return catchStep(swept, () => pureOk(undefined))
150
159
  }
151
160
 
152
161
  // Lock-free staging upload (plan/staging-lease.md): stream each chunk straight
@@ -162,73 +171,84 @@ const gcStage = stageDir => {
162
171
  * @param {Sha2} sha2
163
172
  * @param {string} path
164
173
  * @param {string} stageDir
165
- * @param {List<O1, IoResult<Vec>>} payload
166
- * @returns {Effect<O1 | FileCasOperation, IoResult<Vec>>}
174
+ * @param {List<O1, Vec, IoChannel>} payload
175
+ * @returns {Effect<O1 | FileCasOperation, Vec, IoChannel>}
167
176
  */
168
177
  const writeImpl = (sha2, path, stageDir, payload) => {
169
178
  // Publish the finished staging file to its content-addressed shard. The three
170
179
  // filesystem steps run best-effort with their results ignored; success is decided
171
180
  // afterward by observing the target's size (see staging-lease.md "Publish ignores
172
181
  // results and checks the end state").
173
- /** @type {(state: Sha2State, offset: number, curPath: string) => Effect<FileCasOperation, IoResult<Vec>>} */
182
+ /** @type {(state: Sha2State, offset: number, curPath: string) => Effect<FileCasOperation, Vec, IoChannel>} */
174
183
  const publish = (state, offset, curPath) => {
175
184
  const hash = sha2.end(state)
176
185
  const rel = toPath(hash)
177
186
  const dst = join(path, rel)
178
187
  const dstDir = join(path, ...parse(rel).slice(0, -1))
179
- const created = step(mkdir(dstDir, { recursive: true }), () => rename(curPath, dst))
180
- const removed = step(created, () => rm(curPath))
181
- const stated = step(removed, () => stat(dst))
182
- return mapStep(
188
+ // `resultStep` throughout, and that is the "ignores results" above
189
+ // written in the type: each link runs whatever the previous one
190
+ // answered, and only the closing `stat` decides the outcome.
191
+ const created = resultStep(mkdir(dstDir, { recursive: true }), () => rename(curPath, dst))
192
+ const removed = resultStep(created, () => rm(curPath))
193
+ const stated = resultStep(removed, () => stat(dst))
194
+ return resultMapStep(
183
195
  stated,
184
- st => st[0] === 'ok' && st[1].size === offset ? ok(hash) : error('publish size mismatch'))
196
+ st => st[0] === 'ok' && st[1].size === offset ? ok(hash) : error(ioError({ message: 'publish size mismatch' })))
185
197
  }
186
198
  // Any streaming error fails closed: delete the partial file, return the error.
187
- /** @type {(curPath: string, e: unknown) => Effect<FileCasOperation, IoResult<Vec>>} */
199
+ /** @type {(curPath: string, e: IoChannel) => Effect<FileCasOperation, Vec, IoChannel>} */
188
200
  const fail = (curPath, e) =>
189
- mapStep(rm(curPath), () => error(e))
190
- const rndEffect = step(gcStage(stageDir), () => random256)
191
- return step(rndEffect, rnd => {
201
+ // The cleanup's own outcome is discarded on purpose: `e` is what the
202
+ // caller needs to hear, and a failed `rm` has no better answer.
203
+ resultMapStep(rm(curPath), () => error(e))
204
+ const rndEffect = ioStep(gcStage(stageDir), () => random256)
205
+ return ioStep(rndEffect, rnd => {
192
206
  const rndStr = vecToCBase32(rnd)
193
- /** @type {(state: Sha2State, offset: number, curPath: string) => (stream: List<O1, IoResult<Vec>>) => Effect<O1 | FileCasOperation, IoResult<Vec>>} */
207
+ /** @type {(state: Sha2State, offset: number, curPath: string) => (stream: List<O1, Vec, IoChannel>) => Effect<O1 | FileCasOperation, Vec, IoChannel>} */
194
208
  const loop = (state, offset, curPath) =>
195
209
  stream =>
196
- step(stream, node => {
210
+ // `resultStep`, not `step`: a stream that fails must still be
211
+ // cleaned up after, so this handles the failure rather than
212
+ // letting it propagate past the partial file.
213
+ resultStep(stream, r => {
214
+ if (r[0] === 'error') {
215
+ return fail(curPath, r[1])
216
+ }
217
+ const node = r[1]
197
218
  if (node === undefined) {
198
219
  return publish(state, offset, curPath)
199
220
  }
200
- const { first, tail } = node
201
- if (first[0] === 'error') {
202
- return fail(curPath, first[1])
203
- }
204
- const chunk = first[1]
205
- return step(writeBytes(curPath, offset, chunk), wb => {
221
+ const { first: chunk, tail } = node
222
+ // Both branches matter: a failed write is cleaned up after,
223
+ // rather than propagated with the partial file left behind.
224
+ return resultStep(writeBytes(curPath, offset, chunk), wb => {
206
225
  if (wb[0] === 'error') { return fail(curPath, wb[1]) }
207
226
  const newState = sha2.append(chunk)(state)
208
227
  const newOffset = offset + Number(length(chunk) / 8n)
209
228
  // Renew the lease: rename to a fresh deadline (keeps `delta` constant).
210
229
  // The new path is still needed after the rename, to recurse with,
211
230
  // so the rename captures it rather than closing over it.
212
- const nextPath = step(
231
+ const nextPath = ioMapStep(
213
232
  now(),
214
- t => pure(join(stageDir, stageName(t + leaseDelta, rndStr))))
233
+ t => join(stageDir, stageName(t + leaseDelta, rndStr)))
215
234
  const renamed = historyStep(
216
235
  history(nextPath),
217
236
  next => rename(curPath, next))
218
- return step(
237
+ return resultStep(
219
238
  renamed,
220
- ([[rt, v], next]) =>
221
- rt === 'error'
222
- ? fail(curPath, v)
223
- : loop(newState, newOffset, next)(tail))
239
+ r => {
240
+ if (r[0] === 'error') { return fail(curPath, r[1]) }
241
+ const [, next] = r[1]
242
+ return loop(newState, newOffset, next)(tail)
243
+ })
224
244
  })
225
245
  })
226
- const started = step(mkdir(stageDir, { recursive: true }), () => now())
227
- return step(started, t0 => {
246
+ const started = ioStep(mkdir(stageDir, { recursive: true }), () => now())
247
+ return ioStep(started, t0 => {
228
248
  const path0 = join(stageDir, stageName(t0 + leaseDelta, rndStr))
229
- return step(
249
+ return ioStep(
230
250
  createExclusive(path0),
231
- okStep(() => loop(sha2.init, 0, path0)(payload)))
251
+ () => loop(sha2.init, 0, path0)(payload))
232
252
  })
233
253
  })
234
254
  }
@@ -245,42 +265,41 @@ export const fileCas = sha2 => path => {
245
265
  return {
246
266
  read: hash => {
247
267
  const p = join(path, toPath(hash))
248
- /** @type {(offset: number) => List<FileCasOperation, IoResult<Vec>>} */
268
+ /** @type {(offset: number) => List<FileCasOperation, Vec, IoChannel>} */
249
269
  const loop = offset =>
250
- step(
270
+ // A missing shard or read error fails the stream, and `step`
271
+ // propagates it — the cell's own failure can never be mistaken
272
+ // for the `undefined` that ends one.
273
+ ioStep(
251
274
  readBytes(p, offset, chunkBytes),
252
- /** @type {(result: IoResult<Vec>) => List<FileCasOperation, IoResult<Vec>>} */ (result) => {
253
- const [t, v] = result
254
- // A missing shard or read error is an explicit error item, never EOF.
255
- if (t === 'error') {
256
- return nonEmpty(result, elEmpty())
257
- }
258
- // End the stream only on an empty read; every non-empty read — including a
259
- // final short (`< CHUNK_BYTES`) chunk — is emitted as an `ok` item.
260
- return length(v) === 0n
261
- ? elEmpty()
262
- : nonEmpty(ok(v), loop(offset + chunkBytes))
263
- })
275
+ // End the stream only on an empty read; every non-empty read — including a
276
+ // final short (`< CHUNK_BYTES`) chunk — is emitted as a cell.
277
+ chunk => length(chunk) === 0n
278
+ ? elEmpty()
279
+ : nonEmpty(chunk, loop(offset + chunkBytes)))
264
280
  return loop(0)
265
281
  },
266
282
  write: payload => writeImpl(sha2, path, stageDir, payload),
267
283
  list: () =>
268
284
  // A fresh store has no `.cas` directory yet. Treat *only* that case as an
269
- // empty store, mirroring how `read` maps a missing shard to an error item.
285
+ // empty store, mirroring how `read` fails a stream on a missing shard.
270
286
  // A `.cas` that exists but cannot be read (permissions, corruption) is a
271
287
  // genuine storage error and is surfaced, not masked as "no hashes".
272
- step(access(storePrefix), a => {
273
- if (a[0] === 'error') {
274
- if (isNotFound(a[1])) { return pure(/** @type {readonly Vec[]} */ ([])) }
275
- throw a[1]
276
- }
277
- return mapStep(
278
- readdir(storePrefix, { recursive: true }),
279
- r => unwrap(r).flatMap(({ name, parentPath, isFile }) =>
280
- toOption(isFile
281
- ? cBase32ToVec(normalize(parentPath).substring(normalizedStorePrefix.length).replaceAll('/', '') + name)
282
- : null)))
283
- }),
288
+ //
289
+ // `resultStep` because both branches genuinely matter: the absent
290
+ // store is a success answering `ok([])`, and every other failure is
291
+ // the caller's to see. It used to `throw` here, which is what the
292
+ // paragraph above always meant by "surfaced" but could not say
293
+ // while the return type had no error channel to say it in.
294
+ resultStep(access(storePrefix), a =>
295
+ a[0] === 'error'
296
+ ? isNotFound(a[1]) ? pureOk([]) : pureError(a[1])
297
+ : ioMapStep(
298
+ readdir(storePrefix, { recursive: true }),
299
+ r => r.flatMap(({ name, parentPath, isFile }) =>
300
+ toOption(isFile
301
+ ? cBase32ToVec(normalize(parentPath).substring(normalizedStorePrefix.length).replaceAll('/', '') + name)
302
+ : null)))),
284
303
  url: hash =>
285
304
  join(path, toPath(hash))
286
305
  }
@@ -288,44 +307,41 @@ export const fileCas = sha2 => path => {
288
307
 
289
308
  /** 256-bit random `Vec` built from 8 sequential `randomInt` (32-bit) calls.
290
309
  *
291
- * @type {Effect<RandomInt, Vec>}
310
+ * @type {Effect<RandomInt, Vec, NotImplemented>}
292
311
  */
293
312
  const random256 =
294
313
  foldStep(
295
- pure([0, 1, 2, 3, 4, 5, 6, 7]),
314
+ pureOk([0, 1, 2, 3, 4, 5, 6, 7]),
296
315
  empty,
297
316
  () => (/** @type {Vec} */ acc) =>
298
- mapStep(randomInt(), r => msb.concat(acc)(vec(32n)(BigInt(r)))))
317
+ ioMapStep(randomInt(), r => msb.concat(acc)(vec(32n)(BigInt(r)))))
299
318
 
300
- /** Streams any file at `filePath` in `<=128 KiB` chunks as a `ListEffect` of `ok` items.
319
+ /** Streams any file at `filePath` in `<=128 KiB` chunks.
301
320
  *
302
- * @type {(filePath: string) => List<ReadBytes, IoResult<Vec>>}
321
+ * A failed read fails the stream. It used to be yielded as an error *item*
322
+ * followed by an explicit empty tail — a tail no consumer would ever pull.
323
+ *
324
+ * @type {(filePath: string) => List<ReadBytes, Vec, IoChannel>}
303
325
  */
304
326
  const streamFile = filePath => {
305
- /** @type {(offset: number) => List<ReadBytes, IoResult<Vec>>} */
327
+ /** @type {(offset: number) => List<ReadBytes, Vec, IoChannel>} */
306
328
  const loop = offset =>
307
- step(
329
+ ioStep(
308
330
  readBytes(filePath, offset, chunkBytes),
309
- /** @type {(result: IoResult<Vec>) => List<ReadBytes, IoResult<Vec>>} */ (result) => {
310
- if (result[0] === 'error') {
311
- return nonEmpty(result, elEmpty())
312
- }
313
- const chunk = result[1]
314
- return length(chunk) === 0n
315
- ? elEmpty()
316
- : nonEmpty(ok(chunk), loop(offset + chunkBytes))
317
- })
331
+ chunk => length(chunk) === 0n
332
+ ? elEmpty()
333
+ : nonEmpty(chunk, loop(offset + chunkBytes)))
318
334
  return loop(0)
319
335
  }
320
336
 
321
337
  /**
322
338
  * Streams the file at `path` through `cas.write`, returning the content hash.
323
- * Both the CLI `cas add` and the MCP `add` delegate to this; the MCP layer
324
- * additionally deletes the source file on success.
339
+ * Its one consumer is the CLI's `cas add`; the MCP `cas_add` tool takes inline
340
+ * content and calls `write` itself, so it does not come through here.
325
341
  *
326
342
  * @template {Operation} O
327
343
  * @param {Cas<O>} cas
328
- * @returns {(path: string) => Effect<O | ReadBytes, IoResult<Vec>>}
344
+ * @returns {(path: string) => Effect<O | ReadBytes, Vec, IoChannel>}
329
345
  */
330
346
  export const casAddFile = cas => path =>
331
347
  // streamFile produces only ReadBytes effects. TypeScript can't prove ListEffect<ReadBytes,T>
@@ -333,18 +349,3 @@ export const casAddFile = cas => path =>
333
349
  // caller passes a Cas<O> where ReadBytes ⊆ O (e.g. FileCasOperation).
334
350
  cas.write(streamFile(path))
335
351
 
336
- /**
337
- * Upload pipeline: streams `fileName` from `~/cas_upload/` through `casAddFile`,
338
- * then deletes the source on success. On failure the source is left in place so
339
- * the upload can be retried; `write` already cleans up its own partial staging file.
340
- *
341
- * @type {(home: string) => (fileName: string) => Effect<FileCasOperation, IoResult<Vec>>}
342
- */
343
- export const casUpload = home => fileName => {
344
- const src = join(home, 'cas_upload', fileName)
345
- const c = fileCas(sha256)(home)
346
- return step(
347
- casAddFile(c)(src),
348
- okStep(/** @type {(v: Vec) => Effect<Rm, IoResult<Vec>>} */ (v =>
349
- mapStep(rm(src), () => ok(v)))))
350
- }
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * @import { Vec } from '../types/bit_vec/types.ts'
3
3
  * @import { FileCasOperation } from './types.ts'
4
+ * @import { IoChannel, IoResult, Mkdir, ReadFile, WriteFile } from '../effects/node/types.ts'
4
5
  * @import { Effect } from '../effects/types.ts'
5
- * @import { ReadFile, WriteFile, Rm, Mkdir, IoResult } from '../effects/node/types.ts'
6
- * @import { Ok } from '../types/result/types.ts'
6
+ * @import { Ok, Result } from '../types/result/types.ts'
7
7
  * @import { List } from '../effects/list/types.ts'
8
8
  */
9
9
  import type { FileCasOperation } from './types.ts';
10
- import type { ReadFile, WriteFile, Rm, Mkdir } from '../effects/node/types.ts';
11
- export type _TestOp = FileCasOperation | WriteFile | ReadFile | Rm | Mkdir;
10
+ import type { Mkdir, ReadFile, WriteFile } from '../effects/node/types.ts';
11
+ export type _TestOp = FileCasOperation | WriteFile | ReadFile | Mkdir;
12
12
  export declare const proof: {
13
13
  addBigFile: () => void;
14
14
  addAndGetBigFile: () => void;
@@ -16,14 +16,17 @@ export declare const proof: {
16
16
  casReadMissingShard: () => void;
17
17
  casWriteMultiChunk: () => void;
18
18
  casWriteDedup: () => void;
19
- casWriteErrorItemAborts: () => void;
19
+ casWriteStreamFailureAborts: () => void;
20
20
  casWriteReadExceedsMaxLength: () => void;
21
21
  casWriteGcReclaimsExpired: () => void;
22
+ casWriteGcRmFailureDoesNotFailTheUpload: () => void;
22
23
  casWriteGcSkipsLiveLease: () => void;
23
- casUploadSuccess: () => void;
24
- casUploadFailureKeepsSource: () => void;
24
+ casWriteBytesErrorAborts: () => void;
25
+ casWriteLeaseRenewalRenameErrorAborts: () => void;
26
+ casWritePublishSizeMismatchErrors: () => void;
27
+ casWritePublishStatErrorErrorsEvenWithMatchingSize: () => void;
25
28
  collectReadDrainsChunks: () => void;
26
- collectReadPropagatesErrorItem: () => void;
29
+ collectReadPropagatesStreamFailure: () => void;
27
30
  collectReadOverflow: () => void;
28
31
  casListPropagatesNonNotFoundAccessError: () => void;
29
32
  };