functionalscript 0.36.0 → 0.38.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.
- package/README.md +1 -1
- package/{fs → fjs}/asn.1/module.f.js +1 -1
- package/{fs → fjs}/asn.1/proof.f.js +32 -94
- package/fjs/asserts/module.f.d.ts +36 -0
- package/fjs/asserts/module.f.js +39 -0
- package/fjs/asserts/proof.f.d.ts +11 -0
- package/fjs/asserts/proof.f.js +18 -0
- package/{fs → fjs}/base_n/module.f.js +3 -3
- package/{fs → fjs}/base_n/proof.f.js +15 -39
- package/{fs → fjs/basen}/base128/module.f.d.ts +1 -1
- package/{fs → fjs/basen}/base128/module.f.js +1 -1
- package/{fs → fjs/basen}/base128/proof.f.js +3 -3
- package/{fs → fjs/basen}/base64/module.f.d.ts +2 -2
- package/{fs → fjs/basen}/base64/module.f.js +25 -15
- package/{fs → fjs/basen}/base64/proof.f.d.ts +2 -0
- package/{fs → fjs/basen}/base64/proof.f.js +26 -6
- package/{fs → fjs/basen}/cbase32/module.f.d.ts +2 -2
- package/{fs → fjs/basen}/cbase32/module.f.js +2 -2
- package/{fs → fjs/basen}/cbase32/proof.f.js +16 -43
- package/fjs/bnf/data/module.f.d.ts +52 -0
- package/{fs → fjs}/bnf/data/module.f.js +56 -6
- package/{fs → fjs}/bnf/data/proof.f.d.ts +1 -0
- package/{fs → fjs}/bnf/data/proof.f.js +59 -24
- package/{fs → fjs}/bnf/descent/module.f.d.ts +4 -14
- package/fjs/bnf/descent/module.f.js +124 -0
- package/{fs → fjs}/bnf/descent/proof.f.d.ts +1 -0
- package/{fs → fjs}/bnf/descent/proof.f.js +59 -28
- package/{fs → fjs}/bnf/ll1/module.f.d.ts +4 -4
- package/{fs → fjs}/bnf/ll1/module.f.js +15 -13
- package/{fs → fjs}/bnf/ll1/proof.f.d.ts +1 -0
- package/{fs → fjs}/bnf/ll1/proof.f.js +33 -16
- package/{fs → fjs}/bnf/module.f.d.ts +19 -5
- package/{fs → fjs}/bnf/module.f.js +14 -15
- package/fjs/bnf/proof.f.js +69 -0
- package/{fs → fjs}/bnf/testlib.f.js +2 -2
- package/{fs → fjs}/cas/cli/module.f.js +7 -7
- package/{fs → fjs}/cas/cli/proof.f.d.ts +4 -1
- package/{fs → fjs}/cas/cli/proof.f.js +37 -74
- package/fjs/cas/evo/mcp/module.f.d.ts +19 -0
- package/fjs/cas/evo/mcp/module.f.js +56 -0
- package/fjs/cas/evo/mcp/proof.f.d.ts +9 -0
- package/fjs/cas/evo/mcp/proof.f.js +95 -0
- package/fjs/cas/evo/module.f.d.ts +138 -0
- package/fjs/cas/evo/module.f.js +309 -0
- package/fjs/cas/evo/proof.f.d.ts +32 -0
- package/fjs/cas/evo/proof.f.js +453 -0
- package/fjs/cas/mcp/module.f.d.ts +49 -0
- package/fjs/cas/mcp/module.f.js +267 -0
- package/{fs → fjs}/cas/mcp/proof.f.d.ts +7 -2
- package/{fs → fjs}/cas/mcp/proof.f.js +116 -60
- package/{fs → fjs}/cas/module.f.d.ts +10 -0
- package/{fs → fjs}/cas/module.f.js +36 -10
- package/{fs → fjs}/cas/proof.f.d.ts +8 -2
- package/{fs → fjs}/cas/proof.f.js +85 -94
- package/{fs → fjs}/ci/bun/module.f.js +2 -2
- package/{fs → fjs}/ci/common/module.f.d.ts +2 -2
- package/{fs → fjs}/ci/common/module.f.js +4 -4
- package/fjs/ci/config/module.f.d.ts +42 -0
- package/{fs → fjs}/ci/config/module.f.js +6 -8
- package/fjs/ci/deno/module.f.js +20 -0
- package/{fs → fjs}/ci/module.f.js +9 -9
- package/{fs → fjs}/ci/node/module.f.d.ts +1 -1
- package/{fs → fjs}/ci/node/module.f.js +3 -5
- package/{fs → fjs}/ci/playwright/module.f.js +3 -3
- package/{fs → fjs}/ci/proof.f.js +27 -22
- package/{fs → fjs}/ci/rust/module.f.js +11 -3
- package/{fs → fjs}/cli/module.f.js +3 -3
- package/{fs → fjs}/cli/proof.f.js +25 -66
- package/{fs/types → fjs/common}/monoid/module.f.d.ts +1 -1
- package/{fs/types → fjs/common}/monoid/proof.f.js +4 -9
- package/{fs → fjs}/crypto/hmac/module.f.js +2 -2
- package/{fs → fjs}/crypto/hmac/proof.f.js +9 -17
- package/{fs → fjs}/crypto/pow/module.f.js +3 -3
- package/fjs/crypto/pow/proof.f.js +111 -0
- package/{fs → fjs}/crypto/secp/module.f.js +2 -2
- package/{fs → fjs}/crypto/secp/proof.f.js +14 -35
- package/{fs → fjs}/crypto/sha2/module.f.js +2 -2
- package/{fs → fjs}/crypto/sha2/proof.f.js +23 -54
- package/{fs → fjs}/crypto/sign/module.f.d.ts +0 -5
- package/{fs → fjs}/crypto/sign/module.f.js +11 -8
- package/{fs → fjs}/crypto/sign/proof.f.js +36 -105
- package/{fs → fjs}/crypto/vdf/module.f.js +1 -1
- package/{fs → fjs}/crypto/vdf/proof.f.js +23 -64
- package/{fs → fjs}/dev/module.f.js +6 -6
- package/{fs → fjs}/dev/package_json/module.f.js +6 -6
- package/{fs → fjs}/dev/package_json/proof.f.js +9 -9
- package/{fs → fjs}/dev/proof.f.js +3 -5
- package/{fs → fjs}/djs/ast/module.f.js +2 -2
- package/{fs → fjs}/djs/ast/proof.f.js +8 -16
- package/fjs/djs/examples/m.f.d.ts +2 -0
- package/{fs → fjs}/djs/module.f.d.ts +1 -1
- package/{fs → fjs}/djs/module.f.js +5 -5
- package/{fs → fjs}/djs/parser/module.f.d.ts +5 -0
- package/{fs → fjs}/djs/parser/module.f.js +24 -4
- package/{fs → fjs}/djs/parser/proof.f.js +145 -407
- package/{fs → fjs}/djs/proof.f.js +16 -39
- package/{fs → fjs}/djs/serializer/module.f.js +6 -6
- package/{fs → fjs}/djs/serializer/proof.f.js +18 -43
- package/fjs/djs/tokenizer/module.f.d.ts +24 -0
- package/fjs/djs/tokenizer/module.f.js +472 -0
- package/{fs/djs/tokenizer-new → fjs/djs/tokenizer}/proof.f.d.ts +4 -3
- package/{fs/djs/tokenizer-new → fjs/djs/tokenizer}/proof.f.js +447 -446
- package/{fs → fjs}/djs/transpiler/module.f.d.ts +5 -0
- package/{fs → fjs}/djs/transpiler/module.f.js +21 -11
- package/{fs → fjs}/djs/transpiler/proof.f.d.ts +3 -0
- package/fjs/djs/transpiler/proof.f.js +67 -0
- package/{fs → fjs}/effects/memory/module.f.js +4 -4
- package/{fs → fjs}/effects/memory/proof.f.js +11 -24
- package/{fs → fjs}/effects/module.f.js +1 -1
- package/{fs → fjs}/effects/node/memory/module.js +2 -2
- package/{fs → fjs}/effects/node/memory/proof.js +8 -15
- package/{fs → fjs}/effects/node/module.d.ts +1 -1
- package/{fs → fjs}/effects/node/module.f.d.ts +1 -1
- package/{fs → fjs}/effects/node/module.f.js +7 -7
- package/{fs → fjs}/effects/node/module.js +13 -13
- package/{fs → fjs}/effects/node/proof.f.d.ts +1 -0
- package/{fs → fjs}/effects/node/proof.f.js +106 -292
- package/{fs → fjs}/effects/node/virtual/module.f.js +7 -7
- package/{fs → fjs}/effects/node/virtual/proof.f.d.ts +2 -0
- package/{fs → fjs}/effects/node/virtual/proof.f.js +44 -30
- package/{fs → fjs}/effects/proof.f.js +18 -47
- package/fjs/emergent_testing/all.test.js +4 -0
- package/{fs → fjs}/emergent_testing/example.f.d.ts +1 -1
- package/{fs → fjs}/emergent_testing/module.f.js +6 -6
- package/{fs → fjs}/emergent_testing/proof.f.d.ts +19 -17
- package/{fs → fjs}/emergent_testing/proof.f.js +14 -14
- package/fjs/emergent_testing/scenarios/all.js +1 -0
- package/{fs → fjs}/fsc/bnf.f.js +2 -2
- package/{fs → fjs}/fsc/json.f.js +1 -1
- package/{fs → fjs}/fsc/module.f.js +7 -7
- package/{fs → fjs}/fsc/proof.f.js +16 -38
- package/{fs → fjs}/fsm/module.f.js +9 -9
- package/{fs → fjs}/fsm/proof.f.js +12 -19
- package/{fs → fjs}/js/tokenizer/module.f.js +7 -7
- package/{fs → fjs}/js/tokenizer/proof.f.js +5 -5
- package/{fs → fjs}/mcp/module.f.d.ts +20 -13
- package/{fs → fjs}/mcp/module.f.js +16 -9
- package/{fs → fjs}/mcp/proof.f.js +17 -17
- package/{fs → fjs}/mcp/stdio/module.f.d.ts +4 -4
- package/{fs → fjs}/mcp/stdio/module.f.js +12 -12
- package/{fs → fjs}/mcp/stdio/proof.f.js +10 -10
- package/{fs → fjs/media}/html/module.f.d.ts +3 -3
- package/{fs → fjs/media}/html/module.f.js +9 -9
- package/{fs → fjs/media}/html/proof.f.js +12 -33
- package/fjs/media/json/module.f.d.ts +50 -0
- package/{fs → fjs/media}/json/module.f.js +7 -7
- package/{fs → fjs/media}/json/parser/module.f.d.ts +7 -2
- package/{fs → fjs/media}/json/parser/module.f.js +21 -7
- package/{fs → fjs/media}/json/parser/proof.f.js +45 -120
- package/{fs → fjs/media}/json/proof.f.js +4 -4
- package/fjs/media/json/rpc/module.f.d.ts +114 -0
- package/{fs → fjs/media}/json/rpc/module.f.js +4 -4
- package/{fs → fjs/media}/json/rpc/proof.f.js +4 -4
- package/fjs/media/json/schema/module.f.d.ts +78 -0
- package/{fs → fjs/media}/json/schema/module.f.js +25 -41
- package/{fs → fjs/media}/json/schema/proof.f.js +6 -9
- package/{fs → fjs/media}/json/serializer/module.f.d.ts +1 -1
- package/{fs → fjs/media}/json/serializer/module.f.js +2 -2
- package/{fs → fjs/media}/json/serializer/proof.f.js +12 -29
- package/{fs → fjs/media}/json/tokenizer/module.f.d.ts +2 -2
- package/{fs → fjs/media}/json/tokenizer/module.f.js +4 -4
- package/{fs → fjs/media}/json/tokenizer/proof.f.js +5 -5
- package/fjs/media/module.f.d.ts +37 -0
- package/fjs/media/module.f.js +23 -0
- package/fjs/media/proof.f.d.ts +9 -0
- package/fjs/media/proof.f.js +58 -0
- package/fjs/media/revision/module.f.d.ts +78 -0
- package/fjs/media/revision/module.f.js +122 -0
- package/fjs/media/revision/proof.f.d.ts +31 -0
- package/fjs/media/revision/proof.f.js +140 -0
- package/{fs/mime → fjs/media/type}/module.f.d.ts +6 -6
- package/{fs/mime → fjs/media/type}/module.f.js +6 -6
- package/{fs/mime → fjs/media/type}/proof.f.js +8 -12
- package/fjs/module.f.d.ts +2 -0
- package/{fs/fjs → fjs}/module.f.js +9 -9
- package/fjs/module.js +4 -0
- package/{fs → fjs}/path/module.f.js +2 -2
- package/{fs → fjs}/path/proof.f.js +27 -78
- package/{fs/fjs → fjs}/proof.f.js +6 -4
- package/{fs → fjs}/sul/id/module.f.js +9 -9
- package/{fs → fjs}/sul/id/proof.f.js +12 -26
- package/{fs → fjs}/sul/level/hash/module.f.js +3 -3
- package/{fs → fjs}/sul/level/hash/proof.f.js +8 -8
- package/{fs → fjs}/sul/level/literal/module.f.js +5 -5
- package/{fs → fjs}/sul/level/literal/proof.f.js +15 -29
- package/{fs → fjs}/sul/module.f.js +3 -3
- package/{fs → fjs}/sul/proof.f.js +3 -3
- package/fjs/text/ascii/proof.f.d.ts +6 -0
- package/fjs/text/ascii/proof.f.js +14 -0
- package/{fs → fjs}/text/code_point/module.f.js +12 -12
- package/{fs → fjs}/text/code_point/proof.f.js +1 -1
- package/{fs → fjs}/text/module.f.js +5 -5
- package/{fs → fjs}/text/proof.f.js +7 -12
- package/{fs → fjs}/text/sgr/module.f.js +2 -2
- package/{fs → fjs}/text/sgr/proof.f.js +8 -17
- package/{fs → fjs}/text/utf16/module.f.js +6 -6
- package/{fs → fjs}/text/utf16/proof.f.js +27 -70
- package/{fs → fjs}/text/utf8/module.f.js +22 -17
- package/{fs → fjs}/text/utf8/proof.f.js +59 -128
- package/{fs → fjs}/types/array/module.f.js +1 -1
- package/{fs → fjs}/types/array/proof.f.js +21 -55
- package/{fs → fjs}/types/bigfloat/module.f.js +1 -1
- package/fjs/types/bigfloat/proof.f.js +217 -0
- package/{fs → fjs}/types/bigint/module.f.js +2 -2
- package/{fs → fjs}/types/bigint/proof.f.js +51 -147
- package/{fs → fjs}/types/bit_vec/module.f.js +8 -8
- package/{fs → fjs}/types/bit_vec/proof.f.js +76 -218
- package/{fs → fjs}/types/btree/find/module.f.js +1 -1
- package/fjs/types/btree/find/proof.f.d.ts +3 -0
- package/fjs/types/btree/find/proof.f.js +108 -0
- package/{fs → fjs}/types/btree/module.f.js +2 -2
- package/fjs/types/btree/proof.f.d.ts +13 -0
- package/{fs → fjs}/types/btree/proof.f.js +12 -19
- package/{fs → fjs}/types/btree/remove/module.f.js +4 -4
- package/fjs/types/btree/remove/proof.f.d.ts +9 -0
- package/{fs → fjs}/types/btree/remove/proof.f.js +88 -235
- package/{fs → fjs}/types/btree/set/module.f.js +8 -5
- package/{fs → fjs}/types/btree/set/proof.f.js +39 -28
- package/{fs → fjs}/types/byte_set/module.f.js +2 -2
- package/fjs/types/byte_set/proof.f.js +79 -0
- package/{fs → fjs}/types/function/compare/proof.f.js +14 -37
- package/fjs/types/function/operator/proof.f.d.ts +23 -0
- package/{fs → fjs}/types/function/operator/proof.f.js +18 -49
- package/{fs → fjs}/types/function/proof.f.js +3 -4
- package/{fs → fjs}/types/list/module.f.js +2 -2
- package/fjs/types/list/proof.f.d.ts +30 -0
- package/{fs → fjs}/types/list/proof.f.js +59 -167
- package/{fs → fjs}/types/map/proof.f.js +6 -14
- package/fjs/types/nibble_set/proof.f.js +60 -0
- package/{fs → fjs}/types/nominal/proof.f.js +5 -10
- package/{fs → fjs}/types/nullable/module.f.js +2 -2
- package/fjs/types/nullable/proof.f.js +29 -0
- package/{fs → fjs}/types/number/module.f.js +5 -5
- package/{fs → fjs}/types/number/proof.f.js +10 -25
- package/{fs → fjs}/types/object/module.f.js +4 -4
- package/{fs → fjs}/types/object/proof.f.js +4 -7
- package/{fs → fjs}/types/ordered_map/module.f.js +6 -6
- package/{fs → fjs}/types/ordered_map/proof.f.js +3 -3
- package/fjs/types/patricia_trie/proof.f.d.ts +13 -0
- package/{fs → fjs}/types/patricia_trie/proof.f.js +22 -22
- package/{fs → fjs}/types/prime_field/module.f.js +3 -8
- package/{fs → fjs}/types/prime_field/proof.f.js +2 -2
- package/{fs → fjs}/types/range/module.f.d.ts +1 -1
- package/fjs/types/range/module.f.js +2 -0
- package/fjs/types/range/proof.f.js +9 -0
- package/{fs → fjs}/types/range_map/module.f.js +4 -4
- package/{fs → fjs}/types/range_map/proof.f.js +35 -88
- package/{fs → fjs}/types/result/module.js +1 -1
- package/fjs/types/result/proof.f.d.ts +12 -0
- package/fjs/types/result/proof.f.js +31 -0
- package/{fs → fjs}/types/rtti/common/module.f.d.ts +36 -1
- package/{fs → fjs}/types/rtti/common/module.f.js +48 -4
- package/fjs/types/rtti/common/proof.f.d.ts +8 -0
- package/fjs/types/rtti/common/proof.f.js +51 -0
- package/{fs → fjs}/types/rtti/module.f.d.ts +2 -2
- package/{fs → fjs}/types/rtti/module.f.js +1 -1
- package/{fs → fjs}/types/rtti/parse/module.f.js +16 -25
- package/{fs → fjs}/types/rtti/parse/proof.f.js +9 -20
- package/{fs → fjs}/types/rtti/ts/module.f.d.ts +10 -4
- package/{fs → fjs}/types/rtti/ts/module.f.js +1 -1
- package/{fs → fjs}/types/rtti/ts/proof.f.js +2 -2
- package/{fs → fjs}/types/rtti/validate/module.f.js +11 -32
- package/{fs → fjs}/types/rtti/validate/proof.f.js +6 -11
- package/{fs → fjs}/types/sorted_list/module.f.js +3 -3
- package/{fs → fjs}/types/sorted_list/proof.f.js +14 -27
- package/{fs → fjs}/types/sorted_set/module.f.js +1 -1
- package/{fs → fjs}/types/sorted_set/proof.f.js +20 -45
- package/{fs → fjs}/types/string/module.f.js +4 -4
- package/fjs/types/string/proof.f.js +77 -0
- package/{fs → fjs}/types/string_set/module.f.js +6 -6
- package/fjs/types/string_set/proof.f.js +43 -0
- package/{fs → fjs}/types/ts/proof.f.d.ts +18 -18
- package/{fs → fjs}/types/ts/proof.f.js +16 -43
- package/{fs → fjs}/types/uint8array/module.f.js +10 -10
- package/{fs → fjs}/types/uint8array/proof.f.d.ts +4 -1
- package/{fs → fjs}/types/uint8array/proof.f.js +11 -11
- package/{fs → fjs}/website/module.f.js +3 -3
- package/fjs/website/proof.f.js +13 -0
- package/package.json +8 -8
- package/fs/asserts/module.f.d.ts +0 -4
- package/fs/asserts/module.f.js +0 -9
- package/fs/asserts/proof.f.d.ts +0 -8
- package/fs/asserts/proof.f.js +0 -51
- package/fs/bnf/data/module.f.d.ts +0 -30
- package/fs/bnf/descent/module.f.js +0 -119
- package/fs/bnf/proof.f.js +0 -104
- package/fs/cas/mcp/module.f.d.ts +0 -36
- package/fs/cas/mcp/module.f.js +0 -249
- package/fs/ci/config/module.f.d.ts +0 -43
- package/fs/ci/deno/module.f.js +0 -16
- package/fs/crypto/pow/proof.f.js +0 -161
- package/fs/djs/examples/m.f.d.ts +0 -2
- package/fs/djs/tokenizer/module.f.d.ts +0 -12
- package/fs/djs/tokenizer/module.f.js +0 -62
- package/fs/djs/tokenizer/proof.f.d.ts +0 -7
- package/fs/djs/tokenizer/proof.f.js +0 -543
- package/fs/djs/tokenizer-new/module.f.d.ts +0 -3
- package/fs/djs/tokenizer-new/module.f.js +0 -155
- package/fs/djs/transpiler/proof.f.js +0 -68
- package/fs/emergent_testing/all.test.js +0 -4
- package/fs/emergent_testing/scenarios/all.js +0 -1
- package/fs/fjs/module.f.d.ts +0 -2
- package/fs/fjs/module.js +0 -4
- package/fs/json/module.f.d.ts +0 -50
- package/fs/json/rpc/module.f.d.ts +0 -114
- package/fs/json/schema/module.f.d.ts +0 -77
- package/fs/text/ascii/proof.f.d.ts +0 -4
- package/fs/text/ascii/proof.f.js +0 -24
- package/fs/types/bigfloat/proof.f.js +0 -348
- package/fs/types/btree/find/proof.f.js +0 -151
- package/fs/types/btree/proof.f.d.ts +0 -7
- package/fs/types/btree/remove/proof.f.d.ts +0 -4
- package/fs/types/byte_set/proof.f.js +0 -120
- package/fs/types/function/operator/proof.f.d.ts +0 -12
- package/fs/types/list/proof.f.d.ts +0 -22
- package/fs/types/nibble_set/proof.f.js +0 -89
- package/fs/types/nullable/proof.f.js +0 -50
- package/fs/types/patricia_trie/proof.f.d.ts +0 -7
- package/fs/types/range/module.f.js +0 -2
- package/fs/types/range/proof.f.d.ts +0 -1
- package/fs/types/range/proof.f.js +0 -18
- package/fs/types/result/proof.f.d.ts +0 -6
- package/fs/types/result/proof.f.js +0 -52
- package/fs/types/string/proof.f.js +0 -118
- package/fs/types/string_set/proof.f.js +0 -82
- package/fs/website/proof.f.js +0 -16
- /package/{fs → fjs}/asn.1/module.f.d.ts +0 -0
- /package/{fs → fjs}/asn.1/proof.f.d.ts +0 -0
- /package/{fs → fjs}/base_n/module.f.d.ts +0 -0
- /package/{fs → fjs}/base_n/proof.f.d.ts +0 -0
- /package/{fs → fjs/basen}/base128/proof.f.d.ts +0 -0
- /package/{fs → fjs/basen}/cbase32/proof.f.d.ts +0 -0
- /package/{fs → fjs}/bnf/proof.f.d.ts +0 -0
- /package/{fs → fjs}/bnf/testlib.f.d.ts +0 -0
- /package/{fs → fjs}/cas/cli/module.f.d.ts +0 -0
- /package/{fs → fjs}/ci/bun/module.f.d.ts +0 -0
- /package/{fs → fjs}/ci/deno/module.f.d.ts +0 -0
- /package/{fs → fjs}/ci/module.f.d.ts +0 -0
- /package/{fs → fjs}/ci/playwright/module.f.d.ts +0 -0
- /package/{fs → fjs}/ci/proof.f.d.ts +0 -0
- /package/{fs → fjs}/ci/rust/module.f.d.ts +0 -0
- /package/{fs → fjs}/cli/module.f.d.ts +0 -0
- /package/{fs → fjs}/cli/proof.f.d.ts +0 -0
- /package/{fs/types → fjs/common}/monoid/module.f.js +0 -0
- /package/{fs/types → fjs/common}/monoid/proof.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/hmac/module.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/hmac/proof.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/pow/module.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/pow/proof.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/secp/module.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/secp/proof.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/sha2/module.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/sha2/proof.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/sign/proof.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/vdf/module.f.d.ts +0 -0
- /package/{fs → fjs}/crypto/vdf/proof.f.d.ts +0 -0
- /package/{fs → fjs}/dev/module.f.d.ts +0 -0
- /package/{fs → fjs}/dev/package_json/module.f.d.ts +0 -0
- /package/{fs → fjs}/dev/package_json/proof.f.d.ts +0 -0
- /package/{fs → fjs}/dev/proof.f.d.ts +0 -0
- /package/{fs → fjs}/djs/ast/module.f.d.ts +0 -0
- /package/{fs → fjs}/djs/ast/proof.f.d.ts +0 -0
- /package/{fs → fjs}/djs/examples/input.f.d.ts +0 -0
- /package/{fs → fjs}/djs/examples/input.f.js +0 -0
- /package/{fs → fjs}/djs/examples/m.f.js +0 -0
- /package/{fs → fjs}/djs/parser/proof.f.d.ts +0 -0
- /package/{fs → fjs}/djs/proof.f.d.ts +0 -0
- /package/{fs → fjs}/djs/serializer/module.f.d.ts +0 -0
- /package/{fs → fjs}/djs/serializer/proof.f.d.ts +0 -0
- /package/{fs → fjs}/effects/list/module.f.d.ts +0 -0
- /package/{fs → fjs}/effects/list/module.f.js +0 -0
- /package/{fs → fjs}/effects/memory/module.f.d.ts +0 -0
- /package/{fs → fjs}/effects/memory/proof.f.d.ts +0 -0
- /package/{fs → fjs}/effects/mock/module.f.d.ts +0 -0
- /package/{fs → fjs}/effects/mock/module.f.js +0 -0
- /package/{fs → fjs}/effects/module.d.ts +0 -0
- /package/{fs → fjs}/effects/module.f.d.ts +0 -0
- /package/{fs → fjs}/effects/module.js +0 -0
- /package/{fs → fjs}/effects/node/memory/module.d.ts +0 -0
- /package/{fs → fjs}/effects/node/memory/proof.d.ts +0 -0
- /package/{fs → fjs}/effects/node/virtual/module.f.d.ts +0 -0
- /package/{fs → fjs}/effects/proof.f.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/all.test.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/example.f.js +0 -0
- /package/{fs → fjs}/emergent_testing/module.f.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/all.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/async-subtests.fail.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/async-subtests.fail.js +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/async-subtests.pass.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/async-subtests.pass.js +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/async.fail.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/async.fail.js +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/async.pass.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/async.pass.js +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/fail.fail.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/fail.fail.js +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/return-value.pass.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/return-value.pass.js +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/thenable.pass.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/thenable.pass.js +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/thenable2.pass.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/thenable2.pass.js +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/throw.pass.d.ts +0 -0
- /package/{fs → fjs}/emergent_testing/scenarios/throw.pass.js +0 -0
- /package/{fs → fjs}/fsc/bnf.f.d.ts +0 -0
- /package/{fs → fjs}/fsc/json.f.d.ts +0 -0
- /package/{fs → fjs}/fsc/module.f.d.ts +0 -0
- /package/{fs → fjs}/fsc/proof.f.d.ts +0 -0
- /package/{fs → fjs}/fsm/module.f.d.ts +0 -0
- /package/{fs → fjs}/fsm/proof.f.d.ts +0 -0
- /package/{fs → fjs}/js/tokenizer/module.f.d.ts +0 -0
- /package/{fs → fjs}/js/tokenizer/proof.f.d.ts +0 -0
- /package/{fs → fjs}/mcp/proof.f.d.ts +0 -0
- /package/{fs → fjs}/mcp/stdio/proof.f.d.ts +0 -0
- /package/{fs → fjs/media}/html/proof.f.d.ts +0 -0
- /package/{fs → fjs/media}/json/parser/proof.f.d.ts +0 -0
- /package/{fs → fjs/media}/json/proof.f.d.ts +0 -0
- /package/{fs → fjs/media}/json/rpc/proof.f.d.ts +0 -0
- /package/{fs → fjs/media}/json/schema/proof.f.d.ts +0 -0
- /package/{fs → fjs/media}/json/serializer/proof.f.d.ts +0 -0
- /package/{fs → fjs/media}/json/tokenizer/proof.f.d.ts +0 -0
- /package/{fs/mime → fjs/media/type}/proof.f.d.ts +0 -0
- /package/{fs/fjs → fjs}/module.d.ts +0 -0
- /package/{fs → fjs}/path/module.f.d.ts +0 -0
- /package/{fs → fjs}/path/proof.f.d.ts +0 -0
- /package/{fs/fjs → fjs}/proof.f.d.ts +0 -0
- /package/{fs → fjs}/sul/id/module.f.d.ts +0 -0
- /package/{fs → fjs}/sul/id/proof.f.d.ts +0 -0
- /package/{fs → fjs}/sul/level/hash/module.f.d.ts +0 -0
- /package/{fs → fjs}/sul/level/hash/proof.f.d.ts +0 -0
- /package/{fs → fjs}/sul/level/literal/module.f.d.ts +0 -0
- /package/{fs → fjs}/sul/level/literal/proof.f.d.ts +0 -0
- /package/{fs → fjs}/sul/module.f.d.ts +0 -0
- /package/{fs → fjs}/sul/proof.f.d.ts +0 -0
- /package/{fs → fjs}/text/ascii/module.f.d.ts +0 -0
- /package/{fs → fjs}/text/ascii/module.f.js +0 -0
- /package/{fs → fjs}/text/code_point/module.f.d.ts +0 -0
- /package/{fs → fjs}/text/code_point/proof.f.d.ts +0 -0
- /package/{fs → fjs}/text/module.f.d.ts +0 -0
- /package/{fs → fjs}/text/proof.f.d.ts +0 -0
- /package/{fs → fjs}/text/sgr/module.f.d.ts +0 -0
- /package/{fs → fjs}/text/sgr/proof.f.d.ts +0 -0
- /package/{fs → fjs}/text/utf16/module.f.d.ts +0 -0
- /package/{fs → fjs}/text/utf16/proof.f.d.ts +0 -0
- /package/{fs → fjs}/text/utf8/module.f.d.ts +0 -0
- /package/{fs → fjs}/text/utf8/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/array/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/array/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/bigfloat/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/bigfloat/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/bigint/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/bigint/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/bit_vec/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/bit_vec/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/btree/find/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/btree/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/btree/remove/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/btree/set/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/btree/set/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/btree/types/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/btree/types/module.f.js +0 -0
- /package/{fs → fjs}/types/byte_set/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/byte_set/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/function/compare/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/function/compare/module.f.js +0 -0
- /package/{fs/types/btree/find → fjs/types/function/compare}/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/function/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/function/module.f.js +0 -0
- /package/{fs → fjs}/types/function/operator/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/function/operator/module.f.js +0 -0
- /package/{fs/types/function/compare → fjs/types/function}/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/list/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/map/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/map/module.f.js +0 -0
- /package/{fs → fjs}/types/map/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/nibble_set/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/nibble_set/module.f.js +0 -0
- /package/{fs → fjs}/types/nibble_set/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/nominal/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/nominal/module.f.js +0 -0
- /package/{fs → fjs}/types/nominal/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/nullable/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/nullable/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/number/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/number/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/object/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/object/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/option/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/option/module.f.js +0 -0
- /package/{fs → fjs}/types/ordered_map/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/ordered_map/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/patricia_trie/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/patricia_trie/module.f.js +0 -0
- /package/{fs → fjs}/types/phantom/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/phantom/module.f.js +0 -0
- /package/{fs → fjs}/types/prime_field/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/prime_field/proof.f.d.ts +0 -0
- /package/{fs/types/function → fjs/types/range}/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/range_map/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/range_map/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/result/module.d.ts +0 -0
- /package/{fs → fjs}/types/result/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/result/module.f.js +0 -0
- /package/{fs → fjs}/types/rtti/parse/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/rtti/parse/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/rtti/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/rtti/proof.f.js +0 -0
- /package/{fs → fjs}/types/rtti/ts/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/rtti/validate/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/rtti/validate/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/sorted_list/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/sorted_list/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/sorted_set/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/sorted_set/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/string/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/string/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/string_set/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/string_set/proof.f.d.ts +0 -0
- /package/{fs → fjs}/types/ts/module.f.d.ts +0 -0
- /package/{fs → fjs}/types/ts/module.f.js +0 -0
- /package/{fs → fjs}/types/uint8array/module.f.d.ts +0 -0
- /package/{fs → fjs}/website/module.f.d.ts +0 -0
- /package/{fs → fjs}/website/proof.f.d.ts +0 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP adapter for the content-addressable store and the Evo API layered on
|
|
3
|
+
* top of it.
|
|
4
|
+
*
|
|
5
|
+
* Maps `Cas<O>` operations onto MCP tools, so an agent that speaks MCP can
|
|
6
|
+
* store a blob and get back its hash, fetch a blob by hash, and enumerate
|
|
7
|
+
* what is stored — without shelling out to the `cas` CLI. The store itself
|
|
8
|
+
* (`fjs/cas/module.f.ts`) stays transport-agnostic; this is an additional
|
|
9
|
+
* front end alongside the CLI `main`. The same server also exposes
|
|
10
|
+
* `fjs/cas/evo`'s subject/head API (`evo_list`/`evo_head`/`evo_add`,
|
|
11
|
+
* `fjs/cas/evo/mcp`) — one process, one `~/.cas/` store, one in-memory Evo
|
|
12
|
+
* cache scanned once at startup (`initEvo`).
|
|
13
|
+
*
|
|
14
|
+
* ## Tools
|
|
15
|
+
*
|
|
16
|
+
* | Tool | args | action | result |
|
|
17
|
+
* |-------------|-------------------------------------------------|---------------|--------------------------------------|
|
|
18
|
+
* | `cas_add` | `{ content, type? }` | `c.write(...)`| hash (cBase32) |
|
|
19
|
+
* | `cas_get` | `{ hash, content?: boolean }` | `c.read(key)` | JSON `{length,mimeType,type[,uri][,text\|blob]}` |
|
|
20
|
+
* | `cas_list` | `{}` | `c.list()` | hashes, one per line |
|
|
21
|
+
* | `evo_list` | `{}` | `e.list()` | subjects, one per line |
|
|
22
|
+
* | `evo_head` | `{ subject }` | `e.head(...)` | head hashes, one per line |
|
|
23
|
+
* | `evo_add` | `{ parents, snapshot?, subject?, archived? }` | `e.add(...)` | hash (cBase32) |
|
|
24
|
+
*
|
|
25
|
+
* ## `cas_add` input encoding
|
|
26
|
+
*
|
|
27
|
+
* The optional `type` field controls how `content` is interpreted:
|
|
28
|
+
* - `'text'` (default, or omitted): `content` is a UTF-8 string stored as raw
|
|
29
|
+
* bytes. Most agent-generated content (scripts, JSON, prompts) can be stored
|
|
30
|
+
* without any encoding step.
|
|
31
|
+
* - `'base64'`: `content` is RFC 4648 base64, decoded to bytes before storage.
|
|
32
|
+
* Use this for pre-encoded binary payloads.
|
|
33
|
+
*
|
|
34
|
+
* Inline content (either encoding) is capped at 128 KiB (`maxLength`). There is
|
|
35
|
+
* no MCP route for larger content — the server never opens a local path named by
|
|
36
|
+
* the client (see the invariant in `fjs/cas/mcp/README.md`); large files go
|
|
37
|
+
* through the `cas` CLI (`cas add <path>`) instead, run directly by the user.
|
|
38
|
+
*
|
|
39
|
+
* ## `cas_get` output
|
|
40
|
+
*
|
|
41
|
+
* Always returns a JSON object `{ length, mimeType, type[, uri][, text | blob] }`.
|
|
42
|
+
* These field names mirror the MCP resource-contents shape (`resources/read`
|
|
43
|
+
* results), so the tool view and the future resource view of a blob use the same
|
|
44
|
+
* vocabulary: `mimeType` (not `mime_type`), `uri` (not `url`) — the resource URI
|
|
45
|
+
* under which the same blob will be readable via `resources/read` — and `text` /
|
|
46
|
+
* `blob` (not a `type`-tagged `content`) for inline payloads. `type` is always
|
|
47
|
+
* present (`'text'` or `'base64'`) as the discriminator for which of `text` /
|
|
48
|
+
* `blob` a later `content: true` fetch would populate — MCP allows extra fields,
|
|
49
|
+
* so `type` and `length` simply stay alongside the required resource-contents
|
|
50
|
+
* fields. A single classifier — the `fjs/media/type` detector state machine (length ×
|
|
51
|
+
* magic-byte eliminator × UTF-8 DFA) — produces the same three-way verdict on
|
|
52
|
+
* both paths, so there is no second, divergent copy of the rules:
|
|
53
|
+
*
|
|
54
|
+
* 1. **Magic-byte hit** (PNG/JPEG/GIF/WebP/PDF/ZIP) → `type: 'base64'` with the
|
|
55
|
+
* detected `mimeType`.
|
|
56
|
+
* 2. **Whole-blob-valid UTF-8** → `type: 'text'`, `mimeType: 'text/plain'`.
|
|
57
|
+
* 3. **Fallback** → `type: 'base64'`, `mimeType: 'application/octet-stream'`.
|
|
58
|
+
*
|
|
59
|
+
* **Metadata-only (`content: false`, the default) is size-independent for
|
|
60
|
+
* non-text and oversized blobs.** It folds the read stream through
|
|
61
|
+
* `fjs/media/type` `detectStream` and never buffers the blob. Because a single
|
|
62
|
+
* `Vec` caps at `maxLength` bits (128 KiB), the old drain-into-one-`Vec`
|
|
63
|
+
* approach failed on any blob larger than one chunk even when only metadata
|
|
64
|
+
* was asked for; streaming detection returns correct
|
|
65
|
+
* `{ length, mimeType, type[, uri] }` regardless of size. A dialect (e.g.
|
|
66
|
+
* `vnd.fjs.revision`, see `fjs/media/module.f.ts`) can only be recognized by
|
|
67
|
+
* parsing the whole blob as JSON, so when the streaming verdict is
|
|
68
|
+
* whole-blob-valid text within the same bounded inline cap as `content: true`
|
|
69
|
+
* (`maxLengthBytes`), one extra bounded read materializes the blob purely to
|
|
70
|
+
* refine `mimeType` through the dialect-aware detector; a larger or non-text
|
|
71
|
+
* blob is never a dialect match and is returned from the streaming verdict
|
|
72
|
+
* alone, unbuffered.
|
|
73
|
+
*
|
|
74
|
+
* **`content: true`** first derives `{ length, mimeType, type }` with the same
|
|
75
|
+
* size-independent `fjs/media/type` `detectStream` machine (to decide whether the
|
|
76
|
+
* blob even fits inline), then materializes the bytes (via `collectRead`, bounded
|
|
77
|
+
* by `maxLength`) and re-derives the verdict from the dialect-aware detector
|
|
78
|
+
* (`fjs/media/module.f.ts` `detect`) — now that the whole blob is in hand, a
|
|
79
|
+
* dialect match is reflected in the result — before encoding the inline payload
|
|
80
|
+
* by `type` — a `fjs/text/utf8` `fromVec` string on `text` (for `type: 'text'`),
|
|
81
|
+
* base64 on `blob` (for `type: 'base64'`). A blob larger than `maxLength` (128
|
|
82
|
+
* KiB) cannot be buffered into one `Vec`, so it is rejected here with a
|
|
83
|
+
* descriptive *"too large"* error (carrying the byte size and `uri`) rather than
|
|
84
|
+
* being misreported as absent; it should be fetched via `uri` or inspected with
|
|
85
|
+
* metadata-only `cas_get`.
|
|
86
|
+
*
|
|
87
|
+
* ## Encoding split: hashes vs. content
|
|
88
|
+
*
|
|
89
|
+
* `Cas<O>` deals in `Vec` (bit vectors); MCP models only `textContent` today.
|
|
90
|
+
* **Hashes** travel as cBase32 (`fjs/basen/cbase32`) — the canonical CAS hash format,
|
|
91
|
+
* shared with the CLI and the on-disk store layout. **Content** encoding is
|
|
92
|
+
* determined at read time as described above.
|
|
93
|
+
*
|
|
94
|
+
* ## Error convention
|
|
95
|
+
*
|
|
96
|
+
* MCP draws a line the dispatcher already respects: *protocol* failures
|
|
97
|
+
* (unknown method, malformed JSON-RPC params) are JSON-RPC errors handled by
|
|
98
|
+
* `mcpStep`. *Tool* failures come back as a normal `tools/call` result with
|
|
99
|
+
* `isError: true` and a text explanation, so the model can read and react:
|
|
100
|
+
* - `type: 'base64'` with malformed content (base64 `decode` → `null`) → `isError`
|
|
101
|
+
* - malformed `hash` (`cBase32ToVec` → `null`) → `isError`
|
|
102
|
+
* - `cas_get` on an absent hash (`c.read` → `undefined`) → `isError`
|
|
103
|
+
* - `cas_get` with `content: true` on a blob larger than `maxLength` → `isError`
|
|
104
|
+
* (distinct "too large" message, not "no such hash")
|
|
105
|
+
* - unknown tool `name` → `isError`
|
|
106
|
+
*
|
|
107
|
+
* @module
|
|
108
|
+
*/
|
|
109
|
+
import { string, option, or, boolean } from '../../types/rtti/module.f.js';
|
|
110
|
+
import { stringify } from '../../media/json/module.f.js';
|
|
111
|
+
import { pure } from '../../effects/module.f.js';
|
|
112
|
+
import { create } from '../../effects/memory/module.f.js';
|
|
113
|
+
import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.js';
|
|
114
|
+
import { decode as base64Decode, encode as base64Encode } from '../../basen/base64/module.f.js';
|
|
115
|
+
import { tryUtf8 } from '../../text/module.f.js';
|
|
116
|
+
import { detectStream } from '../../media/type/module.f.js';
|
|
117
|
+
import { detect as detectDialect } from '../../media/module.f.js';
|
|
118
|
+
import { maxLengthBytes } from '../../types/bit_vec/module.f.js';
|
|
119
|
+
import { ok, error } from '../../types/result/module.f.js';
|
|
120
|
+
import {} from '../../effects/node/module.f.js';
|
|
121
|
+
import { stdioTransport } from '../../mcp/stdio/module.f.js';
|
|
122
|
+
import { mcpStep, uninitializedState, toolEntry, fromRegistry, errorResult, okResult, } from '../../mcp/module.f.js';
|
|
123
|
+
import { collectRead, fileCas } from '../module.f.js';
|
|
124
|
+
import { fromVec } from '../../text/utf8/module.f.js';
|
|
125
|
+
import { identity } from '../../types/function/module.f.js';
|
|
126
|
+
import { sha256 } from '../../crypto/sha2/module.f.js';
|
|
127
|
+
import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.js';
|
|
128
|
+
import { initEvo, evo, syncRevision } from '../evo/module.f.js';
|
|
129
|
+
import { evoToolRegistry } from '../evo/mcp/module.f.js';
|
|
130
|
+
// ── Argument schemas (declared once, used for both inputSchema and validate) ─────
|
|
131
|
+
/** Arguments for `cas_add`: content to store, with optional encoding type. */
|
|
132
|
+
export const casAddArgs = {
|
|
133
|
+
content: string,
|
|
134
|
+
type: or('text', 'base64', undefined)
|
|
135
|
+
};
|
|
136
|
+
/** Arguments for `cas_get`: the cBase32 hash to look up; optionally request inline content. */
|
|
137
|
+
export const casGetArgs = {
|
|
138
|
+
hash: string,
|
|
139
|
+
content: option(boolean)
|
|
140
|
+
};
|
|
141
|
+
/** Arguments for `cas_list`: none. */
|
|
142
|
+
export const casListArgs = {};
|
|
143
|
+
// ── Tool registry ──────────────────────────────────────────────────────────────
|
|
144
|
+
const toJson = stringify(identity);
|
|
145
|
+
/**
|
|
146
|
+
* Registry of all CAS tools, bound to `home` and the Evo cache at
|
|
147
|
+
* `cacheKey`. `cas_add` is the only tool that writes, so it is the only one
|
|
148
|
+
* that needs `cacheKey`: a successfully stored blob is folded into the Evo
|
|
149
|
+
* cache via `syncRevision` when it happens to be a `vnd.fjs.revision` — a
|
|
150
|
+
* plain `cas_add` and `evo_add` are two ways to reach the same store, and
|
|
151
|
+
* this keeps `evo_list`/`evo_head` honest about either one without a
|
|
152
|
+
* rescan.
|
|
153
|
+
*/
|
|
154
|
+
export const casToolRegistry = (home) => (cacheKey) => {
|
|
155
|
+
const c = fileCas(sha256)(home);
|
|
156
|
+
return [
|
|
157
|
+
toolEntry('cas_add', 'Store content and return its hash (cBase32). Pass type:"base64" for binary; omit or pass type:"text" for UTF-8 text (default). Inline content is capped at 128 KiB (131072 bytes) — larger content is rejected. For larger content, store the file with the `cas` CLI instead: run `npx functionalscript cas add <path>` yourself if you have shell access, or give the user that exact command to run — it prints the resulting hash on stdout.', casAddArgs, ({ type, content }) => {
|
|
158
|
+
// type:'text' or 'base64' — resolve content to Vec, store via c.write()
|
|
159
|
+
let x = type === 'base64'
|
|
160
|
+
? base64Decode(content)
|
|
161
|
+
: tryUtf8(content);
|
|
162
|
+
return x === null
|
|
163
|
+
? pure(errorResult('too large or malformed — for large content, run `npx functionalscript cas add <path>` (or have the user run it) instead'))
|
|
164
|
+
// The resolved content fits in one chunk; feed it as a single-item stream.
|
|
165
|
+
: c.write(nonEmpty(ok(x), elEmpty())).step((writeResult) => {
|
|
166
|
+
if (writeResult[0] === 'error') {
|
|
167
|
+
return pure(errorResult('write'));
|
|
168
|
+
}
|
|
169
|
+
const hash = writeResult[1];
|
|
170
|
+
return syncRevision(cacheKey)(hash)(x).step(() => pure(okResult(vecToCBase32(hash))));
|
|
171
|
+
});
|
|
172
|
+
}),
|
|
173
|
+
toolEntry('cas_get', 'Inspect a blob by hash. Always returns JSON {length,mimeType,type[,uri]} where type is "text" or "base64". Pass content:true to also include the inline payload as text (type:"text") or blob (type:"base64"), but content is capped at 128 KiB (131072 bytes) — a larger blob is rejected with an error. To download a blob, prefer the uri field returned in the result instead of requesting inline content.', casGetArgs, r => {
|
|
174
|
+
const key = cBase32ToVec(r.hash);
|
|
175
|
+
if (key === null) {
|
|
176
|
+
return pure(errorResult(`invalid cBase32 hash: ${r.hash}`));
|
|
177
|
+
}
|
|
178
|
+
const uri = c.url(key);
|
|
179
|
+
return detectStream(c.read(key)).step(([tag, detected]) => {
|
|
180
|
+
if (tag === 'error') {
|
|
181
|
+
return pure(errorResult(`no such hash: ${r.hash}`));
|
|
182
|
+
}
|
|
183
|
+
const { length, mime_type: mimeType, type } = detected;
|
|
184
|
+
const meta = { length: Number(length), mimeType, type, uri };
|
|
185
|
+
if (r.content !== true) {
|
|
186
|
+
// A dialect match can only be decided from the whole parsed blob;
|
|
187
|
+
// only attempt the extra bounded read when it stands a chance
|
|
188
|
+
// (whole-blob text within the same cap `content: true` allows).
|
|
189
|
+
if (type !== 'text' || length > maxLengthBytes) {
|
|
190
|
+
return pure(okResult(toJson(meta)));
|
|
191
|
+
}
|
|
192
|
+
return collectRead(c.read(key)).step(([collectTag, value]) => {
|
|
193
|
+
// Already known to fit from the streaming pass above, so an
|
|
194
|
+
// error here means the hash vanished between reads; fall back
|
|
195
|
+
// to the streaming verdict rather than fail the whole request.
|
|
196
|
+
if (collectTag === 'error') {
|
|
197
|
+
return pure(okResult(toJson(meta)));
|
|
198
|
+
}
|
|
199
|
+
const refined = detectDialect(value);
|
|
200
|
+
return pure(okResult(toJson({ ...meta, mimeType: refined.mime_type })));
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
// A single `Vec` caps at `maxLength` bits (`maxLengthBytes` bytes), so
|
|
204
|
+
// a larger blob cannot be buffered for inline transfer. Report the
|
|
205
|
+
// size and point at the size-independent alternatives instead of
|
|
206
|
+
// misreporting an existing blob as `no such hash`.
|
|
207
|
+
if (length > maxLengthBytes) {
|
|
208
|
+
return pure(errorResult(`blob too large to fetch inline (${length} bytes, limit ${maxLengthBytes} bytes); use the uri field (${uri}) or omit content for metadata`));
|
|
209
|
+
}
|
|
210
|
+
return collectRead(c.read(key)).step(([collectTag, value]) => {
|
|
211
|
+
if (collectTag === 'error') {
|
|
212
|
+
return pure(errorResult(`no such hash: ${r.hash}`));
|
|
213
|
+
}
|
|
214
|
+
// Re-derive the verdict from the now-materialized blob so a
|
|
215
|
+
// dialect match — only decidable with the whole parsed JSON in
|
|
216
|
+
// hand — is reflected in the inline result too, not just the
|
|
217
|
+
// streaming guess.
|
|
218
|
+
const refined = detectDialect(value);
|
|
219
|
+
const refinedMeta = { length: Number(refined.length), mimeType: refined.mime_type, type: refined.type, uri };
|
|
220
|
+
if (refined.type === 'text') {
|
|
221
|
+
// `type: 'text'` means the detector validated `value` as UTF-8,
|
|
222
|
+
// so `fromVec` is non-null here; guard defensively regardless.
|
|
223
|
+
const str = fromVec(value);
|
|
224
|
+
return pure(str === null
|
|
225
|
+
? errorResult(`content is not byte-aligned: ${r.hash}`)
|
|
226
|
+
: okResult(toJson({ ...refinedMeta, text: str })));
|
|
227
|
+
}
|
|
228
|
+
const blob = base64Encode(value);
|
|
229
|
+
return pure(blob === null
|
|
230
|
+
? errorResult(`content is not byte-aligned: ${r.hash}`)
|
|
231
|
+
: okResult(toJson({ ...refinedMeta, blob })));
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
}),
|
|
235
|
+
toolEntry('cas_list', 'List all stored content hashes (cBase32), one per line.', casListArgs, () => c.list().step(hashes => pure(okResult(hashes.map(vecToCBase32).join('\n'))))),
|
|
236
|
+
];
|
|
237
|
+
};
|
|
238
|
+
// ── Handlers ────────────────────────────────────────────────────────────────────
|
|
239
|
+
/**
|
|
240
|
+
* MCP handlers for `FileCas` plus the Evo API (`fjs/cas/evo`) layered on it,
|
|
241
|
+
* bound to `home` and an already-built Evo cache slot (see `initEvo`).
|
|
242
|
+
*/
|
|
243
|
+
export const casMcpHandlers = (home) => (cacheKey) => fromRegistry([...casToolRegistry(home)(cacheKey), ...evoToolRegistry(evo(fileCas(sha256)(home))(cacheKey))]);
|
|
244
|
+
// ── Session configuration ───────────────────────────────────────────────────────
|
|
245
|
+
/**
|
|
246
|
+
* Static MCP configuration for the CAS server: advertises the `tools`
|
|
247
|
+
* capability, identifies the server, and pins the protocol version.
|
|
248
|
+
*/
|
|
249
|
+
export const casConfig = {
|
|
250
|
+
serverInfo: { name: 'functionalscript-cas', version: '0.30.0' },
|
|
251
|
+
capabilities: { tools: {} },
|
|
252
|
+
protocolVersion: '2024-11-05',
|
|
253
|
+
};
|
|
254
|
+
// ── Server ──────────────────────────────────────────────────────────────────────
|
|
255
|
+
/**
|
|
256
|
+
* Runs the combined CAS + Evo MCP server over stdio: scans `~/.cas/` once to
|
|
257
|
+
* build the Evo subject/head cache (`initEvo`), allocates the session-state
|
|
258
|
+
* slot, builds the `mcpStep` for the merged tool registry, and drives the
|
|
259
|
+
* read → parse → dispatch → write loop until stdin EOF.
|
|
260
|
+
*/
|
|
261
|
+
export const casMcpServer = (home) => initEvo(fileCas(sha256)(home)).step(cacheKey => create(uninitializedState).step(sessionKey => stdioTransport(mcpStep(casConfig)(casMcpHandlers(home)(cacheKey))(sessionKey))));
|
|
262
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
263
|
+
export const proof = {
|
|
264
|
+
// casMcpServer is never called in integration tests because it drives a
|
|
265
|
+
// real stdio server; call it here to cover its Effect-building body.
|
|
266
|
+
casMcpServer: () => { casMcpServer('/'); },
|
|
267
|
+
};
|
|
@@ -7,7 +7,10 @@ export declare const proof: {
|
|
|
7
7
|
getContentBase64InflationOverflowWritesInternalError: () => void;
|
|
8
8
|
getContentBase64NearBoundarySucceeds: () => void;
|
|
9
9
|
getContentDoubleEscapedOverflowWritesInternalError: () => void;
|
|
10
|
-
|
|
10
|
+
toolsListAdvertisesSixTools: () => void;
|
|
11
|
+
evoAddListHeadRoundTripsThroughCasServer: () => void;
|
|
12
|
+
evoAddDomainErrorIsErrorThroughCasServer: () => void;
|
|
13
|
+
casAddSyncsEvoCacheForRevisionContent: () => void;
|
|
11
14
|
addReturnsHash: () => void;
|
|
12
15
|
textAddGetMetaRoundTrips: () => void;
|
|
13
16
|
textAddGetContentRoundTrips: () => void;
|
|
@@ -16,13 +19,15 @@ export declare const proof: {
|
|
|
16
19
|
addGetRoundTrips: () => void;
|
|
17
20
|
getUntypedReturnsText: () => void;
|
|
18
21
|
getTypedReturnsBinaryJson: () => void;
|
|
22
|
+
getMetaRecognizesRevisionDialect: () => void;
|
|
23
|
+
getContentRecognizesRevisionDialect: () => void;
|
|
19
24
|
listEnumeratesStoredHashes: () => void;
|
|
20
25
|
addInvalidContentIsError: () => void;
|
|
21
26
|
addBadLengthContentIsError: () => void;
|
|
22
27
|
addBase64OverLimitIsError: () => void;
|
|
23
28
|
addTextOverLimitIsError: () => void;
|
|
24
29
|
addTextAtLimitSucceeds: () => void;
|
|
25
|
-
|
|
30
|
+
addBase64AtLimitSucceeds: () => void;
|
|
26
31
|
getUnterminatedHashIsError: () => void;
|
|
27
32
|
addMissingContentIsError: () => void;
|
|
28
33
|
getMissingHashArgumentIsError: () => void;
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { assert, assertEq } from
|
|
2
|
-
import { pure } from
|
|
3
|
-
import { run } from
|
|
4
|
-
import { asBase, asNominal, create } from
|
|
5
|
-
import { msb, u8ListToVec, vec8, repeat, length, maxLengthBytes } from
|
|
6
|
-
import { vecToCBase32 } from
|
|
7
|
-
import { encode as base64Encode } from
|
|
8
|
-
import { utf8 } from
|
|
9
|
-
import { fileCas } from
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
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';
|
|
19
|
+
import { initEvo } from '../evo/module.f.js';
|
|
18
20
|
const initialTestState = { memory: { next: 0, values: {} } };
|
|
19
21
|
const mock = {
|
|
20
22
|
memCreate: value => state => {
|
|
@@ -55,10 +57,10 @@ const feed = (step) => (msgs) => {
|
|
|
55
57
|
// Rename/ReadBytes/RandomInt/Mkdir). Uses fileKvStore so upload and get share
|
|
56
58
|
// the same filesystem-backed CAS.
|
|
57
59
|
const runSessionVirtual = (root, home = '/home/user') => (msgs) => {
|
|
58
|
-
const effect = create(uninitializedState).step(sessionKey => {
|
|
59
|
-
const step = mcpStep(casConfig)(casMcpHandlers(home))(sessionKey);
|
|
60
|
+
const effect = initEvo(fileCas(sha256)(home)).step(cacheKey => create(uninitializedState).step(sessionKey => {
|
|
61
|
+
const step = mcpStep(casConfig)(casMcpHandlers(home)(cacheKey))(sessionKey);
|
|
60
62
|
return feed(step)(msgs);
|
|
61
|
-
});
|
|
63
|
+
}));
|
|
62
64
|
return virtual({ ...emptyState, root })(effect)[1];
|
|
63
65
|
};
|
|
64
66
|
// Seeds a blob directly into the virtual store via `c.write`, bypassing the
|
|
@@ -90,10 +92,10 @@ const toBytes = (s) => [...fromVec(utf8(s))];
|
|
|
90
92
|
// `session`/`runSessionVirtual`, which collect `mcpStep`'s `Response` values
|
|
91
93
|
// as plain JS objects and never serialize/encode them, this is the only
|
|
92
94
|
// helper that can observe the transport's oversized-response fallback (see
|
|
93
|
-
// `
|
|
95
|
+
// `fjs/mcp/stdio/module.f.ts` `writeResponse`).
|
|
94
96
|
const runStdio = (root, home = '/home/user') => (msgs) => {
|
|
95
97
|
const input = [init, initialized, ...msgs].map(m => JSON.stringify(m)).join('\n') + '\n';
|
|
96
|
-
const effect = create(uninitializedState).step(sessionKey => stdioTransport(mcpStep(casConfig)(casMcpHandlers(home))(sessionKey)));
|
|
98
|
+
const effect = initEvo(fileCas(sha256)(home)).step(cacheKey => create(uninitializedState).step(sessionKey => stdioTransport(mcpStep(casConfig)(casMcpHandlers(home)(cacheKey))(sessionKey))));
|
|
97
99
|
const stdout = virtual({ ...emptyState, root, stdin: toBytes(input) })(effect)[0].stdout;
|
|
98
100
|
// Only requests get a written line (notifications, like `initialized`,
|
|
99
101
|
// write nothing) — drop the `init` response, keep one line per `msgs` entry.
|
|
@@ -104,10 +106,13 @@ const item0 = (resp) => resultOf(resp).content[0];
|
|
|
104
106
|
const textOf = (resp) => item0(resp).text;
|
|
105
107
|
// A plain text sample for text add→get round-trips.
|
|
106
108
|
const textSample = 'hello, world!';
|
|
109
|
+
// A valid `vnd.fjs.revision` blob: every required field present, including the
|
|
110
|
+
// explicit `snapshot` and `generation` the format now mandates.
|
|
111
|
+
const revisionSample = `{"dialect":"${revisionDialect}","subject":"8","parents":[],"snapshot":"8","generation":0}`;
|
|
107
112
|
// A base64-encoded binary payload for binary add→get round-trips.
|
|
108
113
|
const binarySample = base64Encode(vec8(0x2an));
|
|
109
114
|
// A base64 blob whose leading bytes are the PNG magic-byte signature, so
|
|
110
|
-
// `cas_get` detects its type and returns base64 with
|
|
115
|
+
// `cas_get` detects its type and returns base64 with mimeType image/png.
|
|
111
116
|
const pngSample = base64Encode(u8ListToVec(msb)([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x01]));
|
|
112
117
|
// Returns the RFC 4648 base64 encoding of `n` zero bytes, computed directly
|
|
113
118
|
// without bigint arithmetic — independent of `base64.encode` so these tests
|
|
@@ -141,9 +146,10 @@ const largeMultiChunkBlobMeta = (chunk0, chunk1, expectedType, expectedMime) =>
|
|
|
141
146
|
assert(!resultOf(metaResp).isError);
|
|
142
147
|
const meta = JSON.parse(textOf(metaResp));
|
|
143
148
|
assertEq(meta.type, expectedType);
|
|
144
|
-
assertEq(meta.
|
|
149
|
+
assertEq(meta.mimeType, expectedMime);
|
|
145
150
|
assertEq(meta.length, Number((length(chunk0) + length(chunk1)) / 8n));
|
|
146
|
-
assertEq(meta.
|
|
151
|
+
assertEq(meta.text, undefined);
|
|
152
|
+
assertEq(meta.blob, undefined);
|
|
147
153
|
};
|
|
148
154
|
// A full `maxLengthBytes`-long chunk of repeated ASCII 'a' — valid UTF-8.
|
|
149
155
|
const asciiChunk = repeat(maxLengthBytes)(vec8(0x61n));
|
|
@@ -202,12 +208,12 @@ export const proof = {
|
|
|
202
208
|
// the JSON-RPC envelope is even added. The transport's `writeResponse`
|
|
203
209
|
// (`tryUtf8`) must catch this and write a JSON-RPC internal-error response —
|
|
204
210
|
// carrying the *original request's* `id` — instead of crashing the process.
|
|
205
|
-
// See `
|
|
211
|
+
// See `fjs/mcp/stdio/module.f.ts` `writeResponse`.
|
|
206
212
|
//
|
|
207
213
|
// This test originally timed out under `bun test`'s native 5s per-test
|
|
208
214
|
// limit (12-14s observed in CI on PR #1201) — the cost was in
|
|
209
215
|
// `base64Encode`, quadratic before the `baseN.vecToString` fix (see
|
|
210
|
-
// `
|
|
216
|
+
// `fjs/basen/base64/proof.f.ts` `encodeLargeVecIsSlow`). Now well under budget on
|
|
211
217
|
// both engines.
|
|
212
218
|
getContentBase64InflationOverflowWritesInternalError: () => {
|
|
213
219
|
const [root, hash] = seedBlob({})([oversizedBase64Chunk]);
|
|
@@ -251,14 +257,41 @@ export const proof = {
|
|
|
251
257
|
assertEq(err.error?.code, -32603);
|
|
252
258
|
assertEq(err.id, 2);
|
|
253
259
|
},
|
|
254
|
-
|
|
260
|
+
toolsListAdvertisesSixTools: () => {
|
|
255
261
|
const [resp] = runSessionVirtual({})([init, initialized, list(2)]).slice(2);
|
|
256
262
|
const tools = resp.result.tools;
|
|
257
|
-
assertEq(tools.length,
|
|
258
|
-
assertEq(tools.map(t => t.name).join(','), 'cas_add,cas_get,cas_list');
|
|
263
|
+
assertEq(tools.length, 6);
|
|
264
|
+
assertEq(tools.map(t => t.name).join(','), 'cas_add,cas_get,cas_list,evo_list,evo_head,evo_add');
|
|
259
265
|
const add = resp.result.tools[0];
|
|
260
266
|
assertEq(add.inputSchema.type, 'object');
|
|
261
267
|
},
|
|
268
|
+
// The same server also exposes Evo (fjs/cas/evo): add a revision, then
|
|
269
|
+
// find it via evo_list/evo_head — one process, one store, one cache.
|
|
270
|
+
evoAddListHeadRoundTripsThroughCasServer: () => {
|
|
271
|
+
const [addResp, listResp, headResp] = session(call(2, 'evo_add', { parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x1n)) }), call(3, 'evo_list', {}), call(4, 'evo_head', { subject: 'doc' }));
|
|
272
|
+
assert(!resultOf(addResp).isError);
|
|
273
|
+
const hash = textOf(addResp);
|
|
274
|
+
assert(hash.length > 0);
|
|
275
|
+
assertEq(textOf(listResp), '["doc"]');
|
|
276
|
+
assertEq(textOf(headResp), hash);
|
|
277
|
+
},
|
|
278
|
+
evoAddDomainErrorIsErrorThroughCasServer: () => {
|
|
279
|
+
// Valid arguments, but the domain rule fails: `subject` is required
|
|
280
|
+
// unless there is exactly one parent.
|
|
281
|
+
const [resp] = session(call(2, 'evo_add', { parents: [] }));
|
|
282
|
+
assertEq(resultOf(resp).isError, true);
|
|
283
|
+
assert(textOf(resp).includes('subject is required'));
|
|
284
|
+
},
|
|
285
|
+
// A plain `cas_add` of `vnd.fjs.revision` content is folded into the Evo
|
|
286
|
+
// cache too (`syncRevision`), not just `evo_add` — `cas_add`/`evo_add`
|
|
287
|
+
// are two ways to reach the same store and cache.
|
|
288
|
+
casAddSyncsEvoCacheForRevisionContent: () => {
|
|
289
|
+
const [addResp, listResp, headResp] = session(call(2, 'cas_add', { content: revisionSample }), call(3, 'evo_list', {}), call(4, 'evo_head', { subject: '8' }));
|
|
290
|
+
assert(!resultOf(addResp).isError);
|
|
291
|
+
const hash = textOf(addResp);
|
|
292
|
+
assertEq(textOf(listResp), '["8"]');
|
|
293
|
+
assertEq(textOf(headResp), hash);
|
|
294
|
+
},
|
|
262
295
|
addReturnsHash: () => {
|
|
263
296
|
const [resp] = session(call(2, 'cas_add', { content: textSample }));
|
|
264
297
|
assert(!resultOf(resp).isError);
|
|
@@ -271,10 +304,10 @@ export const proof = {
|
|
|
271
304
|
const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash }));
|
|
272
305
|
assert(!resultOf(getResp).isError);
|
|
273
306
|
const result = JSON.parse(textOf(getResp));
|
|
274
|
-
assertEq(result.
|
|
307
|
+
assertEq(result.mimeType, 'text/plain');
|
|
275
308
|
assertEq(result.type, 'text');
|
|
276
309
|
assertEq(result.length, textSample.length);
|
|
277
|
-
assertEq(result.
|
|
310
|
+
assertEq(result.text, undefined);
|
|
278
311
|
},
|
|
279
312
|
// cas_get with content:true returns inline content.
|
|
280
313
|
textAddGetContentRoundTrips: () => {
|
|
@@ -284,8 +317,8 @@ export const proof = {
|
|
|
284
317
|
assert(!resultOf(getResp).isError);
|
|
285
318
|
const result = JSON.parse(textOf(getResp));
|
|
286
319
|
assertEq(result.type, 'text');
|
|
287
|
-
assertEq(result.
|
|
288
|
-
assertEq(result.
|
|
320
|
+
assertEq(result.mimeType, 'text/plain');
|
|
321
|
+
assertEq(result.text, textSample);
|
|
289
322
|
},
|
|
290
323
|
// Binary add→get round-trip: store as base64, retrieve metadata without content.
|
|
291
324
|
binaryAddGetMetaRoundTrips: () => {
|
|
@@ -294,9 +327,9 @@ export const proof = {
|
|
|
294
327
|
const [, getResp] = session(call(2, 'cas_add', { content: binarySample, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
295
328
|
assert(!resultOf(getResp).isError);
|
|
296
329
|
const result = JSON.parse(textOf(getResp));
|
|
297
|
-
assertEq(result.
|
|
330
|
+
assertEq(result.text, undefined);
|
|
298
331
|
assertEq(result.type, 'text');
|
|
299
|
-
assertEq(result.
|
|
332
|
+
assertEq(result.mimeType, 'text/plain');
|
|
300
333
|
},
|
|
301
334
|
// Binary add→get with content:true returns inline base64 content.
|
|
302
335
|
binaryAddGetContentRoundTrips: () => {
|
|
@@ -306,7 +339,7 @@ export const proof = {
|
|
|
306
339
|
assert(!resultOf(getResp).isError);
|
|
307
340
|
const result = JSON.parse(textOf(getResp));
|
|
308
341
|
assertEq(result.type, 'text');
|
|
309
|
-
assertEq(result.
|
|
342
|
+
assertEq(result.text, '*');
|
|
310
343
|
},
|
|
311
344
|
addGetRoundTrips: () => {
|
|
312
345
|
const [addResp] = session(call(2, 'cas_add', { content: textSample }));
|
|
@@ -314,7 +347,7 @@ export const proof = {
|
|
|
314
347
|
const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash, content: true }));
|
|
315
348
|
assert(!resultOf(getResp).isError);
|
|
316
349
|
const result = JSON.parse(textOf(getResp));
|
|
317
|
-
assertEq(result.
|
|
350
|
+
assertEq(result.text, textSample);
|
|
318
351
|
},
|
|
319
352
|
// Text content (no magic-byte match, valid UTF-8) comes back with type:'text' when content requested.
|
|
320
353
|
getUntypedReturnsText: () => {
|
|
@@ -324,9 +357,9 @@ export const proof = {
|
|
|
324
357
|
assertEq(item0(getResp) === null ? null : item0(getResp).type, 'text');
|
|
325
358
|
const result = JSON.parse(textOf(getResp));
|
|
326
359
|
assertEq(result.type, 'text');
|
|
327
|
-
assertEq(result.
|
|
360
|
+
assertEq(result.mimeType, 'text/plain');
|
|
328
361
|
},
|
|
329
|
-
// Bytes with a recognised PNG signature come back as base64 with
|
|
362
|
+
// Bytes with a recognised PNG signature come back as base64 with mimeType image/png when content requested.
|
|
330
363
|
getTypedReturnsBinaryJson: () => {
|
|
331
364
|
const [addResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }));
|
|
332
365
|
const hash = textOf(addResp);
|
|
@@ -335,8 +368,33 @@ export const proof = {
|
|
|
335
368
|
assertEq(item0(getResp).type, 'text');
|
|
336
369
|
const result = JSON.parse(textOf(getResp));
|
|
337
370
|
assertEq(result.type, 'base64');
|
|
338
|
-
assertEq(result.
|
|
339
|
-
assertEq(result.
|
|
371
|
+
assertEq(result.mimeType, 'image/png');
|
|
372
|
+
assertEq(result.blob, pngSample);
|
|
373
|
+
},
|
|
374
|
+
// A stored `vnd.fjs.revision` blob is recognized by metadata-only
|
|
375
|
+
// `cas_get` (content:false, the default) — the CAS read path routes the
|
|
376
|
+
// bounded, whole-blob-text case through the dialect-aware detector rather
|
|
377
|
+
// than only reporting the streaming text/plain verdict.
|
|
378
|
+
getMetaRecognizesRevisionDialect: () => {
|
|
379
|
+
const [addResp] = session(call(2, 'cas_add', { content: revisionSample }));
|
|
380
|
+
const hash = textOf(addResp);
|
|
381
|
+
const [, metaResp] = session(call(2, 'cas_add', { content: revisionSample }), call(3, 'cas_get', { hash }));
|
|
382
|
+
assert(!resultOf(metaResp).isError);
|
|
383
|
+
const meta = JSON.parse(textOf(metaResp));
|
|
384
|
+
assertEq(meta.mimeType, revisionMediaType);
|
|
385
|
+
assertEq(meta.type, 'text');
|
|
386
|
+
},
|
|
387
|
+
// Same dialect recognition on the `content: true` path, alongside the
|
|
388
|
+
// inline text payload.
|
|
389
|
+
getContentRecognizesRevisionDialect: () => {
|
|
390
|
+
const [addResp] = session(call(2, 'cas_add', { content: revisionSample }));
|
|
391
|
+
const hash = textOf(addResp);
|
|
392
|
+
const [, getResp] = session(call(2, 'cas_add', { content: revisionSample }), call(3, 'cas_get', { hash, content: true }));
|
|
393
|
+
assert(!resultOf(getResp).isError);
|
|
394
|
+
const result = JSON.parse(textOf(getResp));
|
|
395
|
+
assertEq(result.mimeType, revisionMediaType);
|
|
396
|
+
assertEq(result.type, 'text');
|
|
397
|
+
assertEq(result.text, revisionSample);
|
|
340
398
|
},
|
|
341
399
|
listEnumeratesStoredHashes: () => {
|
|
342
400
|
const [addResp, listResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_list', {}));
|
|
@@ -375,16 +433,14 @@ export const proof = {
|
|
|
375
433
|
assert(!resultOf(resp).isError);
|
|
376
434
|
assert(textOf(resp).length > 0);
|
|
377
435
|
},
|
|
378
|
-
// base64 of exactly maxLengthBytes —
|
|
379
|
-
//
|
|
380
|
-
// maxLength
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
addBase64AtLimitIsError: () => {
|
|
436
|
+
// base64 of exactly maxLengthBytes — base64Decode trims the encoding's
|
|
437
|
+
// padding bits from the last character before measuring the result
|
|
438
|
+
// against maxLength, so a payload landing exactly at the limit decodes
|
|
439
|
+
// and stores cleanly.
|
|
440
|
+
addBase64AtLimitSucceeds: () => {
|
|
384
441
|
const [resp] = session(call(2, 'cas_add', { content: base64OfA(maxLengthBytes), type: 'base64' }));
|
|
385
|
-
|
|
386
|
-
assert(textOf(resp).
|
|
387
|
-
assert(textOf(resp).includes('cas add'));
|
|
442
|
+
assert(!resultOf(resp).isError);
|
|
443
|
+
assert(textOf(resp).length > 0);
|
|
388
444
|
},
|
|
389
445
|
getUnterminatedHashIsError: () => {
|
|
390
446
|
const [resp] = session(call(2, 'cas_get', { hash: '0' }));
|
|
@@ -423,10 +479,10 @@ export const proof = {
|
|
|
423
479
|
const [, metaResp] = session(call(2, 'cas_add', { content: 'text content' }), call(3, 'cas_get', { hash }));
|
|
424
480
|
assert(!resultOf(metaResp).isError);
|
|
425
481
|
const meta = JSON.parse(textOf(metaResp));
|
|
426
|
-
assertEq(meta.
|
|
482
|
+
assertEq(meta.mimeType, 'text/plain');
|
|
427
483
|
assertEq(meta.type, 'text');
|
|
428
484
|
assertEq(meta.length, 12);
|
|
429
|
-
assertEq(meta.
|
|
485
|
+
assertEq(meta.text, undefined);
|
|
430
486
|
},
|
|
431
487
|
getMetaBinaryBlob: () => {
|
|
432
488
|
const [addResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }));
|
|
@@ -434,10 +490,10 @@ export const proof = {
|
|
|
434
490
|
const [, metaResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
435
491
|
assert(!resultOf(metaResp).isError);
|
|
436
492
|
const meta = JSON.parse(textOf(metaResp));
|
|
437
|
-
assertEq(meta.
|
|
493
|
+
assertEq(meta.mimeType, 'image/png');
|
|
438
494
|
assertEq(meta.type, 'base64');
|
|
439
495
|
assertEq(meta.length, 10);
|
|
440
|
-
assertEq(meta.
|
|
496
|
+
assertEq(meta.blob, undefined);
|
|
441
497
|
},
|
|
442
498
|
getMetaOctetStreamForUnknownBinary: () => {
|
|
443
499
|
const binaryContent = u8ListToVec(msb)([0xFF, 0xFE, 0x00, 0x01]);
|
|
@@ -447,9 +503,9 @@ export const proof = {
|
|
|
447
503
|
const [, metaResp] = session(call(2, 'cas_add', { content: binaryB64, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
448
504
|
assert(!resultOf(metaResp).isError);
|
|
449
505
|
const meta = JSON.parse(textOf(metaResp));
|
|
450
|
-
assertEq(meta.
|
|
506
|
+
assertEq(meta.mimeType, 'application/octet-stream');
|
|
451
507
|
assertEq(meta.type, 'base64');
|
|
452
|
-
assertEq(meta.
|
|
508
|
+
assertEq(meta.blob, undefined);
|
|
453
509
|
},
|
|
454
510
|
// A NUL-bearing blob is valid UTF-8 yet binary: cas_get must report
|
|
455
511
|
// base64/octet-stream, not text/plain.
|
|
@@ -461,7 +517,7 @@ export const proof = {
|
|
|
461
517
|
const [, metaResp] = session(call(2, 'cas_add', { content: nulB64, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
462
518
|
assert(!resultOf(metaResp).isError);
|
|
463
519
|
const meta = JSON.parse(textOf(metaResp));
|
|
464
|
-
assertEq(meta.
|
|
520
|
+
assertEq(meta.mimeType, 'application/octet-stream');
|
|
465
521
|
assertEq(meta.type, 'base64');
|
|
466
522
|
},
|
|
467
523
|
getMetaMissingHashIsError: () => {
|
|
@@ -482,8 +538,8 @@ export const proof = {
|
|
|
482
538
|
const [, getResp] = session(call(2, 'cas_add', { content: binaryB64, type: 'base64' }), call(3, 'cas_get', { hash, content: true }));
|
|
483
539
|
assert(!resultOf(getResp).isError);
|
|
484
540
|
const result = JSON.parse(textOf(getResp));
|
|
485
|
-
assertEq(result.
|
|
541
|
+
assertEq(result.mimeType, 'application/octet-stream');
|
|
486
542
|
assertEq(result.type, 'base64');
|
|
487
|
-
assertEq(result.
|
|
543
|
+
assertEq(result.blob, binaryB64);
|
|
488
544
|
},
|
|
489
545
|
};
|