functionalscript 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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/sorted_set/module.f.d.mts +18 -0
  274. package/fjs/types/sorted_set/module.f.mjs +22 -0
  275. package/fjs/types/sorted_set/proof.f.d.mts +1 -0
  276. package/fjs/types/sorted_set/proof.f.mjs +16 -1
  277. package/fjs/types/uint8array/module.f.d.mts +1 -1
  278. package/fjs/types/uint8array/module.f.mjs +1 -1
  279. package/fjs/website/module.f.d.mts +3 -3
  280. package/fjs/website/module.f.mjs +4 -7
  281. package/fjs/website/proof.f.mjs +2 -1
  282. package/package.json +2 -2
  283. package/fjs/dev/package_json/module.f.d.mts +0 -39
  284. package/fjs/dev/package_json/module.f.mjs +0 -40
  285. package/fjs/dev/package_json/proof.f.d.mts +0 -6
  286. package/fjs/dev/package_json/proof.f.mjs +0 -32
  287. package/fjs/effects/eff/module.f.d.mts +0 -20
  288. package/fjs/effects/eff/module.f.mjs +0 -70
  289. package/fjs/effects/eff/proof.f.d.mts +0 -15
  290. package/fjs/effects/eff/proof.f.mjs +0 -69
  291. package/fjs/effects/eff/types.d.ts +0 -71
  292. package/fjs/types/rtti/validate/module.f.d.mts +0 -41
  293. package/fjs/types/rtti/validate/module.f.mjs +0 -171
  294. package/fjs/types/rtti/validate/proof.f.d.mts +0 -125
  295. package/fjs/types/rtti/validate/proof.f.mjs +0 -437
  296. package/fjs/types/rtti/validate/types.d.ts +0 -6
@@ -172,8 +172,6 @@ const op = norm => op => ap => bp => {
172
172
 
173
173
  /**
174
174
  * @typedef {{
175
- * readonly front: (len: bigint) => (v: Vec) => bigint
176
- * readonly removeFront: (len: bigint) => (v: Vec) => Vec
177
175
  * readonly norm: _NormOp
178
176
  * readonly uintCmp: (a: bigint) => (b: bigint) => Sign
179
177
  * readonly unpackSplit: (len: bigint) => (u: Unpacked) => readonly[bigint, bigint]
@@ -183,6 +181,15 @@ const op = norm => op => ap => bp => {
183
181
 
184
182
  const unpackEmpty = /** @type {const} */{ length: 0n, uint: 0n }
185
183
 
184
+ /**
185
+ * The element-to-`Unpacked` maps the two {@link mappedListToVec} instances
186
+ * differ by. Neither depends on a bit order, so both are bound once here
187
+ * (`fjs/AGENTS.md` §3.3).
188
+ *
189
+ * @type {(_: number) => Unpacked}
190
+ */
191
+ const u8ToUnpacked = b => ({ length: 8n, uint: BigInt(b) })
192
+
186
193
  /**
187
194
  * Concatenation as a monoid over `Nullable<Unpacked>`, where `null` means "the
188
195
  * concatenation is longer than `maxLength`" and is an **absorbing** element:
@@ -198,7 +205,7 @@ const unpackEmpty = /** @type {const} */{ length: 0n, uint: 0n }
198
205
  * result because the result is what must not be built: `maxLength` is the
199
206
  * smallest `bigint` size supported across FunctionalScript's runtimes. The two
200
207
  * lengths are exact and additive — this is the length, not an estimate of it
201
- * (AGENTS.md §5.6).
208
+ * (`DESIGN.md` §6).
202
209
  *
203
210
  * Being absorbing is also what keeps the fold's walk bounded: `foldAbsorbing`
204
211
  * stops at the first merge that overflows instead of reading the rest of a list
@@ -241,8 +248,26 @@ const tryUnpackConcat = unpackConcat => ({
241
248
  const unpackListToVec = unpackConcat =>
242
249
  compose(foldAbsorbing(tryUnpackConcat(unpackConcat)))(nullableMap(pack))
243
250
 
251
+ /**
252
+ * Concatenates a list of `I` values into one vector, mapping each element to
253
+ * its `Unpacked` form first; `null` when the result would exceed `maxLength`.
254
+ *
255
+ * The concatenation counterpart of {@link mappedChunkList}. Everything about
256
+ * the operation is fixed except how one element becomes `Unpacked`, so each
257
+ * element type supplies just that map. The `bo` parameter is structural rather
258
+ * than a whole `BitOrder` because `bo`'s own body applies this before the
259
+ * record exists.
260
+ *
261
+ * @type {<I>(g: (i: I) => Unpacked) => (bo: { readonly unpackConcat: _UnpackConcat }) => (list: List<I>) => Nullable<Vec>}
262
+ */
263
+ const mappedListToVec = g => ({ unpackConcat }) => {
264
+ const f = unpackListToVec(unpackConcat)
265
+ const mg = map(g)
266
+ return list => f(mg(list))
267
+ }
268
+
244
269
  /** @type {(base: _Base) => BitOrder} */
245
- const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }) => {
270
+ const bo = ({ norm, uintCmp, unpackSplit, unpackConcatUint }) => {
246
271
  /** @param {bigint} len */
247
272
  const unpackPopFront = len => {
248
273
  const m = mask(len)
@@ -253,6 +278,19 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
253
278
  return /** @type {const} */([uint & m, { length: v.length - len, uint: rest }])
254
279
  }
255
280
  }
281
+ // `front` and `removeFront` are the two projections of `unpackPopFront`,
282
+ // so each bit order supplies only `unpackSplit` and both fall out of it.
283
+ // `pack` re-masks, so `removeFront` can hand on the unmasked rest.
284
+ /** @type {(len: bigint) => (v: Vec) => bigint} */
285
+ const front = len => {
286
+ const f = unpackPopFront(len)
287
+ return v => f(unpack(v))[0]
288
+ }
289
+ /** @type {(len: bigint) => (v: Vec) => Vec} */
290
+ const removeFront = len => {
291
+ const f = unpackPopFront(len)
292
+ return v => pack(f(unpack(v))[1])
293
+ }
256
294
  /** @type {_UnpackConcat} */
257
295
  const unpackConcat = a => b => ({
258
296
  length: a.length + b.length,
@@ -272,9 +310,7 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
272
310
  const bu = unpack(b)
273
311
  return pack(unpackConcat(au)(bu))
274
312
  }
275
- const unpackedListToVec = unpackListToVec(unpackConcat)
276
- /** @param {List<Vec>} list */
277
- const tryListToVec = list => unpackedListToVec(map(unpack)(list))
313
+ const tryListToVec = mappedListToVec(unpack)({ unpackConcat })
278
314
  return {
279
315
  front,
280
316
  removeFront,
@@ -298,7 +334,8 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
298
334
  unpackConcat,
299
335
  startsWith: prefix => {
300
336
  const { length: n, uint: u } = unpack(prefix)
301
- return v => length(v) < n ? false : popFront(n)(v)[0] === u
337
+ const f = front(n)
338
+ return v => length(v) < n ? false : f(v) === u
302
339
  }
303
340
  }
304
341
  }
@@ -315,14 +352,6 @@ const lsbUnpackConcatUint =
315
352
  * Usually associated with Little-Endian (LE) byte order.
316
353
  */
317
354
  export const lsb = bo({
318
- front: len => {
319
- const m = mask(len)
320
- return v => uint(v) & m
321
- },
322
- removeFront: len => v => {
323
- const { length, uint } = unpack(v)
324
- return vec(length - len)(uint >> len)
325
- },
326
355
  norm: ({ uint: a }) => ({ uint: b }) => () =>
327
356
  ({ a, b }),
328
357
  uintCmp: a => b => {
@@ -341,17 +370,6 @@ export const lsb = bo({
341
370
  * Usually associated with Big-Endian (BE) byte order.
342
371
  */
343
372
  export const msb = bo({
344
- front: len => {
345
- const m = mask(len)
346
- return v => {
347
- const { length, uint } = unpack(v)
348
- return (uint >> (length - len)) & m
349
- }
350
- },
351
- removeFront: len => v => {
352
- const { length, uint } = unpack(v)
353
- return vec(length - len)(uint)
354
- },
355
373
  norm: ({ length: al, uint: a }) => ({ length: bl, uint: b }) => len =>
356
374
  ({ a: a << (len - al), b: b << (len - bl) }),
357
375
  uintCmp: cmp,
@@ -366,11 +384,7 @@ export const msb = bo({
366
384
  *
367
385
  * @type {(_: BitOrder) => (list: List<number>) => Nullable<Vec>}
368
386
  */
369
- export const tryU8ListToVec = ({ unpackConcat }) => {
370
- const unpackedListToVec = unpackListToVec(unpackConcat)
371
- return list => unpackedListToVec(
372
- map(/** @type {(_: number) => Unpacked} */b => ({ length: 8n, uint: BigInt(b) }))(list))
373
- }
387
+ export const tryU8ListToVec = mappedListToVec(u8ToUnpacked)
374
388
 
375
389
  /**
376
390
  * Converts a list of unsigned 8-bit integers to a bit vector using the provided bit order.
@@ -193,9 +193,9 @@ export const proof = {
193
193
  },
194
194
  removeBack: () => {
195
195
  const v = vec(17n)(0x12345n)
196
- assertEq(v, unsafeVec(0x12345n), (/** @type {bigint} */ (asBase(v))).toString(16))
196
+ assertEq(v, unsafeVec(0x12345n), (asBase(v)).toString(16))
197
197
  const r = lsb.removeFront(9n)(v)
198
- assertEq(r, unsafeVec(0x91n), (/** @type {bigint} */ (asBase(r))).toString(16))
198
+ assertEq(r, unsafeVec(0x91n), (asBase(r)).toString(16))
199
199
  },
200
200
  uint: [
201
201
  // 0
@@ -130,7 +130,7 @@ export const nodeRemove = c => node => {
130
130
  const { first, tail } = find(c)(node)
131
131
  /** @type {(n: TNode<T>) => (f: (v: T) => PathItem<T>) => _RemovePath<T>} */
132
132
  const branch = n => f => {
133
- const [v, p] = path(/** @type {Path<T>} */(null))(n)
133
+ const [v, p] = path(null)(n)
134
134
  return { first: p.first, tail: concat(p.tail)({ first: f(v), tail }) }
135
135
  }
136
136
  const [i, n] = first
@@ -11,6 +11,7 @@
11
11
  import { collapseRoot } from '../types/module.f.mjs'
12
12
  import { find } from '../find/module.f.mjs'
13
13
  import { fold } from '../../list/module.f.mjs'
14
+ import { assert } from '../../../asserts/module.f.mjs'
14
15
 
15
16
  /**
16
17
  * @template T
@@ -80,30 +81,27 @@ const nodeSet = c => g => node => {
80
81
  // insert
81
82
  const value = g(null)
82
83
  // TODO: remove after TSGO fix the regression.
83
- // const _xl: 1|2 = x.length
84
- switch (x.length) {
84
+ const xL = x.length
85
+ // See https://github.com/microsoft/typescript-go/issues/4613
86
+ assert(xL === 1 || xL === 2)
87
+ switch (xL) {
85
88
  case 1: { return [[x[0], value]] }
86
89
  case 2: { return [[x[0]], value, [x[1]]] }
87
90
  }
88
- // TODO: remove after TSGO fix the regression.
89
- // See https://github.com/microsoft/typescript-go/issues/4613
90
- throw 'unreachable'
91
91
  }
92
92
  case 3: {
93
93
  // replace
94
94
  // TODO: remove after TSGO fix the regression.
95
- // const _xl: 2|5 = x.length
96
- switch (x.length) {
95
+ const xL = x.length
96
+ // See https://github.com/microsoft/typescript-go/issues/4613
97
+ assert(xL === 2 || xL === 5)
98
+ switch (xL) {
97
99
  case 2: { return [[x[0], g(x[1])]] }
98
100
  case 5: { return [[x[0], x[1], x[2], g(x[3]), x[4]]] }
99
101
  }
100
- // TODO: remove after TSGO fix the regression.
101
- throw 'unreachable'
102
102
  }
103
103
  case 4: {
104
104
  // insert
105
- // TODO: remove after TSGO fix the regression.
106
- // const _xl: 2 = x.length
107
105
  const [v0, v1] = x
108
106
  return [[v0], v1, [g(null)]]
109
107
  }
@@ -424,6 +424,18 @@ const test = [
424
424
  jsonStr(replacedSecond),
425
425
  '[[["1"],"100",["121","144"]],"16",[["169"],"25",["36"]],"4-updated",[["49"],"64",["81","9"]]]',
426
426
  )
427
+ },
428
+
429
+ // Replacing the first value of a two-value leaf exercises the
430
+ // `x.length === 2` arm of the `i === 1` "replace" case, which the
431
+ // fixtures above never reach — their two-value-leaf replaces all land on
432
+ // the second value.
433
+ () => {
434
+ /** @type {TNode<string>} */
435
+ const _map = set(['1'])('2')
436
+ assertEq(jsonStr(_map), '["1","2"]')
437
+ const replaced = replace(_map)('1')(() => '1-updated')
438
+ assertEq(jsonStr(replaced), '["1-updated","2"]')
427
439
  }
428
440
  ]
429
441
 
@@ -5,11 +5,9 @@
5
5
  * @module
6
6
  *
7
7
  * @import { RangeMap } from '../range_map/types.ts'
8
- * @import { SortedSet } from '../sorted_set/types.ts'
9
8
  * @import { ByteSet } from './types.ts'
10
9
  */
11
10
  import type { RangeMap } from '../range_map/types.ts';
12
- import type { SortedSet } from '../sorted_set/types.ts';
13
11
  import type { ByteSet } from './types.ts';
14
12
  export type _Byte = number;
15
13
  /** @typedef {number} _Byte */
@@ -31,5 +29,14 @@ export declare const set: (_: number) => (b: ByteSet) => ByteSet;
31
29
  export declare const setRange: (_: readonly [number, number]) => (b: ByteSet) => ByteSet;
32
30
  /** @type {(n: _Byte) => (s: ByteSet) => ByteSet} */
33
31
  export declare const unset: (n: _Byte) => (s: ByteSet) => ByteSet;
34
- /** @type {(n: ByteSet) => (s: string) => RangeMap<SortedSet<string>>} */
35
- export declare const toRangeMap: (n: ByteSet) => (s: string) => RangeMap<SortedSet<string>>;
32
+ /**
33
+ * Re-expresses the set as a range map: one entry per membership boundary,
34
+ * carrying whether the range up to that byte is in the set.
35
+ *
36
+ * The payload is the set's own answer — `boolean` — and nothing more. A caller
37
+ * that needs ranges labelled with something else maps over the result; that
38
+ * labelling belongs to the caller, not to a byte set.
39
+ *
40
+ * @type {(n: ByteSet) => RangeMap<boolean>}
41
+ */
42
+ export declare const toRangeMap: (n: ByteSet) => RangeMap<boolean>;
@@ -5,7 +5,6 @@
5
5
  * @module
6
6
  *
7
7
  * @import { RangeMap } from '../range_map/types.ts'
8
- * @import { SortedSet } from '../sorted_set/types.ts'
9
8
  * @import { ByteSet } from './types.ts'
10
9
  */
11
10
 
@@ -57,12 +56,21 @@ export const unset = n => s => difference(s)(one(n))
57
56
 
58
57
  const counter = reverse(countdown(256))
59
58
 
60
- /** @type {(n: ByteSet) => (s: string) => (i: number) => RangeMap<SortedSet<string>>} */
61
- const toRangeMapOp = n => s => i => {
59
+ /** @type {(n: ByteSet) => (i: number) => RangeMap<boolean>} */
60
+ const toRangeMapOp = n => i => {
62
61
  const current = has(i + 1)(n)
63
62
  const prev = has(i)(n)
64
- return current === prev ? null : [[prev ? [s] : [], i]]
63
+ return current === prev ? null : [[prev, i]]
65
64
  }
66
65
 
67
- /** @type {(n: ByteSet) => (s: string) => RangeMap<SortedSet<string>>} */
68
- export const toRangeMap = n => s => flat(map(toRangeMapOp(n)(s))(counter))
66
+ /**
67
+ * Re-expresses the set as a range map: one entry per membership boundary,
68
+ * carrying whether the range up to that byte is in the set.
69
+ *
70
+ * The payload is the set's own answer — `boolean` — and nothing more. A caller
71
+ * that needs ranges labelled with something else maps over the result; that
72
+ * labelling belongs to the caller, not to a byte set.
73
+ *
74
+ * @type {(n: ByteSet) => RangeMap<boolean>}
75
+ */
76
+ export const toRangeMap = n => flat(map(toRangeMapOp(n))(counter))
@@ -65,22 +65,22 @@ export const proof = {
65
65
  },
66
66
  toRangeMap: [
67
67
  () => {
68
- const result = stringify(toArray(toRangeMap(empty)('a')))
68
+ const result = stringify(toArray(toRangeMap(empty)))
69
69
  assertEq(result, '[]')
70
70
  },
71
71
  () => {
72
72
  const s = set(0)(empty)
73
- const result = stringify(toArray(toRangeMap(s)('a')))
74
- assertEq(result, '[[["a"],0]]')
73
+ const result = stringify(toArray(toRangeMap(s)))
74
+ assertEq(result, '[[true,0]]')
75
75
  },
76
76
  () => {
77
77
  const s = setRange([1, 2])(empty)
78
- const result = stringify(toArray(toRangeMap(s)('a')))
79
- assertEq(result, '[[[],0],[["a"],2]]')
78
+ const result = stringify(toArray(toRangeMap(s)))
79
+ assertEq(result, '[[false,0],[true,2]]')
80
80
  },
81
81
  () => {
82
- const result = stringify(toArray(toRangeMap(universe)('a')))
83
- assertEq(result, '[[["a"],255]]')
82
+ const result = stringify(toArray(toRangeMap(universe)))
83
+ assertEq(result, '[[true,255]]')
84
84
  },
85
85
  ]
86
86
  }
@@ -7,14 +7,21 @@
7
7
  * @import { Cmp1, Cmp2, Compare, Sign } from './types.ts'
8
8
  */
9
9
 
10
+ import { assert } from '../../../asserts/module.f.mjs'
11
+
10
12
  /** @type {<T>(cmp: Compare<T>) => (value: T) => Index<3>} */
11
- export const index3
12
- = cmp => value => /** @type {Index<3>} */(cmp(value) + 1)
13
+ export const index3 = cmp => value => {
14
+ const i = cmp(value) + 1
15
+ assert(i === 0 || i === 1 || i === 2, i)
16
+ return i
17
+ }
13
18
 
14
19
  /** @type {<T>(cmp: Compare<T>) => (v2: Tuple<2, T>) => Index<5>} */
15
20
  export const index5 = cmp => ([v0, v1]) => {
16
21
  const _0 = cmp(v0)
17
- return /** @type {Index<5>} */(_0 <= 0 ? _0 + 1 : cmp(v1) + 3)
22
+ const i = _0 <= 0 ? _0 + 1 : cmp(v1) + 3
23
+ assert(i === 0 || i === 1 || i === 2 || i === 3 || i === 4, i)
24
+ return i
18
25
  }
19
26
 
20
27
  /** @type {<A extends Cmp1>(a: A) => <B extends Cmp2<A, B>>(b: B) => Sign} */
@@ -77,7 +77,7 @@ export declare const reduce: <T>(op: Reduce<T>) => <D>(def: D) => (input: List<T
77
77
  * Folds `input` with an {@link Accumulator}, short-circuiting to `null` the
78
78
  * moment `update` rejects an item; otherwise finalizes with `end`. This is the
79
79
  * list-level `try*` sibling of {@link fold} for accumulations that can fail
80
- * partway through (see the `try*`/`Nullable` convention in `AGENTS.md`).
80
+ * partway through (see the `try*`/`Nullable` convention in `DESIGN.md` §6).
81
81
  *
82
82
  * @type {<I, T, R>({ init, update, end }: Accumulator<I, T, R>) => (input: List<I>) => Nullable<R>}
83
83
  */
@@ -238,7 +238,7 @@ export const reduce = op => def => compose(scan(reduceToScan(op)))(last(def))
238
238
  * Folds `input` with an {@link Accumulator}, short-circuiting to `null` the
239
239
  * moment `update` rejects an item; otherwise finalizes with `end`. This is the
240
240
  * list-level `try*` sibling of {@link fold} for accumulations that can fail
241
- * partway through (see the `try*`/`Nullable` convention in `AGENTS.md`).
241
+ * partway through (see the `try*`/`Nullable` convention in `DESIGN.md` §6).
242
242
  *
243
243
  * @type {<I, T, R>({ init, update, end }: Accumulator<I, T, R>) => (input: List<I>) => Nullable<R>}
244
244
  */
@@ -9,13 +9,27 @@
9
9
  import type { Option } from '../option/types.ts';
10
10
  import type { Nullable } from './types.ts';
11
11
  /**
12
- * @type {<T, R>(f: (value: T) => R) => (value: Nullable<T>) => Nullable<R>}
12
+ * Folds a `Nullable<T>` into a single value: `f` for a present value, `none`
13
+ * for `null`.
14
+ *
15
+ * The two branches carry independent result types, and `R2` lives on its own
16
+ * curry step on purpose. With `T`, `R1` and `R2` all on the outer generic they
17
+ * are instantiated together at `match(f)` — before `none` exists — so `R2` has
18
+ * nothing to infer from and collapses to `unknown`. Inferring it at the second
19
+ * call instead is what lets {@link map} be derived below.
20
+ *
21
+ * @type {<T, R1>(f: (_: T) => R1) => <R2>(none: () => R2) => (_: Nullable<T>) => R1 | R2}
13
22
  */
14
- export declare const map: <T, R>(f: (value: T) => R) => (value: Nullable<T>) => Nullable<R>;
23
+ export declare const match: <T, R1>(f: (_: T) => R1) => <R2>(none: () => R2) => (_: Nullable<T>) => R1 | R2;
15
24
  /**
16
- * @type {<T, R>(f: (_: T) => R) => (none: () => R) => (_: Nullable<T>) => Nullable<R>}
25
+ * Projects the present value of a `Nullable<T>`, passing `null` through.
26
+ *
27
+ * `map` is `match` with the absent branch fixed to `null`, so the
28
+ * `value === null` guard is written once, in `match`.
29
+ *
30
+ * @type {<T, R>(f: (value: T) => R) => (value: Nullable<T>) => Nullable<R>}
17
31
  */
18
- export declare const match: <T, R>(f: (_: T) => R) => (none: () => R) => (_: Nullable<T>) => Nullable<R>;
32
+ export declare const map: <T, R>(f: (value: T) => R) => (value: Nullable<T>) => Nullable<R>;
19
33
  /**
20
34
  * @type {<T>(value: Nullable<T>) => Option<T>}
21
35
  */
@@ -11,14 +11,31 @@ import { assert } from '../../asserts/module.f.mjs'
11
11
  import { fn } from '../function/module.f.mjs'
12
12
 
13
13
  /**
14
- * @type {<T, R>(f: (value: T) => R) => (value: Nullable<T>) => Nullable<R>}
14
+ * Folds a `Nullable<T>` into a single value: `f` for a present value, `none`
15
+ * for `null`.
16
+ *
17
+ * The two branches carry independent result types, and `R2` lives on its own
18
+ * curry step on purpose. With `T`, `R1` and `R2` all on the outer generic they
19
+ * are instantiated together at `match(f)` — before `none` exists — so `R2` has
20
+ * nothing to infer from and collapses to `unknown`. Inferring it at the second
21
+ * call instead is what lets {@link map} be derived below.
22
+ *
23
+ * @type {<T, R1>(f: (_: T) => R1) => <R2>(none: () => R2) => (_: Nullable<T>) => R1 | R2}
15
24
  */
16
- export const map = f => value => value === null ? null : f(value)
25
+ export const match = f => none => value => value === null ? none() : f(value)
26
+
27
+ /** The absent branch `map` fixes `match`'s `none` to. */
28
+ const noneIsNull = () => null
17
29
 
18
30
  /**
19
- * @type {<T, R>(f: (_: T) => R) => (none: () => R) => (_: Nullable<T>) => Nullable<R>}
31
+ * Projects the present value of a `Nullable<T>`, passing `null` through.
32
+ *
33
+ * `map` is `match` with the absent branch fixed to `null`, so the
34
+ * `value === null` guard is written once, in `match`.
35
+ *
36
+ * @type {<T, R>(f: (value: T) => R) => (value: Nullable<T>) => Nullable<R>}
20
37
  */
21
- export const match = f => none => value => value === null ? none() : f(value)
38
+ export const map = f => match(f)(noneIsNull)
22
39
 
23
40
  /**
24
41
  * @type {<T>(value: Nullable<T>) => Option<T>}
@@ -1 +1,5 @@
1
+ /**
2
+ * @import { Assert } from '../../asserts/types.ts'
3
+ * @import { Equal } from '../ts/types.ts'
4
+ */
1
5
  export declare const proof: (() => void)[];
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @import { Assert } from '../../asserts/types.ts'
3
+ * @import { Equal } from '../ts/types.ts'
4
+ */
5
+
1
6
  import { fromUndefined, map, match, toOption } from './module.f.mjs'
2
7
  import { assert, assertEq } from '../../asserts/module.f.mjs'
3
8
 
@@ -24,6 +29,16 @@ export const proof = [
24
29
  assertEq(double(3), 6)
25
30
  assertEq(double(null), -1)
26
31
  },
32
+ () => {
33
+ // The two branches carry independent result types: `describe` is
34
+ // `(_: Nullable<number>) => number | string`, not one unified `R`.
35
+ /** @type {(v: number) => number} */
36
+ const twice = v => v * 2
37
+ const describe = match(twice)(() => 'none')
38
+ /** @typedef {Assert<Equal<ReturnType<typeof describe>, number | string>>} _Branches */
39
+ assertEq(describe(3), 6)
40
+ assertEq(describe(null), 'none')
41
+ },
27
42
  () => {
28
43
  assertEq(fromUndefined(undefined), null, 0)
29
44
  assertEq(fromUndefined(5), 5, 1)
@@ -41,5 +41,15 @@ export declare const isObject: (value: unknown) => value is { readonly [k in str
41
41
  * @type {<T>(map: StringMap<Exclude<T, undefined>>) => readonly Exclude<T, undefined>[]}
42
42
  */
43
43
  export declare const definedValues: <T>(map: StringMap<Exclude<T, undefined>>) => readonly Exclude<T, undefined>[];
44
- /** @type {<T>(cmd: StringMap<Exclude<T, undefined>>) => readonly (readonly [string, Exclude<T, undefined>])[]} */
45
- export declare const definedEntries: <T>(cmd: StringMap<Exclude<T, undefined>>) => readonly (readonly [string, Exclude<T, undefined>])[];
44
+ /**
45
+ * Returns only the defined (non-undefined) entries of a partial record.
46
+ *
47
+ * `T` is the record's own value type, which `StringMap` already makes
48
+ * optional. Writing the parameter as `StringMap<Exclude<T, undefined>>`
49
+ * instead would leave `T` in a non-inference position, so a record whose value
50
+ * type is itself generic — the leaf-parameterized JSON tree, say — could not
51
+ * be passed at all.
52
+ *
53
+ * @type {<T>(cmd: StringMap<T>) => readonly Entry<T>[]}
54
+ */
55
+ export declare const definedEntries: <T>(cmd: StringMap<T>) => readonly Entry<T>[];
@@ -61,7 +61,17 @@ export const definedValues =
61
61
  map =>
62
62
  values(map).filter(v => v !== undefined)
63
63
 
64
- /** @type {<T>(cmd: StringMap<Exclude<T, undefined>>) => readonly (readonly [string, Exclude<T, undefined>])[]} */
64
+ /**
65
+ * Returns only the defined (non-undefined) entries of a partial record.
66
+ *
67
+ * `T` is the record's own value type, which `StringMap` already makes
68
+ * optional. Writing the parameter as `StringMap<Exclude<T, undefined>>`
69
+ * instead would leave `T` in a non-inference position, so a record whose value
70
+ * type is itself generic — the leaf-parameterized JSON tree, say — could not
71
+ * be passed at all.
72
+ *
73
+ * @type {<T>(cmd: StringMap<T>) => readonly Entry<T>[]}
74
+ */
65
75
  export const definedEntries =
66
76
  cmd =>
67
77
  entries(cmd).flatMap(([a, b]) => b === undefined ? [] : [[a, b]])
@@ -6,6 +6,8 @@
6
6
  * @import { Candidate, Create, PatriciaTrie } from './types.ts'
7
7
  */
8
8
 
9
+ import { splitLast } from '../array/module.f.mjs'
10
+
9
11
  /**
10
12
  * Creates a Patricia trie whose node merging is delegated to `create`.
11
13
  *
@@ -14,24 +16,37 @@
14
16
  export const patriciaTrie = create => ({
15
17
  push: (c, [storage, stack]) => {
16
18
  const [u] = c
17
- while (stack.length >= 2) {
18
- const [rLeaf, rHash] = stack[stack.length - 1]
19
- const [lLeaf, lHash] = stack[stack.length - 2]
19
+ // Carry propagation: merge the top two candidates for as long as they
20
+ // are more tightly coupled to each other than the right one is to the
21
+ // incoming leaf. Genuinely sequential each merge decides whether the
22
+ // next one happens — so the loop stays, but the stack is read by
23
+ // splitting rather than by index arithmetic. Either split answering
24
+ // `null` means fewer than two candidates are left, which is where the
25
+ // old `stack.length >= 2` guard stopped.
26
+ while (true) {
27
+ const top = splitLast(stack)
28
+ if (top === null) { break }
29
+ const [belowR, [rLeaf, rHash]] = top
30
+ const below = splitLast(belowR)
31
+ if (below === null) { break }
32
+ const [rest, [lLeaf, lHash]] = below
20
33
  if ((lLeaf ^ rLeaf) >= (rLeaf ^ u)) { break }
21
34
  const [h, newS] = create(lHash, rHash, storage)
22
35
  storage = newS
23
- stack = [...stack.slice(0, -2), [rLeaf, h]]
36
+ stack = [...rest, [rLeaf, h]]
24
37
  }
25
38
  return [storage, [...stack, c]]
26
39
  },
40
+ // Drain the right spine into a root: a right fold whose seed is the last
41
+ // candidate's identity. `splitLast` supplies both halves at once, so the
42
+ // empty stack is the `null` branch rather than a separate length guard.
27
43
  end: ([storage, stack]) => {
28
- if (stack.length === 0) { return [undefined, storage] }
29
- let h = stack[stack.length - 1][1]
30
- for (let i = stack.length - 2; i >= 0; i--) {
31
- const lHash = stack[i][1];
32
- [h, storage] = create(lHash, h, storage)
33
- }
34
- return [h, storage]
44
+ const split = splitLast(stack)
45
+ if (split === null) { return [undefined, storage] }
46
+ const [rest, [, lastHash]] = split
47
+ return rest.reduceRight(
48
+ ([h, s], [, lHash]) => create(lHash, h, s),
49
+ /** @type {readonly [typeof lastHash, typeof storage]} */([lastHash, storage]))
35
50
  }
36
51
  })
37
52
 
@@ -64,8 +64,8 @@ export declare const mapOk: <T, R>(f: (value: T) => R) => <E>(r: Result<T, E>) =
64
64
  /**
65
65
  * Chains a `Result`-returning step onto a `Result`, passing an `error` through
66
66
  * unchanged. The monadic bind to {@link mapOk}'s functor map, and the pure
67
- * sibling of `fjs/effects`' `okStep` which is the same combinator for a step
68
- * that also performs operations.
67
+ * sibling of the branch `fjs/effects`'s `step` writes for an effect that
68
+ * also performs operations.
69
69
  *
70
70
  * The two error types are **unioned, not unified**. A validation chain
71
71
  * typically widens its error as it goes — a JSON parse failure (`string`)
@@ -80,7 +80,7 @@ export declare const mapOk: <T, R>(f: (value: T) => R) => <E>(r: Result<T, E>) =
80
80
  *
81
81
  * Reach for it when the step needs **only** the value it is handed. A chain
82
82
  * whose later steps also read an earlier one's value would have to nest a
83
- * closure per link to keep those values in scope, which AGENTS.md §6.4 rules
83
+ * closure per link to keep those values in scope, which `fjs/AGENTS.md` §3.4 rules
84
84
  * out for the same reason it rules out nested `step`; write those as a flat
85
85
  * sequence of guards.
86
86
  *
@@ -72,8 +72,8 @@ export const mapOk = f => r => r[0] === 'ok' ? ok(f(r[1])) : r
72
72
  /**
73
73
  * Chains a `Result`-returning step onto a `Result`, passing an `error` through
74
74
  * unchanged. The monadic bind to {@link mapOk}'s functor map, and the pure
75
- * sibling of `fjs/effects`' `okStep` which is the same combinator for a step
76
- * that also performs operations.
75
+ * sibling of the branch `fjs/effects`'s `step` writes for an effect that
76
+ * also performs operations.
77
77
  *
78
78
  * The two error types are **unioned, not unified**. A validation chain
79
79
  * typically widens its error as it goes — a JSON parse failure (`string`)
@@ -88,7 +88,7 @@ export const mapOk = f => r => r[0] === 'ok' ? ok(f(r[1])) : r
88
88
  *
89
89
  * Reach for it when the step needs **only** the value it is handed. A chain
90
90
  * whose later steps also read an earlier one's value would have to nest a
91
- * closure per link to keep those values in scope, which AGENTS.md §6.4 rules
91
+ * closure per link to keep those values in scope, which `fjs/AGENTS.md` §3.4 rules
92
92
  * out for the same reason it rules out nested `step`; write those as a flat
93
93
  * sequence of guards.
94
94
  *