functionalscript 0.35.2 → 0.37.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 (270) hide show
  1. package/fs/asn.1/module.f.js +1 -1
  2. package/fs/asserts/proof.f.js +1 -1
  3. package/fs/base_n/module.f.js +3 -3
  4. package/fs/base_n/proof.f.js +3 -3
  5. package/fs/{base128 → basen/base128}/module.f.d.ts +1 -1
  6. package/fs/{base128 → basen/base128}/module.f.js +1 -1
  7. package/fs/{base128 → basen/base128}/proof.f.js +3 -3
  8. package/fs/{base64 → basen/base64}/module.f.d.ts +2 -2
  9. package/fs/basen/base64/module.f.js +71 -0
  10. package/fs/{base64 → basen/base64}/proof.f.d.ts +2 -0
  11. package/fs/{base64 → basen/base64}/proof.f.js +17 -2
  12. package/fs/{cbase32 → basen/cbase32}/module.f.d.ts +2 -2
  13. package/fs/{cbase32 → basen/cbase32}/module.f.js +2 -2
  14. package/fs/{cbase32 → basen/cbase32}/proof.f.js +1 -1
  15. package/fs/bnf/data/module.f.js +4 -4
  16. package/fs/bnf/data/proof.f.js +6 -6
  17. package/fs/bnf/descent/module.f.js +6 -6
  18. package/fs/bnf/descent/proof.f.js +6 -6
  19. package/fs/bnf/ll1/module.f.js +9 -9
  20. package/fs/bnf/ll1/proof.f.js +6 -6
  21. package/fs/bnf/module.f.js +4 -4
  22. package/fs/bnf/proof.f.js +2 -2
  23. package/fs/bnf/testlib.f.js +1 -1
  24. package/fs/cas/cli/module.f.js +7 -7
  25. package/fs/cas/cli/proof.f.js +8 -8
  26. package/fs/cas/mcp/module.f.d.ts +8 -4
  27. package/fs/cas/mcp/module.f.js +124 -72
  28. package/fs/cas/mcp/proof.f.d.ts +3 -8
  29. package/fs/cas/mcp/proof.f.js +122 -186
  30. package/fs/cas/module.f.js +12 -19
  31. package/fs/cas/proof.f.d.ts +4 -2
  32. package/fs/cas/proof.f.js +10 -10
  33. package/fs/ci/bun/module.f.js +2 -2
  34. package/fs/ci/common/module.f.d.ts +2 -2
  35. package/fs/ci/common/module.f.js +4 -4
  36. package/fs/ci/config/module.f.d.ts +20 -21
  37. package/fs/ci/config/module.f.js +8 -10
  38. package/fs/ci/deno/module.f.js +8 -4
  39. package/fs/ci/module.f.js +9 -9
  40. package/fs/ci/node/module.f.d.ts +1 -1
  41. package/fs/ci/node/module.f.js +3 -5
  42. package/fs/ci/playwright/module.f.js +3 -3
  43. package/fs/ci/proof.f.js +14 -14
  44. package/fs/ci/rust/module.f.js +2 -2
  45. package/fs/cli/module.f.js +3 -3
  46. package/fs/cli/proof.f.js +3 -3
  47. package/fs/{types → common}/monoid/module.f.d.ts +1 -1
  48. package/fs/crypto/hmac/module.f.js +2 -2
  49. package/fs/crypto/hmac/proof.f.js +5 -5
  50. package/fs/crypto/pow/module.f.js +3 -3
  51. package/fs/crypto/pow/proof.f.js +4 -4
  52. package/fs/crypto/secp/module.f.js +2 -2
  53. package/fs/crypto/secp/proof.f.js +3 -3
  54. package/fs/crypto/sha2/module.f.js +2 -2
  55. package/fs/crypto/sha2/proof.f.js +6 -6
  56. package/fs/crypto/sign/module.f.js +4 -4
  57. package/fs/crypto/vdf/module.f.js +1 -1
  58. package/fs/crypto/vdf/proof.f.js +1 -1
  59. package/fs/dev/module.f.js +6 -6
  60. package/fs/dev/package_json/module.f.js +6 -6
  61. package/fs/dev/package_json/proof.f.js +3 -3
  62. package/fs/dev/proof.f.js +2 -2
  63. package/fs/djs/ast/module.f.js +2 -2
  64. package/fs/djs/ast/proof.f.js +4 -4
  65. package/fs/djs/examples/m.f.d.ts +1 -1
  66. package/fs/djs/module.f.d.ts +1 -1
  67. package/fs/djs/module.f.js +5 -5
  68. package/fs/djs/parser/module.f.js +4 -4
  69. package/fs/djs/parser/proof.f.js +19 -7
  70. package/fs/djs/proof.f.js +3 -3
  71. package/fs/djs/serializer/module.f.js +6 -6
  72. package/fs/djs/serializer/proof.f.js +4 -4
  73. package/fs/djs/tokenizer/module.f.js +3 -3
  74. package/fs/djs/tokenizer/proof.f.js +5 -5
  75. package/fs/djs/tokenizer-new/proof.f.js +6 -6
  76. package/fs/djs/transpiler/module.f.d.ts +5 -0
  77. package/fs/djs/transpiler/module.f.js +21 -11
  78. package/fs/djs/transpiler/proof.f.js +5 -5
  79. package/fs/effects/memory/module.f.js +4 -4
  80. package/fs/effects/memory/proof.f.js +3 -3
  81. package/fs/effects/module.f.d.ts +15 -0
  82. package/fs/effects/module.f.js +15 -1
  83. package/fs/effects/node/memory/module.js +2 -2
  84. package/fs/effects/node/memory/proof.js +3 -3
  85. package/fs/effects/node/module.f.js +10 -20
  86. package/fs/effects/node/module.js +13 -11
  87. package/fs/effects/node/proof.f.d.ts +3 -0
  88. package/fs/effects/node/proof.f.js +19 -1
  89. package/fs/effects/node/virtual/module.f.js +7 -8
  90. package/fs/effects/node/virtual/proof.f.d.ts +1 -0
  91. package/fs/effects/node/virtual/proof.f.js +11 -4
  92. package/fs/effects/proof.f.d.ts +5 -0
  93. package/fs/effects/proof.f.js +22 -1
  94. package/fs/emergent_testing/all.test.js +2 -2
  95. package/fs/emergent_testing/example.f.d.ts +1 -1
  96. package/fs/emergent_testing/module.f.js +6 -6
  97. package/fs/emergent_testing/proof.f.d.ts +19 -17
  98. package/fs/emergent_testing/proof.f.js +7 -7
  99. package/fs/emergent_testing/scenarios/all.js +1 -1
  100. package/fs/fsc/bnf.f.js +2 -2
  101. package/fs/fsc/json.f.js +1 -1
  102. package/fs/fsc/module.f.d.ts +1 -0
  103. package/fs/fsc/module.f.js +11 -7
  104. package/fs/fsc/proof.f.js +4 -4
  105. package/fs/fsm/module.f.js +9 -9
  106. package/fs/fsm/proof.f.js +7 -7
  107. package/fs/js/tokenizer/module.f.d.ts +1 -1
  108. package/fs/js/tokenizer/module.f.js +14 -8
  109. package/fs/js/tokenizer/proof.f.js +64 -5
  110. package/fs/mcp/module.f.d.ts +13 -13
  111. package/fs/mcp/module.f.js +8 -8
  112. package/fs/mcp/proof.f.js +5 -5
  113. package/fs/mcp/stdio/module.f.d.ts +2 -2
  114. package/fs/mcp/stdio/module.f.js +10 -10
  115. package/fs/mcp/stdio/proof.f.js +10 -10
  116. package/fs/{html → media/html}/module.f.d.ts +3 -3
  117. package/fs/{html → media/html}/module.f.js +9 -9
  118. package/fs/media/json/module.f.d.ts +50 -0
  119. package/fs/{json → media/json}/module.f.js +7 -7
  120. package/fs/{json → media/json}/parser/module.f.d.ts +2 -2
  121. package/fs/{json → media/json}/parser/module.f.js +7 -7
  122. package/fs/{json → media/json}/parser/proof.f.js +24 -6
  123. package/fs/{json → media/json}/proof.f.js +4 -4
  124. package/fs/media/json/rpc/module.f.d.ts +114 -0
  125. package/fs/{json → media/json}/rpc/module.f.js +3 -3
  126. package/fs/{json → media/json}/rpc/proof.f.js +4 -4
  127. package/fs/media/json/schema/module.f.d.ts +78 -0
  128. package/fs/{json → media/json}/schema/module.f.js +2 -2
  129. package/fs/{json → media/json}/schema/proof.f.js +3 -3
  130. package/fs/{json → media/json}/serializer/module.f.d.ts +1 -1
  131. package/fs/{json → media/json}/serializer/module.f.js +2 -2
  132. package/fs/{json → media/json}/serializer/proof.f.js +2 -2
  133. package/fs/{json → media/json}/tokenizer/module.f.d.ts +2 -2
  134. package/fs/{json → media/json}/tokenizer/module.f.js +4 -4
  135. package/fs/{json → media/json}/tokenizer/proof.f.js +5 -5
  136. package/fs/media/module.f.d.ts +37 -0
  137. package/fs/media/module.f.js +24 -0
  138. package/fs/media/proof.f.d.ts +9 -0
  139. package/fs/media/proof.f.js +57 -0
  140. package/fs/media/revision/module.f.d.ts +51 -0
  141. package/fs/media/revision/module.f.js +110 -0
  142. package/fs/media/revision/proof.f.d.ts +29 -0
  143. package/fs/media/revision/proof.f.js +119 -0
  144. package/fs/{mime → media/type}/module.f.d.ts +6 -6
  145. package/fs/{mime → media/type}/module.f.js +6 -6
  146. package/fs/{mime → media/type}/proof.f.js +6 -6
  147. package/fs/module.f.d.ts +2 -0
  148. package/fs/{fjs/module.f.js → module.f.js} +8 -8
  149. package/fs/module.js +4 -0
  150. package/fs/path/module.f.d.ts +6 -0
  151. package/fs/path/module.f.js +8 -2
  152. package/fs/path/proof.f.d.ts +1 -0
  153. package/fs/path/proof.f.js +40 -2
  154. package/fs/{fjs/proof.f.d.ts → proof.f.d.ts} +1 -0
  155. package/fs/{fjs/proof.f.js → proof.f.js} +10 -4
  156. package/fs/sul/id/module.f.js +9 -9
  157. package/fs/sul/id/proof.f.js +5 -5
  158. package/fs/sul/level/hash/module.f.js +3 -3
  159. package/fs/sul/level/hash/proof.f.js +3 -3
  160. package/fs/sul/level/literal/module.f.js +5 -5
  161. package/fs/sul/level/literal/proof.f.js +3 -3
  162. package/fs/sul/module.f.js +3 -3
  163. package/fs/sul/proof.f.js +3 -3
  164. package/fs/text/ascii/proof.f.js +3 -3
  165. package/fs/text/code_point/module.f.js +2 -2
  166. package/fs/text/code_point/proof.f.js +1 -1
  167. package/fs/text/module.f.js +5 -5
  168. package/fs/text/proof.f.js +4 -4
  169. package/fs/text/sgr/module.f.js +2 -2
  170. package/fs/text/sgr/proof.f.js +2 -2
  171. package/fs/text/utf16/module.f.js +5 -5
  172. package/fs/text/utf16/proof.f.js +4 -4
  173. package/fs/text/utf8/module.f.d.ts +6 -0
  174. package/fs/text/utf8/module.f.js +37 -19
  175. package/fs/text/utf8/proof.f.js +58 -5
  176. package/fs/types/array/module.f.js +1 -1
  177. package/fs/types/array/proof.f.js +3 -3
  178. package/fs/types/bigfloat/module.f.js +1 -1
  179. package/fs/types/bigfloat/proof.f.js +1 -1
  180. package/fs/types/bigint/module.f.js +2 -2
  181. package/fs/types/bigint/proof.f.js +3 -3
  182. package/fs/types/bit_vec/module.f.js +8 -8
  183. package/fs/types/bit_vec/proof.f.js +5 -5
  184. package/fs/types/btree/find/module.f.js +1 -1
  185. package/fs/types/btree/find/proof.f.d.ts +3 -1
  186. package/fs/types/btree/find/proof.f.js +7 -7
  187. package/fs/types/btree/module.f.js +2 -2
  188. package/fs/types/btree/proof.f.d.ts +11 -5
  189. package/fs/types/btree/proof.f.js +7 -7
  190. package/fs/types/btree/remove/module.f.js +4 -4
  191. package/fs/types/btree/remove/proof.f.d.ts +5 -2
  192. package/fs/types/btree/remove/proof.f.js +5 -5
  193. package/fs/types/btree/set/module.f.js +3 -3
  194. package/fs/types/btree/set/proof.f.js +4 -4
  195. package/fs/types/byte_set/module.f.js +2 -2
  196. package/fs/types/byte_set/proof.f.js +4 -4
  197. package/fs/types/function/compare/proof.f.js +1 -1
  198. package/fs/types/function/operator/proof.f.d.ts +21 -10
  199. package/fs/types/function/operator/proof.f.js +1 -1
  200. package/fs/types/function/proof.f.js +1 -1
  201. package/fs/types/list/module.f.js +2 -2
  202. package/fs/types/list/proof.f.d.ts +15 -7
  203. package/fs/types/list/proof.f.js +4 -4
  204. package/fs/types/map/proof.f.js +2 -2
  205. package/fs/types/nibble_set/proof.f.js +2 -2
  206. package/fs/types/nullable/module.f.js +2 -2
  207. package/fs/types/nullable/proof.f.js +1 -1
  208. package/fs/types/number/module.f.js +5 -5
  209. package/fs/types/number/proof.f.js +1 -1
  210. package/fs/types/object/module.f.js +4 -4
  211. package/fs/types/object/proof.f.js +1 -1
  212. package/fs/types/ordered_map/module.f.js +6 -6
  213. package/fs/types/ordered_map/proof.f.js +3 -3
  214. package/fs/types/patricia_trie/proof.f.d.ts +11 -5
  215. package/fs/types/patricia_trie/proof.f.js +2 -2
  216. package/fs/types/prime_field/module.f.js +2 -2
  217. package/fs/types/prime_field/proof.f.js +2 -2
  218. package/fs/types/range/proof.f.js +1 -1
  219. package/fs/types/range_map/module.f.js +4 -4
  220. package/fs/types/range_map/proof.f.js +7 -7
  221. package/fs/types/result/module.f.d.ts +4 -0
  222. package/fs/types/result/module.f.js +4 -0
  223. package/fs/types/result/module.js +1 -1
  224. package/fs/types/result/proof.f.d.ts +9 -3
  225. package/fs/types/result/proof.f.js +12 -2
  226. package/fs/types/rtti/common/module.f.js +4 -4
  227. package/fs/types/rtti/module.f.d.ts +2 -2
  228. package/fs/types/rtti/module.f.js +1 -1
  229. package/fs/types/rtti/parse/module.f.js +5 -5
  230. package/fs/types/rtti/parse/proof.f.js +2 -2
  231. package/fs/types/rtti/ts/module.f.js +1 -1
  232. package/fs/types/rtti/ts/proof.f.js +2 -2
  233. package/fs/types/rtti/validate/module.f.js +4 -4
  234. package/fs/types/rtti/validate/proof.f.js +2 -2
  235. package/fs/types/sorted_list/module.f.js +3 -3
  236. package/fs/types/sorted_list/proof.f.js +6 -6
  237. package/fs/types/sorted_set/module.f.js +1 -1
  238. package/fs/types/sorted_set/proof.f.js +6 -6
  239. package/fs/types/string/module.f.js +4 -4
  240. package/fs/types/string/proof.f.js +2 -2
  241. package/fs/types/string_set/module.f.js +6 -6
  242. package/fs/types/string_set/proof.f.js +1 -1
  243. package/fs/types/ts/proof.f.d.ts +18 -18
  244. package/fs/types/ts/proof.f.js +1 -1
  245. package/fs/types/uint8array/module.f.js +4 -4
  246. package/fs/types/uint8array/proof.f.js +4 -4
  247. package/fs/website/module.f.js +3 -3
  248. package/fs/website/proof.f.js +3 -3
  249. package/package.json +8 -8
  250. package/fs/base64/module.f.js +0 -60
  251. package/fs/fjs/module.f.d.ts +0 -2
  252. package/fs/fjs/module.js +0 -4
  253. package/fs/json/module.f.d.ts +0 -50
  254. package/fs/json/rpc/module.f.d.ts +0 -114
  255. package/fs/json/schema/module.f.d.ts +0 -77
  256. /package/fs/{base128 → basen/base128}/proof.f.d.ts +0 -0
  257. /package/fs/{cbase32 → basen/cbase32}/proof.f.d.ts +0 -0
  258. /package/fs/{types → common}/monoid/module.f.js +0 -0
  259. /package/fs/{types → common}/monoid/proof.f.d.ts +0 -0
  260. /package/fs/{types → common}/monoid/proof.f.js +0 -0
  261. /package/fs/{html → media/html}/proof.f.d.ts +0 -0
  262. /package/fs/{html → media/html}/proof.f.js +0 -0
  263. /package/fs/{json → media/json}/parser/proof.f.d.ts +0 -0
  264. /package/fs/{json → media/json}/proof.f.d.ts +0 -0
  265. /package/fs/{json → media/json}/rpc/proof.f.d.ts +0 -0
  266. /package/fs/{json → media/json}/schema/proof.f.d.ts +0 -0
  267. /package/fs/{json → media/json}/serializer/proof.f.d.ts +0 -0
  268. /package/fs/{json → media/json}/tokenizer/proof.f.d.ts +0 -0
  269. /package/fs/{mime → media/type}/proof.f.d.ts +0 -0
  270. /package/fs/{fjs/module.d.ts → module.d.ts} +0 -0
@@ -1,18 +1,21 @@
1
- import { assert, assertEq } from "../../asserts/module.f.js";
2
- import { pure } from "../../effects/module.f.js";
3
- import { run } from "../../effects/mock/module.f.js";
4
- import { asBase, asNominal, create } from "../../effects/memory/module.f.js";
5
- import { msb, u8ListToVec, vec8, repeat, length, maxLengthBytes } from "../../types/bit_vec/module.f.js";
6
- import { vecToCBase32 } from "../../cbase32/module.f.js";
7
- import { encode as base64Encode } from "../../base64/module.f.js";
8
- import { utf8 } from "../../text/module.f.js";
9
- import {} from "../module.f.js";
10
- import { mcpStep, uninitializedState, } from "../../mcp/module.f.js";
11
- import { emptyState, virtual } from "../../effects/node/virtual/module.f.js";
12
- import { casConfig, casMcpHandlers } from "./module.f.js";
13
- import { ok as resultOk } from "../../types/result/module.f.js";
14
- import { stdioTransport } from "../../mcp/stdio/module.f.js";
15
- import { fromVec } from "../../types/uint8array/module.f.js";
1
+ import { assert, assertEq } from '../../asserts/module.f.js';
2
+ import { pure } from '../../effects/module.f.js';
3
+ import { run } from '../../effects/mock/module.f.js';
4
+ import { asBase, asNominal, create } from '../../effects/memory/module.f.js';
5
+ import { msb, u8ListToVec, vec8, repeat, length, maxLengthBytes } from '../../types/bit_vec/module.f.js';
6
+ import { vecToCBase32 } from '../../basen/cbase32/module.f.js';
7
+ import { encode as base64Encode } from '../../basen/base64/module.f.js';
8
+ import { utf8 } from '../../text/module.f.js';
9
+ import { fileCas } from '../module.f.js';
10
+ import { dialect as revisionDialect, mediaType as revisionMediaType } from '../../media/revision/module.f.js';
11
+ import { sha256 } from '../../crypto/sha2/module.f.js';
12
+ import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.js';
13
+ import { mcpStep, uninitializedState, } from '../../mcp/module.f.js';
14
+ import { emptyState, virtual } from '../../effects/node/virtual/module.f.js';
15
+ import { casConfig, casMcpHandlers } from './module.f.js';
16
+ import { ok as resultOk } from '../../types/result/module.f.js';
17
+ import { stdioTransport } from '../../mcp/stdio/module.f.js';
18
+ import { fromVec } from '../../types/uint8array/module.f.js';
16
19
  const initialTestState = { memory: { next: 0, values: {} } };
17
20
  const mock = {
18
21
  memCreate: value => state => {
@@ -59,6 +62,19 @@ const runSessionVirtual = (root, home = '/home/user') => (msgs) => {
59
62
  });
60
63
  return virtual({ ...emptyState, root })(effect)[1];
61
64
  };
65
+ // Seeds a blob directly into the virtual store via `c.write`, bypassing the
66
+ // MCP `cas_add` tool entirely — used by tests that need a stored blob larger
67
+ // than `cas_add`'s inline limit, now that the removed `type:'url'` path is no
68
+ // longer available to stage one through MCP. Returns the resulting root
69
+ // `Dir` (with the new shard written) and the blob's cBase32 hash so a
70
+ // subsequent `runSessionVirtual` call can read it back via `cas_get`.
71
+ const seedBlob = (root, home = '/home/user') => (chunks) => {
72
+ const c = fileCas(sha256)(home);
73
+ const stream = chunks.reduceRight((tail, chunk) => nonEmpty(resultOk(chunk), tail), elEmpty());
74
+ const [state, result] = virtual({ ...emptyState, root })(c.write(stream));
75
+ assert(result[0] === 'ok', result);
76
+ return [state.root, vecToCBase32(result[1])];
77
+ };
62
78
  // ── Messages ────────────────────────────────────────────────────────────────────
63
79
  const init = { jsonrpc: '2.0', method: 'initialize', id: 1,
64
80
  params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'c', version: '0' } } };
@@ -89,14 +105,18 @@ const item0 = (resp) => resultOf(resp).content[0];
89
105
  const textOf = (resp) => item0(resp).text;
90
106
  // A plain text sample for text add→get round-trips.
91
107
  const textSample = 'hello, world!';
108
+ // A valid `vnd.fjs.revision` blob: zero parents, so `subject` must be (and is,
109
+ // per `fs/media/revision/proof.f.ts`) a valid cbase32 hash on its own.
110
+ const revisionSample = `{"dialect":"${revisionDialect}","subject":"8","parents":[]}`;
92
111
  // A base64-encoded binary payload for binary add→get round-trips.
93
112
  const binarySample = base64Encode(vec8(0x2an));
94
113
  // A base64 blob whose leading bytes are the PNG magic-byte signature, so
95
- // `cas_get` detects its type and returns base64 with mime_type image/png.
114
+ // `cas_get` detects its type and returns base64 with mimeType image/png.
96
115
  const pngSample = base64Encode(u8ListToVec(msb)([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x01]));
97
116
  // Returns the RFC 4648 base64 encoding of `n` zero bytes, computed directly
98
- // without bigint arithmetic — safe near maxLengthBytes where base64Encode on a
99
- // padded Vec would trigger the encode-padding-overflow bug. Handles every n%3
117
+ // without bigint arithmetic — independent of `base64.encode` so these tests
118
+ // don't rely on the correctness of the function they're indirectly exercising
119
+ // (`cas_add` decodes the string via `base64.decode`). Handles every n%3
100
120
  // residue: rem=0 → no padding, rem=1 → 'AA==', rem=2 → 'AAA='.
101
121
  const base64OfA = (n) => {
102
122
  const groups = Number(n / 3n);
@@ -117,24 +137,18 @@ const base64OfA = (n) => {
117
137
  // exactly `maxLengthBytes` (the largest single `Vec` the runtime allows), so the
118
138
  // two-chunk blob spans two read chunks without any one `Vec` exceeding the cap.
119
139
  const largeMultiChunkBlobMeta = (chunk0, chunk1, expectedType, expectedMime) => () => {
120
- const root = { 'home': { 'user': { 'cas_upload': { 'big': [chunk0, chunk1] } } } };
121
- const [addResp] = runSessionVirtual(root)([
122
- init, initialized,
123
- call(2, 'cas_add', { content: '/home/user/cas_upload/big', type: 'url' }),
124
- ]).slice(2);
125
- assert(!resultOf(addResp).isError);
126
- const hash = textOf(addResp);
127
- const [, metaResp] = runSessionVirtual(root)([
140
+ const [root, hash] = seedBlob({})([chunk0, chunk1]);
141
+ const [metaResp] = runSessionVirtual(root)([
128
142
  init, initialized,
129
- call(2, 'cas_add', { content: '/home/user/cas_upload/big', type: 'url' }),
130
- call(3, 'cas_get', { hash }),
143
+ call(2, 'cas_get', { hash }),
131
144
  ]).slice(2);
132
145
  assert(!resultOf(metaResp).isError);
133
146
  const meta = JSON.parse(textOf(metaResp));
134
147
  assertEq(meta.type, expectedType);
135
- assertEq(meta.mime_type, expectedMime);
148
+ assertEq(meta.mimeType, expectedMime);
136
149
  assertEq(meta.length, Number((length(chunk0) + length(chunk1)) / 8n));
137
- assertEq(meta.content, undefined);
150
+ assertEq(meta.text, undefined);
151
+ assertEq(meta.blob, undefined);
138
152
  };
139
153
  // A full `maxLengthBytes`-long chunk of repeated ASCII 'a' — valid UTF-8.
140
154
  const asciiChunk = repeat(maxLengthBytes)(vec8(0x61n));
@@ -145,8 +159,7 @@ const binaryChunk = repeat(maxLengthBytes)(vec8(0xffn));
145
159
  // 100,000 raw bytes of 0xFF: comfortably under `maxLengthBytes` (so `cas_get`'s
146
160
  // own `length > maxLengthBytes` guard does not fire), but base64 inflates it to
147
161
  // ceil(100_000/3)*4 = 133,336 chars — already past `maxLength` (131,072 bytes)
148
- // before the JSON-RPC envelope adds anything. Small enough to stay clear of the
149
- // separate `base64Encode`-near-`maxLength` padding bug (see `base64OfA` above).
162
+ // before the JSON-RPC envelope adds anything.
150
163
  const oversizedBase64Chunk = repeat(100000n)(vec8(0xffn));
151
164
  // 90,000 raw bytes: base64 inflates to 120,000 chars, leaving ~11 KiB of margin
152
165
  // under `maxLength` once the envelope is added — the paired "still succeeds" case.
@@ -170,16 +183,10 @@ export const proof = {
170
183
  // "too large" error, not "no such hash" — the blob exists, it just can't be
171
184
  // buffered inline. The metadata-only path (above) still returns its size/type.
172
185
  getContentLargeBlobTooLargeError: () => {
173
- const root = { 'home': { 'user': { 'cas_upload': { 'big': [asciiChunk, asciiChunk] } } } };
174
- const [addResp] = runSessionVirtual(root)([
186
+ const [root, hash] = seedBlob({})([asciiChunk, asciiChunk]);
187
+ const [getResp] = runSessionVirtual(root)([
175
188
  init, initialized,
176
- call(2, 'cas_add', { content: '/home/user/cas_upload/big', type: 'url' }),
177
- ]).slice(2);
178
- const hash = textOf(addResp);
179
- const [, getResp] = runSessionVirtual(root)([
180
- init, initialized,
181
- call(2, 'cas_add', { content: '/home/user/cas_upload/big', type: 'url' }),
182
- call(3, 'cas_get', { hash, content: true }),
189
+ call(2, 'cas_get', { hash, content: true }),
183
190
  ]).slice(2);
184
191
  assertEq(resultOf(getResp).isError, true);
185
192
  const text = textOf(getResp);
@@ -205,34 +212,24 @@ export const proof = {
205
212
  // This test originally timed out under `bun test`'s native 5s per-test
206
213
  // limit (12-14s observed in CI on PR #1201) — the cost was in
207
214
  // `base64Encode`, quadratic before the `baseN.vecToString` fix (see
208
- // `fs/base64/proof.f.ts` `encodeLargeVecIsSlow`). Now well under budget on
215
+ // `fs/basen/base64/proof.f.ts` `encodeLargeVecIsSlow`). Now well under budget on
209
216
  // both engines.
210
217
  getContentBase64InflationOverflowWritesInternalError: () => {
211
- const root = { 'home': { 'user': { 'cas_upload': { 'big': [oversizedBase64Chunk] } } } };
212
- const [addResp] = runStdio(root)([
213
- call(2, 'cas_add', { content: '/home/user/cas_upload/big', type: 'url' }),
214
- ]);
215
- const hash = textOf(addResp);
216
- const [, getResp] = runStdio(root)([
217
- call(2, 'cas_add', { content: '/home/user/cas_upload/big', type: 'url' }),
218
- call(3, 'cas_get', { hash, content: true }),
218
+ const [root, hash] = seedBlob({})([oversizedBase64Chunk]);
219
+ const [getResp] = runStdio(root)([
220
+ call(2, 'cas_get', { hash, content: true }),
219
221
  ]);
220
222
  const err = getResp;
221
223
  assertEq(err.error?.code, -32603);
222
- assertEq(err.id, 3);
224
+ assertEq(err.id, 2);
223
225
  },
224
226
  // The paired boundary case: a blob whose base64 inflation leaves enough
225
227
  // margin under `maxLength` for the envelope — confirms the fallback above
226
228
  // only triggers on genuine overflow, not on every `content:true` binary read.
227
229
  getContentBase64NearBoundarySucceeds: () => {
228
- const root = { 'home': { 'user': { 'cas_upload': { 'big': [boundaryBase64Chunk] } } } };
229
- const [addResp] = runStdio(root)([
230
- call(2, 'cas_add', { content: '/home/user/cas_upload/big', type: 'url' }),
231
- ]);
232
- const hash = textOf(addResp);
233
- const [, getResp] = runStdio(root)([
234
- call(2, 'cas_add', { content: '/home/user/cas_upload/big', type: 'url' }),
235
- call(3, 'cas_get', { hash, content: true }),
230
+ const [root, hash] = seedBlob({})([boundaryBase64Chunk]);
231
+ const [getResp] = runStdio(root)([
232
+ call(2, 'cas_get', { hash, content: true }),
236
233
  ]);
237
234
  assert(!resultOf(getResp).isError);
238
235
  const result = JSON.parse(textOf(getResp));
@@ -243,25 +240,21 @@ export const proof = {
243
240
  // (33,000 bytes) well under `maxLengthBytes`, but each quote costs 2 bytes on
244
241
  // the inner CAS-JSON escape and 4 bytes once the outer JSON-RPC envelope
245
242
  // re-escapes that backslash-quote pair — no `cas_get`-level size guard could
246
- // see this coming, only the transport's real encode attempt can. Uploaded via
247
- // `type: 'url'` (a file, not an inlined JSON string) so the test exercises
248
- // `cas_get`'s response-side escaping without also paying for parsing a huge
249
- // inline `content` argument on the request side. Confirms `writeResponse`'s
243
+ // see this coming, only the transport's real encode attempt can. Seeded
244
+ // directly into the store via `seedBlob` (not an inlined `cas_add` JSON
245
+ // string) so the test exercises `cas_get`'s response-side escaping without
246
+ // also paying for parsing a huge inline `content` argument on the request
247
+ // side. Confirms `writeResponse`'s
250
248
  // `tryUtf8` fallback (not a crash) handles double-escaping overflow, again
251
249
  // preserving the request's `id`.
252
250
  getContentDoubleEscapedOverflowWritesInternalError: () => {
253
- const root = { 'home': { 'user': { 'cas_upload': { 'q': [quotesChunk] } } } };
254
- const [addResp] = runStdio(root)([
255
- call(2, 'cas_add', { content: '/home/user/cas_upload/q', type: 'url' }),
256
- ]);
257
- const hash = textOf(addResp);
258
- const [, getResp] = runStdio(root)([
259
- call(2, 'cas_add', { content: '/home/user/cas_upload/q', type: 'url' }),
260
- call(3, 'cas_get', { hash, content: true }),
251
+ const [root, hash] = seedBlob({})([quotesChunk]);
252
+ const [getResp] = runStdio(root)([
253
+ call(2, 'cas_get', { hash, content: true }),
261
254
  ]);
262
255
  const err = getResp;
263
256
  assertEq(err.error?.code, -32603);
264
- assertEq(err.id, 3);
257
+ assertEq(err.id, 2);
265
258
  },
266
259
  toolsListAdvertisesThreeTools: () => {
267
260
  const [resp] = runSessionVirtual({})([init, initialized, list(2)]).slice(2);
@@ -283,10 +276,10 @@ export const proof = {
283
276
  const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash }));
284
277
  assert(!resultOf(getResp).isError);
285
278
  const result = JSON.parse(textOf(getResp));
286
- assertEq(result.mime_type, 'text/plain');
279
+ assertEq(result.mimeType, 'text/plain');
287
280
  assertEq(result.type, 'text');
288
281
  assertEq(result.length, textSample.length);
289
- assertEq(result.content, undefined);
282
+ assertEq(result.text, undefined);
290
283
  },
291
284
  // cas_get with content:true returns inline content.
292
285
  textAddGetContentRoundTrips: () => {
@@ -296,8 +289,8 @@ export const proof = {
296
289
  assert(!resultOf(getResp).isError);
297
290
  const result = JSON.parse(textOf(getResp));
298
291
  assertEq(result.type, 'text');
299
- assertEq(result.mime_type, 'text/plain');
300
- assertEq(result.content, textSample);
292
+ assertEq(result.mimeType, 'text/plain');
293
+ assertEq(result.text, textSample);
301
294
  },
302
295
  // Binary add→get round-trip: store as base64, retrieve metadata without content.
303
296
  binaryAddGetMetaRoundTrips: () => {
@@ -306,9 +299,9 @@ export const proof = {
306
299
  const [, getResp] = session(call(2, 'cas_add', { content: binarySample, type: 'base64' }), call(3, 'cas_get', { hash }));
307
300
  assert(!resultOf(getResp).isError);
308
301
  const result = JSON.parse(textOf(getResp));
309
- assertEq(result.content, undefined);
302
+ assertEq(result.text, undefined);
310
303
  assertEq(result.type, 'text');
311
- assertEq(result.mime_type, 'text/plain');
304
+ assertEq(result.mimeType, 'text/plain');
312
305
  },
313
306
  // Binary add→get with content:true returns inline base64 content.
314
307
  binaryAddGetContentRoundTrips: () => {
@@ -318,7 +311,7 @@ export const proof = {
318
311
  assert(!resultOf(getResp).isError);
319
312
  const result = JSON.parse(textOf(getResp));
320
313
  assertEq(result.type, 'text');
321
- assertEq(result.content, '*');
314
+ assertEq(result.text, '*');
322
315
  },
323
316
  addGetRoundTrips: () => {
324
317
  const [addResp] = session(call(2, 'cas_add', { content: textSample }));
@@ -326,7 +319,7 @@ export const proof = {
326
319
  const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash, content: true }));
327
320
  assert(!resultOf(getResp).isError);
328
321
  const result = JSON.parse(textOf(getResp));
329
- assertEq(result.content, textSample);
322
+ assertEq(result.text, textSample);
330
323
  },
331
324
  // Text content (no magic-byte match, valid UTF-8) comes back with type:'text' when content requested.
332
325
  getUntypedReturnsText: () => {
@@ -336,9 +329,9 @@ export const proof = {
336
329
  assertEq(item0(getResp) === null ? null : item0(getResp).type, 'text');
337
330
  const result = JSON.parse(textOf(getResp));
338
331
  assertEq(result.type, 'text');
339
- assertEq(result.mime_type, 'text/plain');
332
+ assertEq(result.mimeType, 'text/plain');
340
333
  },
341
- // Bytes with a recognised PNG signature come back as base64 with mime_type image/png when content requested.
334
+ // Bytes with a recognised PNG signature come back as base64 with mimeType image/png when content requested.
342
335
  getTypedReturnsBinaryJson: () => {
343
336
  const [addResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }));
344
337
  const hash = textOf(addResp);
@@ -347,8 +340,33 @@ export const proof = {
347
340
  assertEq(item0(getResp).type, 'text');
348
341
  const result = JSON.parse(textOf(getResp));
349
342
  assertEq(result.type, 'base64');
350
- assertEq(result.mime_type, 'image/png');
351
- assertEq(result.content, pngSample);
343
+ assertEq(result.mimeType, 'image/png');
344
+ assertEq(result.blob, pngSample);
345
+ },
346
+ // A stored `vnd.fjs.revision` blob is recognized by metadata-only
347
+ // `cas_get` (content:false, the default) — the CAS read path routes the
348
+ // bounded, whole-blob-text case through the dialect-aware detector rather
349
+ // than only reporting the streaming text/plain verdict.
350
+ getMetaRecognizesRevisionDialect: () => {
351
+ const [addResp] = session(call(2, 'cas_add', { content: revisionSample }));
352
+ const hash = textOf(addResp);
353
+ const [, metaResp] = session(call(2, 'cas_add', { content: revisionSample }), call(3, 'cas_get', { hash }));
354
+ assert(!resultOf(metaResp).isError);
355
+ const meta = JSON.parse(textOf(metaResp));
356
+ assertEq(meta.mimeType, revisionMediaType);
357
+ assertEq(meta.type, 'text');
358
+ },
359
+ // Same dialect recognition on the `content: true` path, alongside the
360
+ // inline text payload.
361
+ getContentRecognizesRevisionDialect: () => {
362
+ const [addResp] = session(call(2, 'cas_add', { content: revisionSample }));
363
+ const hash = textOf(addResp);
364
+ const [, getResp] = session(call(2, 'cas_add', { content: revisionSample }), call(3, 'cas_get', { hash, content: true }));
365
+ assert(!resultOf(getResp).isError);
366
+ const result = JSON.parse(textOf(getResp));
367
+ assertEq(result.mimeType, revisionMediaType);
368
+ assertEq(result.type, 'text');
369
+ assertEq(result.text, revisionSample);
352
370
  },
353
371
  listEnumeratesStoredHashes: () => {
354
372
  const [addResp, listResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_list', {}));
@@ -370,6 +388,7 @@ export const proof = {
370
388
  const [resp] = session(call(2, 'cas_add', { content: 'A'.repeat(174_764), type: 'base64' }));
371
389
  assertEq(resultOf(resp).isError, true);
372
390
  assert(textOf(resp).includes('too large or malformed'));
391
+ assert(textOf(resp).includes('cas add'));
373
392
  },
374
393
  // One ASCII byte past maxLengthBytes — tryUtf8 returns null and cas_add
375
394
  // surfaces an error.
@@ -377,6 +396,7 @@ export const proof = {
377
396
  const [resp] = session(call(2, 'cas_add', { content: 'a'.repeat(Number(maxLengthBytes) + 1) }));
378
397
  assertEq(resultOf(resp).isError, true);
379
398
  assert(textOf(resp).includes('too large or malformed'));
399
+ assert(textOf(resp).includes('cas add'));
380
400
  },
381
401
  // Exactly maxLengthBytes (131,072) ASCII bytes — tryUtf8 returns a non-null
382
402
  // Vec (len === maxLength, not over), so cas_add stores cleanly.
@@ -385,15 +405,14 @@ export const proof = {
385
405
  assert(!resultOf(resp).isError);
386
406
  assert(textOf(resp).length > 0);
387
407
  },
388
- // base64 of exactly maxLengthBytes — currently returns isError because
389
- // base64Decode measures the raw pre-trim body (1_048_578 bits) against
390
- // maxLength before stripping the 2 padding bits that would land it exactly at
391
- // maxLength. Assert the current failing behavior; flip to a success assertion
392
- // once `fs/base64/todo/decode-rejects-max-size-input.md` is fixed.
393
- addBase64AtLimitIsError: () => {
408
+ // base64 of exactly maxLengthBytes — base64Decode trims the encoding's
409
+ // padding bits from the last character before measuring the result
410
+ // against maxLength, so a payload landing exactly at the limit decodes
411
+ // and stores cleanly.
412
+ addBase64AtLimitSucceeds: () => {
394
413
  const [resp] = session(call(2, 'cas_add', { content: base64OfA(maxLengthBytes), type: 'base64' }));
395
- assertEq(resultOf(resp).isError, true);
396
- assert(textOf(resp).includes('too large or malformed'));
414
+ assert(!resultOf(resp).isError);
415
+ assert(textOf(resp).length > 0);
397
416
  },
398
417
  getUnterminatedHashIsError: () => {
399
418
  const [resp] = session(call(2, 'cas_get', { hash: '0' }));
@@ -425,66 +444,17 @@ export const proof = {
425
444
  assert(!('error' in resp));
426
445
  assert('result' in resp);
427
446
  },
428
- // cas_add with type:'url' streams a file from /home/user/cas_upload/ into CAS.
429
- addUrlStoresFileAndReturnsHash: () => {
430
- const fileContent = utf8('hello from file');
431
- const root = { 'home': { 'user': { 'cas_upload': { 'hello.txt': [fileContent] } } } };
432
- const [addUrlResp] = runSessionVirtual(root)([
433
- init, initialized,
434
- call(2, 'cas_add', { content: '/home/user/cas_upload/hello.txt', type: 'url' }),
435
- ]).slice(2);
436
- assert(!resultOf(addUrlResp).isError);
437
- assert(textOf(addUrlResp).length > 0);
438
- },
439
- addUrlRoundTrips: () => {
440
- const fileContent = utf8('round-trip content');
441
- const root = { 'home': { 'user': { 'cas_upload': { 'rt.txt': [fileContent] } } } };
442
- // First pass: add to get the hash (deterministic for same content).
443
- const [addResp] = runSessionVirtual(root)([
444
- init, initialized,
445
- call(2, 'cas_add', { content: '/home/user/cas_upload/rt.txt', type: 'url' }),
446
- ]).slice(2);
447
- const hash = textOf(addResp);
448
- // Second pass: add again + get in one session (file re-present in fresh virtual state).
449
- const [, getResp] = runSessionVirtual(root)([
450
- init, initialized,
451
- call(2, 'cas_add', { content: '/home/user/cas_upload/rt.txt', type: 'url' }),
452
- call(3, 'cas_get', { hash, content: true }),
453
- ]).slice(2);
454
- assert(!resultOf(getResp).isError);
455
- const result = JSON.parse(textOf(getResp));
456
- assertEq(result.type, 'text');
457
- assertEq(result.content, 'round-trip content');
458
- },
459
- addUrlMissingFileIsError: () => {
460
- const [resp] = runSessionVirtual({})([
461
- init, initialized,
462
- call(2, 'cas_add', { content: '/home/user/cas_upload/nonexistent.txt', type: 'url' }),
463
- ]).slice(2);
464
- assertEq(resultOf(resp).isError, true);
465
- },
466
447
  // cas_get without content:true returns only metadata.
467
448
  getMetaReturnsLengthAndMimeType: () => {
468
- const fileContent = utf8('text content');
469
- const root = { 'home': { 'user': { 'cas_upload': { 'f': [fileContent] } } } };
470
- // First pass: add to get the hash.
471
- const [addResp] = runSessionVirtual(root)([
472
- init, initialized,
473
- call(2, 'cas_add', { content: '/home/user/cas_upload/f', type: 'url' }),
474
- ]).slice(2);
449
+ const [addResp] = session(call(2, 'cas_add', { content: 'text content' }));
475
450
  const hash = textOf(addResp);
476
- // Second pass: add + get metadata.
477
- const [, metaResp] = runSessionVirtual(root)([
478
- init, initialized,
479
- call(2, 'cas_add', { content: '/home/user/cas_upload/f', type: 'url' }),
480
- call(3, 'cas_get', { hash }),
481
- ]).slice(2);
451
+ const [, metaResp] = session(call(2, 'cas_add', { content: 'text content' }), call(3, 'cas_get', { hash }));
482
452
  assert(!resultOf(metaResp).isError);
483
453
  const meta = JSON.parse(textOf(metaResp));
484
- assertEq(meta.mime_type, 'text/plain');
454
+ assertEq(meta.mimeType, 'text/plain');
485
455
  assertEq(meta.type, 'text');
486
456
  assertEq(meta.length, 12);
487
- assertEq(meta.content, undefined);
457
+ assertEq(meta.text, undefined);
488
458
  },
489
459
  getMetaBinaryBlob: () => {
490
460
  const [addResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }));
@@ -492,10 +462,10 @@ export const proof = {
492
462
  const [, metaResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }), call(3, 'cas_get', { hash }));
493
463
  assert(!resultOf(metaResp).isError);
494
464
  const meta = JSON.parse(textOf(metaResp));
495
- assertEq(meta.mime_type, 'image/png');
465
+ assertEq(meta.mimeType, 'image/png');
496
466
  assertEq(meta.type, 'base64');
497
467
  assertEq(meta.length, 10);
498
- assertEq(meta.content, undefined);
468
+ assertEq(meta.blob, undefined);
499
469
  },
500
470
  getMetaOctetStreamForUnknownBinary: () => {
501
471
  const binaryContent = u8ListToVec(msb)([0xFF, 0xFE, 0x00, 0x01]);
@@ -505,9 +475,9 @@ export const proof = {
505
475
  const [, metaResp] = session(call(2, 'cas_add', { content: binaryB64, type: 'base64' }), call(3, 'cas_get', { hash }));
506
476
  assert(!resultOf(metaResp).isError);
507
477
  const meta = JSON.parse(textOf(metaResp));
508
- assertEq(meta.mime_type, 'application/octet-stream');
478
+ assertEq(meta.mimeType, 'application/octet-stream');
509
479
  assertEq(meta.type, 'base64');
510
- assertEq(meta.content, undefined);
480
+ assertEq(meta.blob, undefined);
511
481
  },
512
482
  // A NUL-bearing blob is valid UTF-8 yet binary: cas_get must report
513
483
  // base64/octet-stream, not text/plain.
@@ -519,7 +489,7 @@ export const proof = {
519
489
  const [, metaResp] = session(call(2, 'cas_add', { content: nulB64, type: 'base64' }), call(3, 'cas_get', { hash }));
520
490
  assert(!resultOf(metaResp).isError);
521
491
  const meta = JSON.parse(textOf(metaResp));
522
- assertEq(meta.mime_type, 'application/octet-stream');
492
+ assertEq(meta.mimeType, 'application/octet-stream');
523
493
  assertEq(meta.type, 'base64');
524
494
  },
525
495
  getMetaMissingHashIsError: () => {
@@ -531,40 +501,6 @@ export const proof = {
531
501
  const [resp] = session(call(2, 'cas_get', { hash: 'bad!' }));
532
502
  assertEq(resultOf(resp).isError, true);
533
503
  },
534
- // cas_add type:'url' with a subdirectory path flattens slashes to '-' in staging.
535
- addUrlFromSubdirectorySucceeds: () => {
536
- const fileContent = utf8('nested file content');
537
- const root = { 'home': { 'user': { 'cas_upload': { 'subdir': { 'file.txt': [fileContent] } } } } };
538
- const [resp] = runSessionVirtual(root)([
539
- init, initialized,
540
- call(2, 'cas_add', { content: '/home/user/cas_upload/subdir/file.txt', type: 'url' }),
541
- ]).slice(2);
542
- assert(!resultOf(resp).isError);
543
- assert(textOf(resp).length > 0);
544
- },
545
- // cas_add with type:'url' accepts paths within /home/user/cas_upload/
546
- addUrlFromApprovedDirectorySucceeds: () => {
547
- const fileContent = utf8('approved file');
548
- const root = { 'home': { 'user': { 'cas_upload': { 'test.txt': [fileContent] } } } };
549
- const [resp] = runSessionVirtual(root)([
550
- init, initialized,
551
- call(2, 'cas_add', { content: '/home/user/cas_upload/test.txt', type: 'url' }),
552
- ]).slice(2);
553
- assert(!resultOf(resp).isError);
554
- assert(textOf(resp).length > 0);
555
- },
556
- // cas_add with type:'url' rejects paths outside /home/user/cas_upload/
557
- addUrlFromRandomDirectoryIsRejected: () => {
558
- const [resp] = session(call(2, 'cas_add', { content: '/tmp/secret.txt', type: 'url' }));
559
- assert(resultOf(resp).isError === true);
560
- assert(textOf(resp).includes('/home/user/cas_upload/'));
561
- },
562
- // cas_add with type:'url' rejects path traversal attempts with ..
563
- addUrlWithPathTraversalIsRejected: () => {
564
- const [resp] = session(call(2, 'cas_add', { content: '/home/user/cas_upload/../../etc/passwd', type: 'url' }));
565
- assert(resultOf(resp).isError === true);
566
- assert(textOf(resp).includes('/home/user/cas_upload/'));
567
- },
568
504
  // cas_get with content:true on octet-stream (no magic bytes, not UTF-8) returns inline base64.
569
505
  getOctetStreamWithContentIncludesBase64: () => {
570
506
  const binaryContent = u8ListToVec(msb)([0xFF, 0xFE, 0x00, 0x01]);
@@ -574,8 +510,8 @@ export const proof = {
574
510
  const [, getResp] = session(call(2, 'cas_add', { content: binaryB64, type: 'base64' }), call(3, 'cas_get', { hash, content: true }));
575
511
  assert(!resultOf(getResp).isError);
576
512
  const result = JSON.parse(textOf(getResp));
577
- assertEq(result.mime_type, 'application/octet-stream');
513
+ assertEq(result.mimeType, 'application/octet-stream');
578
514
  assertEq(result.type, 'base64');
579
- assertEq(result.content, binaryB64);
515
+ assertEq(result.blob, binaryB64);
580
516
  },
581
517
  };
@@ -3,16 +3,16 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { sha256 } from "../crypto/sha2/module.f.js";
7
- import { join, normalize, parse } from "../path/module.f.js";
8
- import { empty, length, maxLengthBytes, msb, vec } from "../types/bit_vec/module.f.js";
9
- import { cBase32ToVec, vecToCBase32 } from "../cbase32/module.f.js";
10
- import { foldStep, forEachStep, pure } from "../effects/module.f.js";
11
- import { access, createExclusive, isNotFound, mkdir, now, randomInt, readBytes, readdir, rename, rm, stat, writeBytes, } from "../effects/node/module.f.js";
12
- import { toOption } from "../types/nullable/module.f.js";
13
- import { error, ok, unwrap } from "../types/result/module.f.js";
14
- import { splitAt } from "../types/string/module.f.js";
15
- import { nonEmpty, empty as elEmpty } from "../effects/list/module.f.js";
6
+ import { sha256 } from '../crypto/sha2/module.f.js';
7
+ import { join, normalize, parse } from '../path/module.f.js';
8
+ import { empty, length, maxLengthBytes, msb, vec } from '../types/bit_vec/module.f.js';
9
+ import { cBase32ToVec, vecToCBase32 } from '../basen/cbase32/module.f.js';
10
+ import { foldStep, forEachStep, okStep, pure } from '../effects/module.f.js';
11
+ import { access, createExclusive, isNotFound, mkdir, now, randomInt, readBytes, readdir, rename, rm, stat, writeBytes, } from '../effects/node/module.f.js';
12
+ import { toOption } from '../types/nullable/module.f.js';
13
+ import { error, ok, unwrap } from '../types/result/module.f.js';
14
+ import { splitAt } from '../types/string/module.f.js';
15
+ import { nonEmpty, empty as elEmpty } from '../effects/list/module.f.js';
16
16
  const split2 = splitAt(2);
17
17
  const prefix = '.cas';
18
18
  /** Converts a content key to its sharded relative CAS file path. */
@@ -141,9 +141,7 @@ export const fileCas = (sha2) => (path) => {
141
141
  .step(t0 => {
142
142
  const path0 = join(stageDir, stageName(t0 + leaseDelta, rndStr));
143
143
  return createExclusive(path0)
144
- .step(([c, e]) => c === 'error'
145
- ? pure(error(e))
146
- : loop(sha2.init, 0, path0)(payload));
144
+ .step(okStep(() => loop(sha2.init, 0, path0)(payload)));
147
145
  }));
148
146
  }));
149
147
  },
@@ -200,10 +198,5 @@ cas.write(streamFile(path));
200
198
  export const casUpload = (home) => (fileName) => {
201
199
  const src = join(home, 'cas_upload', fileName);
202
200
  const c = fileCas(sha256)(home);
203
- return casAddFile(c)(src).step((result) => {
204
- if (result[0] === 'error') {
205
- return pure(result);
206
- }
207
- return rm(src).step(() => pure(result));
208
- });
201
+ return casAddFile(c)(src).step(okStep(v => rm(src).step(() => pure(ok(v)))));
209
202
  };
@@ -2,9 +2,11 @@ import { type FileCasOperation } from './module.f.ts';
2
2
  import { type Effect } from '../effects/module.f.ts';
3
3
  import { type ReadFile, type WriteFile, type Rm, type Mkdir } from '../effects/node/module.f.ts';
4
4
  type TestOp = FileCasOperation | WriteFile | ReadFile | Rm | Mkdir;
5
+ declare const testAddBigFile: () => Effect<TestOp, void>;
6
+ declare const testAddAndGetBigFile: () => Effect<TestOp, void>;
5
7
  export declare const proof: {
6
- addBigFile: () => Effect<TestOp, void>;
7
- addAndGetBigFile: () => Effect<TestOp, void>;
8
+ addBigFile: typeof testAddBigFile;
9
+ addAndGetBigFile: typeof testAddAndGetBigFile;
8
10
  casWriteRead: () => void;
9
11
  casReadMissingShard: () => void;
10
12
  casWriteMultiChunk: () => void;
package/fs/cas/proof.f.js CHANGED
@@ -1,13 +1,13 @@
1
- import { length, maxLength, msb, vec, vec8 } from "../types/bit_vec/module.f.js";
2
- import { cBase32ToVec, vecToCBase32 } from "../cbase32/module.f.js";
3
- import { computeSync, sha256 } from "../crypto/sha2/module.f.js";
4
- import { fileCas, casAddFile, casUpload } from "./module.f.js";
5
- import { pure } from "../effects/module.f.js";
6
- import { mkdir, writeFile, rm, readFile, access } from "../effects/node/module.f.js";
7
- import { error, ok } from "../types/result/module.f.js";
8
- import { emptyState, virtual } from "../effects/node/virtual/module.f.js";
9
- import { join } from "../path/module.f.js";
10
- import { nonEmpty, empty } from "../effects/list/module.f.js";
1
+ import { length, maxLength, msb, vec, vec8 } from '../types/bit_vec/module.f.js';
2
+ import { cBase32ToVec, vecToCBase32 } from '../basen/cbase32/module.f.js';
3
+ import { computeSync, sha256 } from '../crypto/sha2/module.f.js';
4
+ import { fileCas, casAddFile, casUpload } from './module.f.js';
5
+ import { pure } from '../effects/module.f.js';
6
+ import { mkdir, writeFile, rm, readFile, access } from '../effects/node/module.f.js';
7
+ import { error, ok } from '../types/result/module.f.js';
8
+ import { emptyState, virtual } from '../effects/node/virtual/module.f.js';
9
+ import { join } from '../path/module.f.js';
10
+ import { nonEmpty, empty } from '../effects/list/module.f.js';
11
11
  const testDir = './test-cas-cli';
12
12
  // Create a 128 KiB big file content (at the max Vec size limit)
13
13
  // This tests the boundary where files are at the chunk size limit
@@ -4,8 +4,8 @@
4
4
  *
5
5
  * @module
6
6
  */
7
- import { bun } from "../config/module.f.js";
8
- import { clean, install, test, uses } from "../common/module.f.js";
7
+ import { bun } from '../config/module.f.js';
8
+ import { clean, install, test, uses } from '../common/module.f.js';
9
9
  export const bunSteps = (version) => clean([
10
10
  install(uses('oven-sh/setup-bun', { 'bun-version': bun })),
11
11
  install({ run: `bun install -g functionalscript@${version}` }),
@@ -7,9 +7,9 @@
7
7
  */
8
8
  import { actions, images } from '../config/module.f.ts';
9
9
  import { type Ts } from '../../types/rtti/ts/module.f.ts';
10
- export declare const os: readonly ["ubuntu", "macos", "windows"];
10
+ export declare const os: readonly ['ubuntu', 'macos', 'windows'];
11
11
  export type Os = typeof os[number];
12
- export declare const architecture: readonly ["intel", "arm"];
12
+ export declare const architecture: readonly ['intel', 'arm'];
13
13
  export type Architecture = typeof architecture[number];
14
14
  export type Image = typeof images[Os][Architecture];
15
15
  export declare const stepSchema: {