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,18 +4,20 @@
4
4
  * @module
5
5
  *
6
6
  * @import { Vec } from '../../../types/bit_vec/types.ts'
7
- * @import { MemOperationMap, RunInstance } from '../../mock/types.ts'
8
- * @import { Key } from '../../memory/types.ts'
9
- * @import { Dirent, FileStat, IoResult, Module, NodeOp, NodeProgramOptions, SandboxResult } from '../types.ts'
7
+ * @import { PartialMemOperationMap, RunInstance } from '../../mock/types.ts'
8
+ * @import { Dirent, FileStat, IoError, IoResult, Module, NodeOp, NodeProgramOptions, SandboxResult } from '../types.ts'
9
+ * @import { Result } from '../../../types/result/types.ts'
10
+ * @import { Error } from '../../../types/result/types.ts'
10
11
  * @import { Dir, State, _Entity } from './types.ts'
11
12
  */
12
13
 
13
- import { todo } from '../../../asserts/module.f.mjs'
14
+ import { assert, todo } from '../../../asserts/module.f.mjs'
14
15
  import { isProperPrefix, join, parse } from '../../../path/module.f.mjs'
15
16
  import { utf8ToString } from '../../../text/module.f.mjs'
16
17
  import { empty, length, maxLengthBytes, msb, vec } from '../../../types/bit_vec/module.f.mjs'
17
18
  import { error, ok } from '../../../types/result/module.f.mjs'
18
- import { run } from '../../mock/module.f.mjs'
19
+ import { ioError, nodeCommands } from '../module.f.mjs'
20
+ import { partialRun } from '../../mock/module.f.mjs'
19
21
  import { asBase, asNominal } from '../../memory/module.f.mjs'
20
22
 
21
23
  /** @type {State} */
@@ -44,10 +46,13 @@ const operation = op => {
44
46
  }
45
47
  const [first, ...rest] = path
46
48
  const subDir = dir[first]
47
- if (typeof subDir !== 'object' || Array.isArray(subDir)) {
49
+ // `instanceof Array`, not `Array.isArray`: the latter narrows to `any[]`,
50
+ // which `readonly Vec[]` is not assignable to, so its negative branch never
51
+ // removes a `readonly` array from `_Entity`. Only `instanceof` narrows here.
52
+ if (typeof subDir !== 'object' || subDir instanceof Array) {
48
53
  return op(dir, path)
49
54
  }
50
- const [newSubDir, r] = f(/** @type {Dir} */ (subDir), rest)
55
+ const [newSubDir, r] = f(subDir, rest)
51
56
  return [{ ...dir, [first]: newSubDir }, r]
52
57
  }
53
58
  return path => state => {
@@ -65,12 +70,22 @@ const readOperation = op => operation((dir, path) => [dir, op(dir, path)])
65
70
 
66
71
  const okVoid = ok(undefined)
67
72
 
73
+ /**
74
+ * A virtual host failure. The virtual runner reports the same normalized
75
+ * {@link IoError} the Node runner does, so a program cannot tell the two apart
76
+ * by the shape of what it catches — which is what makes a proof against the
77
+ * virtual filesystem evidence about the real one.
78
+ *
79
+ * @type {(message: string) => Error<IoError>}
80
+ */
81
+ const fail = message => error(ioError({ message }))
82
+
68
83
  /** @type {(recursive: boolean) => (dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<void>]} */
69
84
  const mkdirOp = recursive => (dir, path) => {
70
85
  let d = {}
71
86
  let i = path.length
72
87
  if (i > 1 && !recursive) {
73
- return [dir, error('non-recursive')]
88
+ return [dir, fail('non-recursive')]
74
89
  }
75
90
  while (i > 0) {
76
91
  i -= 1
@@ -84,7 +99,7 @@ const mkdirOp = recursive => (dir, path) => {
84
99
  const mkdir = recursive => operation(mkdirOp(recursive))
85
100
 
86
101
  /** Absent-path error mirroring Node's `ENOENT`, so `isNotFound` recognizes it. */
87
- const enoent = error({ code: 'ENOENT' })
102
+ const enoent = error(ioError({ code: 'ENOENT', message: 'no such file or directory' }))
88
103
 
89
104
  /** @type {(path: string) => (state: State) => readonly [State, IoResult<Vec>]} */
90
105
  const readFile = readOperation((dir, path) => {
@@ -92,15 +107,18 @@ const readFile = readOperation((dir, path) => {
92
107
  const file = dir[path[0]]
93
108
  if (typeof file === 'function') { throw new Error(`'${path[0]}' is a JsModule; readFile not supported`) }
94
109
  if (file === undefined) { return enoent }
95
- if (!Array.isArray(file)) { return error(`'${path[0]}' is not a file`) }
96
- const chunks = /** @type {readonly Vec[]} */ (file)
110
+ // `operation`'s wrapper descends into every plain-object (`Dir`) entry
111
+ // before this op ever runs, and the `JsModule` case already threw above,
112
+ // so `file` here is always a `Vec[]` — never a bare `Dir`.
113
+ assert(Array.isArray(file), `'${path[0]}' is not a file`)
114
+ const chunks = file
97
115
  const capBits = maxLengthBytes * 8n
98
116
  let result = empty
99
117
  for (const chunk of chunks) {
100
118
  const chunkLen = length(chunk)
101
119
  if (chunkLen === 0n) { continue }
102
120
  if (length(result) + chunkLen > capBits) {
103
- return error(`File size exceeds maximum allowed size of ${maxLengthBytes} bytes`)
121
+ return fail(`File size exceeds maximum allowed size of ${maxLengthBytes} bytes`)
104
122
  }
105
123
  result = msb.concat(result)(chunk)
106
124
  }
@@ -109,13 +127,13 @@ const readFile = readOperation((dir, path) => {
109
127
 
110
128
  /** @type {(path: string) => (state: State) => readonly [State, IoResult<Module>]} */
111
129
  const import_ = readOperation((dir, path) => {
112
- if (path.length !== 1) { return error('no such file') }
130
+ if (path.length !== 1) { return fail('no such file') }
113
131
  const entry = dir[path[0]]
114
- if (typeof entry !== 'function') { return error(`'${path[0]}' is not a JsModule`) }
132
+ if (typeof entry !== 'function') { return fail(`'${path[0]}' is not a JsModule`) }
115
133
  return ok(entry())
116
134
  })
117
135
 
118
- const writeFileError = error('invalid file')
136
+ const writeFileError = fail('invalid file')
119
137
 
120
138
  /** @type {(payload: Vec) => (dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<void>]} */
121
139
  const writeFileOp = payload => (dir, path) => {
@@ -130,7 +148,7 @@ const writeFileOp = payload => (dir, path) => {
130
148
  /** @type {(payload: Vec) => (path: string) => (state: State) => readonly [State, IoResult<void>]} */
131
149
  const writeFile = payload => operation(writeFileOp(payload))
132
150
 
133
- const invalidPath = error('invalid path')
151
+ const invalidPath = fail('invalid path')
134
152
 
135
153
  const { entries } = Object
136
154
 
@@ -143,10 +161,10 @@ const readdir = (base, recursive) => readOperation((dir, path) => {
143
161
  let result = []
144
162
  for (const [name, content] of entries(d)) {
145
163
  if (content === undefined) { continue }
146
- const isFile = Array.isArray(content) || typeof content !== 'object'
164
+ const isFile = content instanceof Array || typeof content !== 'object'
147
165
  result = [...result, { name, parentPath, isFile }]
148
166
  if (!isFile && recursive) {
149
- result = [...result, ...f(join(parentPath, name), /** @type {Dir} */ (content))]
167
+ result = [...result, ...f(join(parentPath, name), content)]
150
168
  }
151
169
  }
152
170
  return result
@@ -163,11 +181,15 @@ const access = readOperation((dir, path) => {
163
181
 
164
182
  /** @type {(dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<void>]} */
165
183
  const rmOp = (dir, path) => {
166
- if (path.length !== 1) { return [dir, error('invalid path')] }
184
+ if (path.length !== 1) { return [dir, fail('invalid path')] }
167
185
  const [name] = path
168
186
  const entry = dir[name]
169
- if (entry === undefined) { return [dir, error('no such file')] }
170
- if (!Array.isArray(entry) && typeof entry === 'object') { return [dir, error('is a directory')] }
187
+ if (entry === undefined) { return [dir, fail('no such file')] }
188
+ // No "is a directory" guard here: `operation`'s wrapper descends into
189
+ // every plain-object (`Dir`) entry before this op ever runs, so `entry`
190
+ // is always a `Vec[]` or a `JsModule` — never a bare `Dir` — and rm can
191
+ // always proceed. (`rm` on a genuinely non-empty directory instead hits
192
+ // `path.length !== 1` above, once the wrapper has descended into it.)
171
193
  const { [name]: _, ...rest } = dir
172
194
  return [rest, okVoid]
173
195
  }
@@ -177,7 +199,7 @@ const rm = operation(rmOp)
177
199
 
178
200
  /** @type {(dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<_Entity>]} */
179
201
  const extractEntity = (dir, path) => {
180
- if (path.length === 0) { return [dir, error('cannot extract root')] }
202
+ if (path.length === 0) { return [dir, fail('cannot extract root')] }
181
203
  if (path.length === 1) {
182
204
  const [name] = path
183
205
  const entry = dir[name]
@@ -187,15 +209,21 @@ const extractEntity = (dir, path) => {
187
209
  }
188
210
  const [first, ...rest] = path
189
211
  const sub = dir[first]
190
- if (sub === undefined || Array.isArray(sub) || typeof sub === 'function') { return [dir, enoent] }
191
- const [newSub, result] = extractEntity(/** @type {Dir} */ (sub), rest)
212
+ if (sub === undefined || sub instanceof Array || typeof sub === 'function') { return [dir, enoent] }
213
+ const [newSub, result] = extractEntity(sub, rest)
192
214
  if (result[0] === 'error') { return [dir, result] }
193
215
  return [{ ...dir, [first]: newSub }, result]
194
216
  }
195
217
 
196
218
  /** @type {(dir: Dir, path: readonly string[], entity: _Entity) => readonly [Dir, IoResult<void>]} */
197
219
  const insertEntityAt = (dir, path, entity) => {
198
- if (path.length === 0) { return [dir, error('cannot insert at root')] }
220
+ // `insertEntityAt`'s only external caller, `rename`, always rejects an
221
+ // empty `dst` earlier — `isProperPrefix([], srcParsed)` is true whenever
222
+ // `srcParsed` is non-empty, so renaming onto root is already caught as
223
+ // "onto an ancestor" before this function runs. The recursive self-calls
224
+ // below never pass an empty path either (they only recurse when
225
+ // `path.length > 1`, with a non-empty remainder).
226
+ assert(path.length > 0, 'cannot insert at root')
199
227
  if (path.length === 1) {
200
228
  const [name] = path
201
229
  const existing = dir[name]
@@ -203,16 +231,16 @@ const insertEntityAt = (dir, path, entity) => {
203
231
  const entityIsDir = !Array.isArray(entity) && typeof entity === 'object'
204
232
  const existingIsDir = !Array.isArray(existing) && typeof existing === 'object'
205
233
  if (entityIsDir && !existingIsDir) {
206
- return [dir, error(`cannot overwrite file '${name}' with a directory`)]
234
+ return [dir, fail(`cannot overwrite file '${name}' with a directory`)]
207
235
  }
208
236
  if (!entityIsDir && existingIsDir) {
209
- return [dir, error(`'${name}' is a directory`)]
237
+ return [dir, fail(`'${name}' is a directory`)]
210
238
  }
211
239
  if (entityIsDir && existingIsDir) {
212
- const existingDir = /** @type {Dir} */ (existing)
240
+ const existingDir = existing
213
241
  const hasContent = Object.values(existingDir).some(v => v !== undefined)
214
242
  if (hasContent) {
215
- return [dir, error(`cannot overwrite non-empty directory '${name}'`)]
243
+ return [dir, fail(`cannot overwrite non-empty directory '${name}'`)]
216
244
  }
217
245
  }
218
246
  }
@@ -221,8 +249,8 @@ const insertEntityAt = (dir, path, entity) => {
221
249
  const [first, ...rest] = path
222
250
  const sub = dir[first]
223
251
  if (sub === undefined) { return [dir, enoent] }
224
- if (Array.isArray(sub) || typeof sub === 'function') { return [dir, error('not a directory')] }
225
- const [newSub, result] = insertEntityAt(/** @type {Dir} */ (sub), rest, entity)
252
+ if (sub instanceof Array || typeof sub === 'function') { return [dir, fail('not a directory')] }
253
+ const [newSub, result] = insertEntityAt(sub, rest, entity)
226
254
  if (result[0] === 'error') { return [dir, result] }
227
255
  return [{ ...dir, [first]: newSub }, result]
228
256
  }
@@ -237,7 +265,7 @@ const rename = (src, dst) => state => {
237
265
  // now that source exists, reject if dst is strictly inside src's subtree (rename into own descendant)
238
266
  // or if src is strictly inside dst's subtree (rename onto own ancestor)
239
267
  if (isProperPrefix(srcParsed, dstParsed) || isProperPrefix(dstParsed, srcParsed)) {
240
- return [state, error('cannot rename a directory into its own subtree or onto an ancestor')]
268
+ return [state, fail('cannot rename a directory into its own subtree or onto an ancestor')]
241
269
  }
242
270
  const [dstRoot, dstResult] = insertEntityAt(srcRoot, dstParsed, srcResult[1])
243
271
  if (dstResult[0] === 'error') { return [state, dstResult] }
@@ -250,13 +278,16 @@ const readBytesOp = (path, offset, size) => readOperation((dir, p) => {
250
278
  const file = dir[p[0]]
251
279
  if (typeof file === 'function') { throw new Error(`'${p[0]}' is a JsModule; readBytes not supported`) }
252
280
  if (file === undefined) { return enoent }
253
- if (!Array.isArray(file)) { return error(`'${p[0]}' is not a file`) }
254
- if (!Number.isInteger(offset)) { return error(`Offset ${offset} is not an integer`) }
255
- if (!Number.isInteger(size)) { return error(`Chunk size ${size} is not an integer`) }
256
- if (offset < 0) { return error(`Offset ${offset} is negative`) }
257
- if (size < 0) { return error(`Chunk size ${size} is negative`) }
258
- if (BigInt(size) > maxLengthBytes) { return error(`Chunk size ${size} exceeds maximum allowed size of ${maxLengthBytes} bytes`) }
259
- const chunks = /** @type {readonly Vec[]} */ (file)
281
+ // `operation`'s wrapper descends into every plain-object (`Dir`) entry
282
+ // before this op ever runs, and the `JsModule` case already threw above,
283
+ // so `file` here is always a `Vec[]` never a bare `Dir`.
284
+ assert(Array.isArray(file), `'${p[0]}' is not a file`)
285
+ if (!Number.isInteger(offset)) { return fail(`Offset ${offset} is not an integer`) }
286
+ if (!Number.isInteger(size)) { return fail(`Chunk size ${size} is not an integer`) }
287
+ if (offset < 0) { return fail(`Offset ${offset} is negative`) }
288
+ if (size < 0) { return fail(`Chunk size ${size} is negative`) }
289
+ if (BigInt(size) > maxLengthBytes) { return fail(`Chunk size ${size} exceeds maximum allowed size of ${maxLengthBytes} bytes`) }
290
+ const chunks = file
260
291
  let toSkip = BigInt(offset) * 8n
261
292
  let toRead = BigInt(size) * 8n
262
293
  let result = empty
@@ -283,7 +314,7 @@ const fileSizeBytes = chunks =>
283
314
  chunks.reduce((acc, c) => acc + Number(length(c) / 8n), 0)
284
315
 
285
316
  /** Absent-path error for an already-existing exclusive create, mirroring `EEXIST`. */
286
- const eexist = error({ code: 'EEXIST' })
317
+ const eexist = error(ioError({ code: 'EEXIST', message: 'file already exists' }))
287
318
 
288
319
  /** @type {(dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<void>]} */
289
320
  const createExclusiveOp = (dir, path) => {
@@ -308,11 +339,11 @@ const writeBytesRawOp = (offset, data) => (dir, p) => {
308
339
  const [name] = p
309
340
  const file = dir[name]
310
341
  if (file === undefined) { return [dir, enoent] } // writeBytes never creates
311
- if (!Array.isArray(file)) { return [dir, error(`'${name}' is not a file`)] }
312
- if (!Number.isInteger(offset) || offset < 0) { return [dir, error(`Offset ${offset} is invalid`)] }
313
- const chunks = /** @type {readonly Vec[]} */ (file)
342
+ if (!Array.isArray(file)) { return [dir, fail(`'${name}' is not a file`)] }
343
+ if (!Number.isInteger(offset) || offset < 0) { return [dir, fail(`Offset ${offset} is invalid`)] }
344
+ const chunks = file
314
345
  if (offset !== fileSizeBytes(chunks)) {
315
- return [dir, error(`writeBytes offset ${offset} must equal the file size (append-only)`)]
346
+ return [dir, fail(`writeBytes offset ${offset} must equal the file size (append-only)`)]
316
347
  }
317
348
  return [{ ...dir, [name]: [...chunks, data] }, okVoid]
318
349
  }
@@ -325,43 +356,46 @@ const statOp = readOperation((dir, path) => {
325
356
  if (path.length !== 1) { return enoent }
326
357
  const file = dir[path[0]]
327
358
  if (file === undefined) { return enoent }
328
- if (!Array.isArray(file)) { return error(`'${path[0]}' is not a file`) }
329
- return ok({ size: fileSizeBytes(/** @type {readonly Vec[]} */ (file)) })
359
+ if (!Array.isArray(file)) { return fail(`'${path[0]}' is not a file`) }
360
+ return ok({ size: fileSizeBytes(file) })
330
361
  })
331
362
 
332
- /** @type {MemOperationMap<NodeOp, State>} */
363
+ /** @type {PartialMemOperationMap<NodeOp, State>} */
333
364
  const map = {
334
365
  all: (...a) => state => {
335
- /** @type {readonly unknown[]} */
366
+ // Each entry is the effect's whole `Result`: `all`'s own envelope says
367
+ // only whether the runner could dispatch it, so the inner answers pass
368
+ // through untouched for `allOk` to collapse.
369
+ /** @type {readonly Result<unknown, unknown>[]} */
336
370
  let e = []
337
371
  for (const i of a) {
338
372
  const [ns, ei] = virtual(state)(i)
339
373
  state = ns
340
374
  e = [...e, ei]
341
375
  }
342
- return [state, e]
376
+ return [state, ok(e)]
343
377
  },
344
378
  memCreate: value => state => {
345
379
  const id = `mem${state.memoryNext}`
346
- const key = /** @type {Key<unknown>} */ (asNominal(id))
380
+ const key = asNominal(id)
347
381
  return [{
348
382
  ...state,
349
383
  memoryNext: state.memoryNext + 1,
350
384
  memoryValues: { ...state.memoryValues, [id]: value },
351
- }, key]
385
+ }, ok(key)]
352
386
  },
353
387
  memRead: key => state =>
354
- [state, state.memoryValues[asBase(key)]],
388
+ [state, ok(state.memoryValues[asBase(key)])],
355
389
  memWrite: (key, value) => state => {
356
390
  const id = asBase(key)
357
391
  return [{
358
392
  ...state,
359
393
  memoryValues: { ...state.memoryValues, [id]: value },
360
- }, undefined]
394
+ }, okVoid]
361
395
  },
362
396
  fetch: url => state => {
363
397
  const result = state.internet[url]
364
- return result === undefined ? [state, error('not found')] : [state, ok(result)]
398
+ return result === undefined ? [state, fail('not found')] : [state, ok(result)]
365
399
  },
366
400
  mkdir: (path, p) => mkdir(p !== undefined)(path),
367
401
  readFile,
@@ -375,12 +409,8 @@ const map = {
375
409
  createExclusive,
376
410
  writeBytes: writeBytesOp,
377
411
  stat: statOp,
378
- randomInt: () => state => [{ ...state, randomNext: state.randomNext + 1 }, state.randomNext],
379
- exec: todo,
380
- createServer: todo,
381
- listen: todo,
382
- forever: todo,
383
- now: () => state => [state, state.epochNs],
412
+ randomInt: () => state => [{ ...state, randomNext: state.randomNext + 1 }, ok(state.randomNext)],
413
+ now: () => state => [state, ok(state.epochNs)],
384
414
  // Virtual sandbox is a pass-through: the fixture's test function is
385
415
  // expected to return a `SandboxResult` directly (encoding pass/fail and a
386
416
  // chosen duration), so the handler invokes it without try/catch or clock
@@ -388,23 +418,35 @@ const map = {
388
418
  // result instead of the runner measuring real execution. A genuine
389
419
  // exception in a fixture propagates loudly as a bug in the fixture.
390
420
  // See: issues/156-tf-virtual-tests.md
391
- sandbox: f => state => [state, /** @type {SandboxResult<unknown>} */ (f())],
392
- await: p => state => [state, [p]],
393
- test: todo,
421
+ sandbox: f => state => [state, ok(/** @type {SandboxResult<unknown>} */ (f()))],
422
+ await: p => state => [state, ok([p])],
394
423
  write: (stream, data) => state => {
395
424
  const s = utf8ToString(data)
396
- return [{ ...state, [stream]: `${state[stream]}${s}` }, undefined]
425
+ return [{ ...state, [stream]: `${state[stream]}${s}` }, okVoid]
397
426
  },
398
427
  read: () => state => {
399
428
  const [first, ...rest] = state.stdin
400
429
  return state.stdin.length === 0
401
- ? [state, null]
402
- : [{ ...state, stdin: rest }, first]
430
+ ? [state, ok(null)]
431
+ : [{ ...state, stdin: rest }, ok(first)]
403
432
  },
404
433
  }
405
434
 
406
- /** @type {RunInstance<NodeOp, State>} */
407
- export const virtual = run(map)
435
+ /**
436
+ * The virtual runner.
437
+ *
438
+ * **It implements part of `NodeOp`, and says so.** `exec`, `createServer`,
439
+ * `listen`, `forever` and `test` have no meaning against an in-memory
440
+ * filesystem, and they used to be present as `todo` handlers — entries that
441
+ * existed only to satisfy a total operation map and threw when reached. They
442
+ * are simply absent now, so a program that asks for one gets
443
+ * `error(notImplemented)` back through its own continuation and decides what an
444
+ * incompatible runner means for it, which is what `NotImplemented` was
445
+ * introduced for. A command that is not a `NodeOp` at all still panics.
446
+ *
447
+ * @type {RunInstance<NodeOp, State>}
448
+ */
449
+ export const virtual = partialRun(nodeCommands)(map)
408
450
 
409
451
  const testContext = { test: todo }
410
452
 
@@ -1,11 +1,21 @@
1
1
  /**
2
- * @import { Dir, JsModule } from './types.ts'
2
+ * @import { Dir } from './types.ts'
3
+ * @import { NodeOp } from '../types.ts'
4
+ * @import { Effect } from '../../types.ts'
5
+ * @import { IoChannel } from '../types.ts'
3
6
  */
4
7
  export declare const proof: {
8
+ unimplemented: {
9
+ declaredButAbsent: () => void;
10
+ programChoosesAFallback: () => void;
11
+ throw: {
12
+ undeclaredCommand: () => void;
13
+ };
14
+ };
5
15
  rm: {
6
16
  success: () => void;
7
17
  notFound: () => void;
8
- isDirectory: () => void;
18
+ onDirectory: () => void;
9
19
  };
10
20
  writeFileOnDirectory: () => void;
11
21
  writeFileOverJsModule: () => void;
@@ -18,7 +28,23 @@ export declare const proof: {
18
28
  importNonModule: () => void;
19
29
  throw: {
20
30
  readFileOnJsModule: () => void;
31
+ readBytesOnJsModule: () => void;
21
32
  };
33
+ readFileSkipsEmptyChunk: () => void;
34
+ readdirSkipsUndefinedEntry: () => void;
35
+ renameEmptySrc: () => void;
36
+ renameSrcThroughFile: () => void;
37
+ renameSrcThreeLevelsMissing: () => void;
38
+ renameDstMissingIntermediate: () => void;
39
+ renameDstThroughFile: () => void;
40
+ renameDstNestedError: () => void;
41
+ createExclusiveNestedMissing: () => void;
42
+ writeBytesNestedMissing: () => void;
43
+ writeBytesMissingFile: () => void;
44
+ writeBytesOnJsModule: () => void;
45
+ writeBytesNegativeOffset: () => void;
46
+ statNestedMissing: () => void;
47
+ statMissingFile: () => void;
22
48
  renameSamePath: () => void;
23
49
  renameIntoOwnSubtree: () => void;
24
50
  renameOntoOwnAncestor: () => void;