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
@@ -6,7 +6,7 @@
6
6
  import type { Vec } from '../types/bit_vec/types.ts';
7
7
  import type { Effect, Operation } from '../effects/types.ts';
8
8
  import type { List } from '../effects/list/types.ts';
9
- import type { IoResult, Mkdir, Now, RandomInt, ReadBytes, Readdir, Rename, Rm, CreateExclusive, WriteBytes, Access, Stat } from '../effects/node/types.ts';
9
+ import type { Access, CreateExclusive, IoChannel, Mkdir, Now, RandomInt, ReadBytes, Readdir, Rename, Rm, Stat, WriteBytes } from '../effects/node/types.ts';
10
10
  /**
11
11
  * The filesystem effects the streaming CAS performs: `read` pulls shards
12
12
  * (`ReadBytes`); `list` walks the store (`Access`/`Readdir`); `write` runs the
@@ -17,18 +17,32 @@ import type { IoResult, Mkdir, Now, RandomInt, ReadBytes, Readdir, Rename, Rm, C
17
17
  export type FileCasOperation = ReadBytes | Mkdir | Readdir | Access | Rename | Rm | RandomInt | Now | CreateExclusive | WriteBytes | Stat | Now | Readdir | Rm;
18
18
  export type Cas<O extends Operation> = {
19
19
  /**
20
- * Streams the content for `hash` out in `<=128 KiB` chunks. Every pull yields an
21
- * explicit `ok(chunk)` or `error` item, so a missing shard or I/O error is a distinct
22
- * error *item* in the stream, never collapsed into end-of-stream (`undefined`).
20
+ * Streams the content for `hash` out in `<=128 KiB` chunks.
21
+ *
22
+ * A missing shard or an I/O error **fails the stream** rather than ending
23
+ * it, so it can never be mistaken for end-of-stream. That distinction used
24
+ * to be a rule this comment had to state, with an `ok(chunk)`/`error` item
25
+ * union that every consumer kept apart by hand; it is now the difference
26
+ * between an `error` cell and an `ok(undefined)` one, which nothing can
27
+ * confuse.
23
28
  */
24
- readonly read: (hash: Vec) => List<O, IoResult<Vec>>;
29
+ readonly read: (hash: Vec) => List<O, Vec, IoChannel>;
25
30
  /**
26
- * Consumes a chunk stream each item `ok(chunk)` or `error` — hashing incrementally,
27
- * and returns the content address. An error item aborts the upload.
31
+ * Consumes a chunk stream, hashing incrementally, and returns the content
32
+ * address. A stream that fails aborts the upload with that failure.
28
33
  */
29
- readonly write: <O1 extends Operation>(payload: List<O1, IoResult<Vec>>) => Effect<O | O1, IoResult<Vec>>;
30
- /** Lists all stored content hashes. */
31
- readonly list: () => Effect<O, readonly Vec[]>;
34
+ readonly write: <O1 extends Operation>(payload: List<O1, Vec, IoChannel>) => Effect<O | O1, Vec, IoChannel>;
35
+ /**
36
+ * Lists all stored content hashes.
37
+ *
38
+ * Fallible like its siblings: walking the store is IO, and a store that
39
+ * cannot be walked must be distinguishable from one that is empty. An
40
+ * absent store *is* empty and answers `ok([])` — that is the fresh-store
41
+ * case, not a failure — but a directory that exists and cannot be read is
42
+ * an error, and returning it is what lets a caller say so instead of
43
+ * reporting no content.
44
+ */
45
+ readonly list: () => Effect<O, readonly Vec[], IoChannel>;
32
46
  };
33
47
  export type FileCas = Cas<FileCasOperation> & {
34
48
  url: (v: Vec) => string;
@@ -18,7 +18,7 @@ export declare const images: {
18
18
  readonly arm: 'windows-11-arm';
19
19
  };
20
20
  };
21
- export declare const functionalscript = /** @type {const} */ "0.44.0";
21
+ export declare const functionalscript = /** @type {const} */ "0.45.0";
22
22
  export declare const bun = "1.3.14";
23
23
  export declare const deno = "2.9.5";
24
24
  export declare const node: {
@@ -28,7 +28,7 @@ export declare const node: {
28
28
  };
29
29
  export declare const nixpkgs: {
30
30
  readonly ref: 'nixos-26.05';
31
- readonly commit: '9f78f44a87948854445dae0b6bf82b2e87e4efb5';
31
+ readonly commit: '02e08985a27c65ffd33d434eeb2e660a2e4dc84d';
32
32
  };
33
33
  export declare const wasmtime = "47.0.3";
34
34
  export declare const wasmer = "7.2.1";
@@ -25,7 +25,7 @@ export const images = /** @type {const} */({
25
25
  // published FunctionalScript release; do not tie it to package.json's current
26
26
  // in-repo version.
27
27
  // https://www.npmjs.com/package/functionalscript
28
- export const functionalscript = /** @type {const} */ '0.44.0'
28
+ export const functionalscript = /** @type {const} */ '0.45.0'
29
29
 
30
30
  // https://bun.sh/
31
31
  export const bun = '1.3.14'
@@ -53,7 +53,7 @@ export const node = /** @type {const} */({
53
53
  // https://channels.nixos.org/nixos-26.05/git-revision
54
54
  export const nixpkgs = /** @type {const} */({
55
55
  ref: 'nixos-26.05',
56
- commit: '9f78f44a87948854445dae0b6bf82b2e87e4efb5',
56
+ commit: '02e08985a27c65ffd33d434eeb2e660a2e4dc84d',
57
57
  })
58
58
 
59
59
  // https://github.com/bytecodealliance/wasmtime/releases
@@ -5,15 +5,15 @@
5
5
  *
6
6
  * @module
7
7
  *
8
- * @import { Effect } from '../effects/types.ts'
9
8
  * @import { NodeOp } from '../effects/node/types.ts'
10
9
  * @import { Architecture, GitHubAction, Job, Jobs, MetaStep, Os } from './common/types.ts'
11
10
  * @import { NixJob } from './nix/types.ts'
12
11
  * @import { Setup } from './types.ts'
12
+ * @import { Effect } from '../effects/types.ts'
13
13
  */
14
- import type { Effect } from '../effects/types.ts';
15
14
  import type { NodeOp } from '../effects/node/types.ts';
16
15
  import type { Setup } from './types.ts';
17
- /** @type {(setup: Setup) => Effect<NodeOp, number>} */
18
- export declare const ci: (setup: Setup) => Effect<NodeOp, number>;
19
- export declare const main: () => Effect<NodeOp, number>;
16
+ import type { Effect } from '../effects/types.ts';
17
+ /** @type {(setup: Setup) => Effect<NodeOp, 0, number>} */
18
+ export declare const ci: (setup: Setup) => Effect<NodeOp, 0, number>;
19
+ export declare const main: () => Effect<NodeOp, 0, number>;
@@ -5,15 +5,16 @@
5
5
  *
6
6
  * @module
7
7
  *
8
- * @import { Effect } from '../effects/types.ts'
9
8
  * @import { NodeOp } from '../effects/node/types.ts'
10
9
  * @import { Architecture, GitHubAction, Job, Jobs, MetaStep, Os } from './common/types.ts'
11
10
  * @import { NixJob } from './nix/types.ts'
12
11
  * @import { Setup } from './types.ts'
12
+ * @import { Effect } from '../effects/types.ts'
13
13
  */
14
14
 
15
- import { mapStep, step } from '../effects/module.f.mjs'
16
- import { access, writeUtf8File } from '../effects/node/module.f.mjs'
15
+ import { resultStep } from '../effects/module.f.mjs'
16
+ import { access, exitStep, writeUtf8File } from '../effects/node/module.f.mjs'
17
+ import { step as ioStep } from '../effects/module.f.mjs'
17
18
  import { functionalscript, images } from './config/module.f.mjs'
18
19
  import {
19
20
  architecture,
@@ -57,8 +58,8 @@ const canonicalJobs = rust => ({
57
58
  'nix-flakes': nixFlakeJob,
58
59
  })
59
60
 
60
- /** @type {(setup: Setup) => Effect<NodeOp, number>} */
61
- export const ci = ({ nodeExtra }) => step(
61
+ /** @type {(setup: Setup) => Effect<NodeOp, 0, number>} */
62
+ export const ci = ({ nodeExtra }) => resultStep(
62
63
  access('Cargo.toml'),
63
64
  result => {
64
65
  const rust = result[0] === 'ok'
@@ -82,8 +83,8 @@ export const ci = ({ nodeExtra }) => step(
82
83
  const workflowWritten = writeUtf8File(
83
84
  '.github/workflows/ci.yml',
84
85
  JSON.stringify(gha, null, ' '))
85
- const flakesWritten = step(workflowWritten, () => nixFlakes(nixJobs))
86
- return mapStep(flakesWritten, () => 0)
86
+ const flakesWritten = ioStep(workflowWritten, () => nixFlakes(nixJobs))
87
+ return exitStep(flakesWritten)
87
88
  })
88
89
 
89
90
  export const main = () => ci({ nodeExtra: () => [] })
@@ -11,14 +11,14 @@
11
11
  *
12
12
  * @module
13
13
  *
14
+ * @import { IoChannel, Mkdir, WriteFile } from '../../effects/node/types.ts'
14
15
  * @import { Effect } from '../../effects/types.ts'
15
- * @import { Mkdir, WriteFile } from '../../effects/node/types.ts'
16
16
  * @import { Expression } from '../../media/nix/types.ts'
17
17
  * @import { MetaStep } from '../common/types.ts'
18
18
  * @import { NixJob } from './types.ts'
19
19
  */
20
+ import type { IoChannel, Mkdir, WriteFile } from '../../effects/node/types.ts';
20
21
  import type { Effect } from '../../effects/types.ts';
21
- import type { Mkdir, WriteFile } from '../../effects/node/types.ts';
22
22
  import type { MetaStep } from '../common/types.ts';
23
23
  import type { NixJob } from './types.ts';
24
24
  /** Directory owned by this generator. */
@@ -35,16 +35,18 @@ export declare const generatedDirectory: 'nix/generated';
35
35
  */
36
36
  export declare const flakeText: (job: NixJob) => string;
37
37
  /**
38
- * Writes one generated flake per job.
38
+ * Writes one generated flake per job, stopping at the first failure.
39
39
  *
40
- * @type {(jobs: readonly NixJob[]) => Effect<Mkdir | WriteFile, void>}
40
+ * @type {(jobs: readonly NixJob[]) => Effect<Mkdir | WriteFile, void, IoChannel>}
41
41
  */
42
- export declare const nixFlakes: (jobs: readonly NixJob[]) => Effect<Mkdir | WriteFile, void>;
42
+ export declare const nixFlakes: (jobs: readonly NixJob[]) => Effect<Mkdir | WriteFile, void, IoChannel>;
43
43
  /** Path a workflow passes to `nix develop`, for the job of the given id. */
44
+ /** @type {(id: string) => string} */
44
45
  export declare const flakePath: (id: string) => string;
45
46
  /** Installs Nix, with `nix-command` and `flakes` enabled by the action's defaults. */
46
47
  export declare const nixInstall: MetaStep;
47
48
  /** Runs one command inside a job's generated development shell. */
49
+ /** @type {(id: string, command: string) => string} */
48
50
  export declare const nixDevelop: (id: string, command: string) => string;
49
51
  /**
50
52
  * Runs a migrated job's whole command sequence in one development shell, so the
@@ -11,15 +11,16 @@
11
11
  *
12
12
  * @module
13
13
  *
14
+ * @import { IoChannel, Mkdir, WriteFile } from '../../effects/node/types.ts'
14
15
  * @import { Effect } from '../../effects/types.ts'
15
- * @import { Mkdir, WriteFile } from '../../effects/node/types.ts'
16
16
  * @import { Expression } from '../../media/nix/types.ts'
17
17
  * @import { MetaStep } from '../common/types.ts'
18
18
  * @import { NixJob } from './types.ts'
19
19
  */
20
20
 
21
- import { forEachStep, mapStep, pure, step } from '../../effects/module.f.mjs'
21
+ import { pureOk } from '../../effects/module.f.mjs'
22
22
  import { mkdir, writeUtf8File } from '../../effects/node/module.f.mjs'
23
+ import { forEachStep, step } from '../../effects/module.f.mjs'
23
24
  import { nixToString } from '../../media/nix/module.f.mjs'
24
25
  import { fromUndefined, unwrap as unwrapNullable } from '../../types/nullable/module.f.mjs'
25
26
  import { unwrap } from '../../types/result/module.f.mjs'
@@ -72,33 +73,33 @@ const flake = ({ system, packages, shellHook }) => ['set',
72
73
  export const flakeText = job =>
73
74
  unwrapNullable(fromUndefined(nixToString(flake(job))))
74
75
 
75
- /** @type {(job: NixJob) => Effect<Mkdir | WriteFile, void>} */
76
+ /** @type {(job: NixJob) => Effect<Mkdir | WriteFile, void, IoChannel>} */
76
77
  const writeFlake = job => {
77
78
  const directory = `${generatedDirectory}/${job.id}`
78
- const created = mapStep(mkdir(directory, { recursive: true }), unwrap)
79
- const written = step(
79
+ const created = mkdir(directory, { recursive: true })
80
+ return step(
80
81
  created,
81
82
  () => writeUtf8File(`${directory}/flake.nix`, flakeText(job)))
82
- return mapStep(written, unwrap)
83
83
  }
84
84
 
85
85
  /**
86
- * Writes one generated flake per job.
86
+ * Writes one generated flake per job, stopping at the first failure.
87
87
  *
88
- * @type {(jobs: readonly NixJob[]) => Effect<Mkdir | WriteFile, void>}
88
+ * @type {(jobs: readonly NixJob[]) => Effect<Mkdir | WriteFile, void, IoChannel>}
89
89
  */
90
90
  export const nixFlakes = jobs =>
91
- forEachStep(pure(jobs), writeFlake)
91
+ forEachStep(pureOk(jobs), writeFlake)
92
92
 
93
93
  /** Path a workflow passes to `nix develop`, for the job of the given id. */
94
- export const flakePath = /** @type {(id: string) => string} */ (id => `./${generatedDirectory}/${id}`)
94
+ /** @type {(id: string) => string} */
95
+ export const flakePath = id => `./${generatedDirectory}/${id}`
95
96
 
96
97
  /** Installs Nix, with `nix-command` and `flakes` enabled by the action's defaults. */
97
98
  export const nixInstall = install(uses('cachix/install-nix-action'))
98
99
 
99
100
  /** Runs one command inside a job's generated development shell. */
100
- export const nixDevelop = /** @type {(id: string, command: string) => string} */
101
- ((id, command) => `nix develop ${flakePath(id)} --command ${command}`)
101
+ /** @type {(id: string, command: string) => string} */
102
+ export const nixDevelop = (id, command) => `nix develop ${flakePath(id)} --command ${command}`
102
103
 
103
104
  /**
104
105
  * Wraps a string so a POSIX shell reproduces it exactly. Single quotes protect
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { assert, assertEq } from '../../asserts/module.f.mjs'
10
- import { step } from '../../effects/module.f.mjs'
10
+ import { step as ioStep } from '../../effects/module.f.mjs'
11
11
  import { readUtf8File } from '../../effects/node/module.f.mjs'
12
12
  import { emptyState, virtual } from '../../effects/node/virtual/module.f.mjs'
13
13
  import { nixpkgs } from '../config/module.f.mjs'
@@ -74,7 +74,7 @@ const shellHookFlake = `{
74
74
 
75
75
  /** @type {(jobs: readonly NixJob[], id: string) => string} */
76
76
  const generated = (jobs, id) => {
77
- const written = step(
77
+ const written = ioStep(
78
78
  nixFlakes(jobs),
79
79
  () => readUtf8File(`${generatedDirectory}/${id}/flake.nix`))
80
80
  const [, [tag, result]] = virtual(emptyState)(written)
@@ -3,6 +3,7 @@
3
3
  * @import { Dir, State } from '../effects/node/virtual/types.ts'
4
4
  */
5
5
 
6
+ import { exitCode } from '../effects/node/module.f.mjs'
6
7
  import { ci, main } from './module.f.mjs'
7
8
  import { functionalscript, node } from './config/module.f.mjs'
8
9
  import { nodeNixJobs } from './node/module.f.mjs'
@@ -39,8 +40,11 @@ const makeState = (/** @type {boolean} */ rust, /** @type {string | undefined} *
39
40
  /** @type {(dir: Dir, name: string) => Dir} */
40
41
  const subDir = (dir, name) => {
41
42
  const entity = dir[name]
42
- assert(typeof entity === 'object' && !Array.isArray(entity), entity)
43
- return /** @type {Dir} */ (entity)
43
+ // `Array.isArray` narrows to `any[]`, which `readonly Vec[]` is not assignable
44
+ // to, so its negative branch never removes a `readonly` array from a union.
45
+ // `instanceof Array` does, so this `assert` narrows `entity` to `Dir`.
46
+ assert(typeof entity === 'object' && !(entity instanceof Array), entity)
47
+ return entity
44
48
  }
45
49
 
46
50
  /** @type {(dir: Dir, name: string) => string} */
@@ -66,14 +70,14 @@ const flake = (state, id) =>
66
70
  /** @type {(rust: boolean, nodeExtra?: (o: Os) => readonly MetaStep[]) => GitHubAction} */
67
71
  const run = (rust, nodeExtra = () => []) => {
68
72
  const [state, result] = virtual(makeState(rust, undefined))(ci({ nodeExtra }))
69
- assertEq(result, 0)
73
+ assertEq(exitCode(result), 0)
70
74
  return workflow(state)
71
75
  }
72
76
 
73
77
  /** @type {(packageJson?: string) => GitHubAction} */
74
78
  const runDefault = packageJson => {
75
79
  const [state, result] = virtual(makeState(false, packageJson))(main())
76
- assertEq(result, 0)
80
+ assertEq(exitCode(result), 0)
77
81
  return workflow(state)
78
82
  }
79
83
 
@@ -175,7 +179,7 @@ export const proof = {
175
179
  },
176
180
  nixFlakes: () => {
177
181
  const [state, result] = virtual(makeState(false, undefined))(main())
178
- assertEq(result, 0)
182
+ assertEq(exitCode(result), 0)
179
183
  for (const { id, packages } of nodeNixJobs) {
180
184
  const [nodePackage] = packages
181
185
  assert(
@@ -5,12 +5,10 @@
5
5
  *
6
6
  * @module
7
7
  *
8
- * @import { NodeOp, NodeProgramOptions, Write } from '../effects/node/types.ts'
9
- * @import { Effect } from '../effects/types.ts'
8
+ * @import { NodeOp, Program, Write } from '../effects/node/types.ts'
10
9
  * @import { Commands } from './types.ts'
11
10
  */
12
- import type { NodeOp, NodeProgramOptions, Write } from '../effects/node/types.ts';
13
- import type { Effect } from '../effects/types.ts';
11
+ import type { NodeOp, Program, Write } from '../effects/node/types.ts';
14
12
  import type { Commands } from './types.ts';
15
- /** @type {<O extends NodeOp>(commands: Commands<O>) => (options: NodeProgramOptions) => Effect<O | Write, number>} */
16
- export declare const dispatch: <O extends NodeOp>(commands: Commands<O>) => (options: NodeProgramOptions) => Effect<O | Write, number>;
13
+ /** @type {<O extends NodeOp>(commands: Commands<O>) => Program<O | Write>} */
14
+ export declare const dispatch: <O extends NodeOp>(commands: Commands<O>) => Program<O | Write>;
@@ -5,18 +5,16 @@
5
5
  *
6
6
  * @module
7
7
  *
8
- * @import { NodeOp, NodeProgramOptions, Write } from '../effects/node/types.ts'
9
- * @import { Effect } from '../effects/types.ts'
8
+ * @import { NodeOp, Program, Write } from '../effects/node/types.ts'
10
9
  * @import { Commands } from './types.ts'
11
10
  */
12
11
 
13
- import { errorExit, log } from '../effects/node/module.f.mjs'
14
- import { pure, step } from '../effects/module.f.mjs'
12
+ import { errorExit, exitStep, log } from '../effects/node/module.f.mjs'
15
13
  import { at, fromEntries } from '../types/object/module.f.mjs'
16
14
 
17
15
  const helpMeta = { names: ['help', 'h', '?'], description: 'Print this help message' }
18
16
 
19
- /** @type {<O extends NodeOp>(commands: Commands<O>) => (options: NodeProgramOptions) => Effect<O | Write, number>} */
17
+ /** @type {<O extends NodeOp>(commands: Commands<O>) => Program<O | Write>} */
20
18
  export const dispatch = commands => options => {
21
19
  const [cmd, ...rest] = options.args
22
20
  const rows = [...commands, helpMeta]
@@ -38,9 +36,7 @@ export const dispatch = commands => options => {
38
36
  return dispatch(targetCmd.handler)({ ...options, args: ['help'] })
39
37
  }
40
38
  }
41
- return step(
42
- log(helpText),
43
- () => pure(0))
39
+ return exitStep(log(helpText))
44
40
  }
45
41
  const found = at(cmd)(map)
46
42
  if (found === null) {
@@ -3,7 +3,8 @@
3
3
  * @import { Commands } from './types.ts'
4
4
  */
5
5
 
6
- import { pure } from '../effects/module.f.mjs'
6
+ import { exitCode } from '../effects/node/module.f.mjs'
7
+ import { pureError, pureOk } from '../effects/module.f.mjs'
7
8
  import { defaultNodeProgramOptions, emptyState, virtual } from '../effects/node/virtual/module.f.mjs'
8
9
  import { dispatch } from './module.f.mjs'
9
10
  import { assert, assertEq } from '../asserts/module.f.mjs'
@@ -17,7 +18,7 @@ const echoCommands = [
17
18
  {
18
19
  names: ['echo', 'e'],
19
20
  description: 'Print the first argument',
20
- handler: ({ args: [arg = ''] }) => pure(arg.length),
21
+ handler: ({ args: [arg = ''] }) => arg.length === 0 ? pureOk(0) : pureError(arg.length),
21
22
  },
22
23
  ]
23
24
 
@@ -27,25 +28,25 @@ const run = (/** @type {Commands<NodeOp>} */ commands) => (/** @type {readonly s
27
28
  export const proof = {
28
29
  knownCommand: () => {
29
30
  const [, code] = run(echoCommands)(['echo', 'hello'])
30
- assertEq(code, 5, ['expected length 5', code])
31
+ assertEq(exitCode(code), 5, ['expected length 5', code])
31
32
  },
32
33
  alias: () => {
33
34
  const [, code] = run(echoCommands)(['e', 'hi'])
34
- assertEq(code, 2, ['expected length 2', code])
35
+ assertEq(exitCode(code), 2, ['expected length 2', code])
35
36
  },
36
37
  noArgs: () => {
37
38
  const [state, code] = run(echoCommands)([])
38
- assertEq(code, 1, ['expected exit 1', code])
39
+ assertEq(exitCode(code), 1, ['expected exit 1', code])
39
40
  assert(state.stderr.length !== 0, 'expected error in stderr')
40
41
  },
41
42
  unknownCommand: () => {
42
43
  const [state, code] = run(echoCommands)(['bogus'])
43
- assertEq(code, 1, ['expected exit 1', code])
44
+ assertEq(exitCode(code), 1, ['expected exit 1', code])
44
45
  assert(state.stderr.length !== 0, 'expected error in stderr')
45
46
  },
46
47
  help: () => {
47
48
  const [state, code] = run(echoCommands)(['help'])
48
- assertEq(code, 0, ['expected exit 0', code])
49
+ assertEq(exitCode(code), 0, ['expected exit 0', code])
49
50
  assert(state.stdout.includes('echo'), 'expected command name in stdout')
50
51
  assert(state.stdout.includes('help'), 'expected help entry in stdout')
51
52
  },
@@ -62,7 +63,7 @@ export const proof = {
62
63
  description: 'Capture args',
63
64
  handler: (/** @type {{ args: readonly string[] }} */ { args }) => {
64
65
  captured.push(...args)
65
- return pure(0)
66
+ return pureOk(0)
66
67
  },
67
68
  }]
68
69
  run(commands)(['grab', 'a', 'b', 'c'])
@@ -73,7 +74,7 @@ export const proof = {
73
74
  const inner = [{
74
75
  names: ['ping'],
75
76
  description: 'Inner ping',
76
- handler: () => pure(42),
77
+ handler: () => pureError(42),
77
78
  }]
78
79
  /** @type {Commands<NodeOp>} */
79
80
  const outer = [{
@@ -82,14 +83,14 @@ export const proof = {
82
83
  handler: inner,
83
84
  }]
84
85
  const [, code] = run(outer)(['sub', 'ping'])
85
- assertEq(code, 42, ['expected 42', code])
86
+ assertEq(exitCode(code), 42, ['expected 42', code])
86
87
  },
87
88
  nestedHelp: () => {
88
89
  /** @type {Commands<NodeOp>} */
89
90
  const inner = [{
90
91
  names: ['ping'],
91
92
  description: 'Inner ping',
92
- handler: () => pure(0),
93
+ handler: () => pureOk(0),
93
94
  }]
94
95
  /** @type {Commands<NodeOp>} */
95
96
  const outer = [{
@@ -98,17 +99,17 @@ export const proof = {
98
99
  handler: inner,
99
100
  }]
100
101
  const [state, code] = run(outer)(['sub', 'help'])
101
- assertEq(code, 0, ['expected exit 0', code])
102
+ assertEq(exitCode(code), 0, ['expected exit 0', code])
102
103
  assert(state.stdout.includes('ping'), 'expected inner command in help')
103
104
  },
104
105
  prototypeNameIsUnknownCommand: () => {
105
106
  const [state, code] = run(echoCommands)(['toString'])
106
- assertEq(code, 1, ['expected exit 1 for prototype-name command', code])
107
+ assertEq(exitCode(code), 1, ['expected exit 1 for prototype-name command', code])
107
108
  assert(state.stderr.includes('unknown command'), 'expected unknown-command error in stderr')
108
109
  },
109
110
  helpPrototypeSafe: () => {
110
111
  const [state, code] = run(echoCommands)(['help', 'toString'])
111
- assertEq(code, 0, ['expected exit 0, not a throw', code])
112
+ assertEq(exitCode(code), 0, ['expected exit 0, not a throw', code])
112
113
  assert(state.stdout.includes('echo'), 'expected top-level help for unknown target')
113
114
  },
114
115
  helpWithTarget: () => {
@@ -116,7 +117,7 @@ export const proof = {
116
117
  const inner = [{
117
118
  names: ['ping'],
118
119
  description: 'Inner ping',
119
- handler: () => pure(0),
120
+ handler: () => pureOk(0),
120
121
  }]
121
122
  /** @type {Commands<NodeOp>} */
122
123
  const outer = [{
@@ -125,7 +126,7 @@ export const proof = {
125
126
  handler: inner,
126
127
  }]
127
128
  const [state, code] = run(outer)(['help', 'sub'])
128
- assertEq(code, 0, ['expected exit 0', code])
129
+ assertEq(exitCode(code), 0, ['expected exit 0', code])
129
130
  assert(state.stdout.includes('ping'), 'expected inner command in help')
130
131
  assert(!(state.stdout.includes('Subcommand group')), 'expected inner help, not outer')
131
132
  },
@@ -3,9 +3,8 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import type { NodeOp, NodeProgramOptions } from '../effects/node/types.ts';
7
- import type { Effect } from '../effects/types.ts';
8
- type Handler<O extends NodeOp> = (options: NodeProgramOptions) => Effect<O, number>;
6
+ import type { NodeOp, Program } from '../effects/node/types.ts';
7
+ type Handler<O extends NodeOp> = Program<O>;
9
8
  export type Command<O extends NodeOp> = {
10
9
  readonly names: readonly string[];
11
10
  readonly description: string;
@@ -65,7 +65,7 @@ export type Monoid<T> = {
65
65
  * has `null`, meaning "longer than `maxLength`".
66
66
  *
67
67
  * The monoid is carried as a field rather than intersected in, so it stays
68
- * independently constructed and consumed (`AGENTS.md` §6.2).
68
+ * independently constructed and consumed (`fjs/AGENTS.md` §3.2).
69
69
  */
70
70
  export type Absorbing<T> = {
71
71
  readonly monoid: Monoid<T>;
@@ -44,8 +44,8 @@ const iPad = vec8(0x36n)
44
44
  * that takes a message and computes the HMAC.
45
45
  */
46
46
  export const hmac = hashFunc => {
47
- const { blockLength } = hashFunc
48
- const p = repeat(blockLength >> 3n)
47
+ const { blockLength, blockBytes } = hashFunc
48
+ const p = repeat(blockBytes)
49
49
  const ip = p(iPad)
50
50
  const op = p(oPad)
51
51
  const c = computeSync(hashFunc)
@@ -11,7 +11,7 @@
11
11
  * @import { Base, Sha2, State, V16, V8 } from './types.ts'
12
12
  */
13
13
 
14
- import { mask } from '../../types/bigint/module.f.mjs'
14
+ import { divUp8, mask } from '../../types/bigint/module.f.mjs'
15
15
  import {
16
16
  vec,
17
17
  length,
@@ -257,6 +257,8 @@ const base = ({ logBitLen, k, bs0, bs1, ss0, ss1 }) => {
257
257
  const sha2 = ({ append, end, chunkLength }, hash, hashLength) => ({
258
258
  hashLength,
259
259
  blockLength: chunkLength,
260
+ hashBytes: divUp8(hashLength),
261
+ blockBytes: divUp8(chunkLength),
260
262
  init: {
261
263
  hash,
262
264
  len: 0n,
@@ -22,6 +22,7 @@ export declare const proof: {
22
22
  sha512x256: () => void;
23
23
  sha512x224: () => void;
24
24
  };
25
+ byteLengths: (() => void)[];
25
26
  utf8: (() => void)[];
26
27
  fill: () => {
27
28
  8: () => void;
@@ -9,6 +9,19 @@ import { assertEq } from '../../asserts/module.f.mjs'
9
9
  import { map } from '../../types/list/module.f.mjs'
10
10
  import { base32, base64, computeSync, sha224, sha256, sha384, sha512, sha512x224, sha512x256 } from './module.f.mjs'
11
11
 
12
+ /**
13
+ * Every SHA-2 length is a whole number of bytes, so the rounded-up byte count
14
+ * and the exact division agree — this pins both that identity and the values.
15
+ *
16
+ * @type {(sha2: Sha2) => (hashBytes: bigint, blockBytes: bigint) => void}
17
+ */
18
+ const checkBytes = ({ hashLength, blockLength, hashBytes, blockBytes }) => (h, b) => {
19
+ assertEq(hashBytes, h)
20
+ assertEq(blockBytes, b)
21
+ assertEq(hashBytes, hashLength >> 3n)
22
+ assertEq(blockBytes, blockLength >> 3n)
23
+ }
24
+
12
25
  /** @type {(sha2: Sha2) => (x: bigint) => void} */
13
26
  const checkEmpty = ({ init, end, hashLength }) => x => {
14
27
  const result = end(init)
@@ -71,6 +84,17 @@ export const proof = {
71
84
  sha512x256: () => checkEmpty(sha512x256)(0xc672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967an),
72
85
  sha512x224: () => checkEmpty(sha512x224)(0x6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4n),
73
86
  },
87
+ // The byte counts consumers read instead of converting bit lengths
88
+ // themselves. Asserted against the bit length each is derived from, not
89
+ // against a hand-written number, so the pairing is what is pinned.
90
+ byteLengths: [
91
+ () => checkBytes(sha224)(28n, 64n),
92
+ () => checkBytes(sha256)(32n, 64n),
93
+ () => checkBytes(sha384)(48n, 128n),
94
+ () => checkBytes(sha512)(64n, 128n),
95
+ () => checkBytes(sha512x224)(28n, 128n),
96
+ () => checkBytes(sha512x256)(32n, 128n),
97
+ ],
74
98
  utf8: [
75
99
  () => {
76
100
  const e = 0x730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525n
@@ -51,6 +51,17 @@ export type Base = {
51
51
  export type Sha2 = {
52
52
  readonly hashLength: bigint;
53
53
  readonly blockLength: bigint;
54
+ /**
55
+ * `hashLength` and `blockLength` in whole bytes, rounded up. Consumers
56
+ * that size a byte buffer read these instead of converting: `hmac` and
57
+ * `sign` each used to do it themselves, with two different spellings
58
+ * (`>> 3n` and `divUp8`), leaving a reader to work out per site whether
59
+ * the two roundings agree. They do for every SHA-2 variant, whose lengths
60
+ * are byte multiples — which is exactly why the decision belongs here
61
+ * once rather than at each call site.
62
+ */
63
+ readonly hashBytes: bigint;
64
+ readonly blockBytes: bigint;
54
65
  readonly init: State;
55
66
  readonly append: Fold<Vec, State>;
56
67
  readonly end: (state: State) => Vec;
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  import { assertNotNullish } from '../../asserts/module.f.mjs'
15
- import { bitLength, divUp8, roundUp8 } from '../../types/bigint/module.f.mjs'
15
+ import { bitLength, roundUp8 } from '../../types/bigint/module.f.mjs'
16
16
  import { empty, length, msb, repeat, unpack, vec, vec8 } from '../../types/bit_vec/module.f.mjs'
17
17
  import { hmac } from '../hmac/module.f.mjs'
18
18
  import { computeSync } from '../sha2/module.f.mjs'
@@ -75,7 +75,7 @@ export const computeK =
75
75
  // step and all subsequent steps, we use the same H function as the
76
76
  // one used in step 'a' to process the input message; this choice
77
77
  // will be discussed in more detail in Section 3.6.
78
- const rep = repeat(divUp8(hf.hashLength))
78
+ const rep = repeat(hf.hashBytes)
79
79
  const v0 = rep(x01)
80
80
  // c. Set:
81
81
  // K = 0x00 0x00 0x00 ... 0x00