functionalscript 0.36.0 → 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.
- package/fs/asn.1/module.f.js +1 -1
- package/fs/asserts/proof.f.js +1 -1
- package/fs/base_n/module.f.js +3 -3
- package/fs/base_n/proof.f.js +3 -3
- package/fs/{base128 → basen/base128}/module.f.d.ts +1 -1
- package/fs/{base128 → basen/base128}/module.f.js +1 -1
- package/fs/{base128 → basen/base128}/proof.f.js +3 -3
- package/fs/{base64 → basen/base64}/module.f.d.ts +2 -2
- package/fs/{base64 → basen/base64}/module.f.js +25 -15
- package/fs/{base64 → basen/base64}/proof.f.d.ts +1 -0
- package/fs/{base64 → basen/base64}/proof.f.js +11 -5
- package/fs/{cbase32 → basen/cbase32}/module.f.d.ts +2 -2
- package/fs/{cbase32 → basen/cbase32}/module.f.js +2 -2
- package/fs/{cbase32 → basen/cbase32}/proof.f.js +1 -1
- package/fs/bnf/data/module.f.js +4 -4
- package/fs/bnf/data/proof.f.js +6 -6
- package/fs/bnf/descent/module.f.js +6 -6
- package/fs/bnf/descent/proof.f.js +6 -6
- package/fs/bnf/ll1/module.f.js +9 -9
- package/fs/bnf/ll1/proof.f.js +6 -6
- package/fs/bnf/module.f.js +4 -4
- package/fs/bnf/proof.f.js +2 -2
- package/fs/bnf/testlib.f.js +1 -1
- package/fs/cas/cli/module.f.js +7 -7
- package/fs/cas/cli/proof.f.js +8 -8
- package/fs/cas/mcp/module.f.js +95 -55
- package/fs/cas/mcp/proof.f.d.ts +3 -1
- package/fs/cas/mcp/proof.f.js +79 -51
- package/fs/cas/module.f.js +10 -10
- package/fs/cas/proof.f.d.ts +4 -2
- package/fs/cas/proof.f.js +10 -10
- package/fs/ci/bun/module.f.js +2 -2
- package/fs/ci/common/module.f.d.ts +2 -2
- package/fs/ci/common/module.f.js +4 -4
- package/fs/ci/config/module.f.d.ts +19 -20
- package/fs/ci/config/module.f.js +3 -5
- package/fs/ci/deno/module.f.js +8 -4
- package/fs/ci/module.f.js +9 -9
- package/fs/ci/node/module.f.d.ts +1 -1
- package/fs/ci/node/module.f.js +3 -5
- package/fs/ci/playwright/module.f.js +3 -3
- package/fs/ci/proof.f.js +14 -14
- package/fs/ci/rust/module.f.js +2 -2
- package/fs/cli/module.f.js +3 -3
- package/fs/cli/proof.f.js +3 -3
- package/fs/{types → common}/monoid/module.f.d.ts +1 -1
- package/fs/crypto/hmac/module.f.js +2 -2
- package/fs/crypto/hmac/proof.f.js +5 -5
- package/fs/crypto/pow/module.f.js +3 -3
- package/fs/crypto/pow/proof.f.js +4 -4
- package/fs/crypto/secp/module.f.js +2 -2
- package/fs/crypto/secp/proof.f.js +3 -3
- package/fs/crypto/sha2/module.f.js +2 -2
- package/fs/crypto/sha2/proof.f.js +6 -6
- package/fs/crypto/sign/module.f.js +4 -4
- package/fs/crypto/vdf/module.f.js +1 -1
- package/fs/crypto/vdf/proof.f.js +1 -1
- package/fs/dev/module.f.js +6 -6
- package/fs/dev/package_json/module.f.js +6 -6
- package/fs/dev/package_json/proof.f.js +3 -3
- package/fs/dev/proof.f.js +2 -2
- package/fs/djs/ast/module.f.js +2 -2
- package/fs/djs/ast/proof.f.js +4 -4
- package/fs/djs/examples/m.f.d.ts +1 -1
- package/fs/djs/module.f.d.ts +1 -1
- package/fs/djs/module.f.js +5 -5
- package/fs/djs/parser/module.f.js +4 -4
- package/fs/djs/parser/proof.f.js +7 -7
- package/fs/djs/proof.f.js +3 -3
- package/fs/djs/serializer/module.f.js +6 -6
- package/fs/djs/serializer/proof.f.js +4 -4
- package/fs/djs/tokenizer/module.f.js +3 -3
- package/fs/djs/tokenizer/proof.f.js +5 -5
- package/fs/djs/tokenizer-new/proof.f.js +6 -6
- package/fs/djs/transpiler/module.f.d.ts +5 -0
- package/fs/djs/transpiler/module.f.js +21 -11
- package/fs/djs/transpiler/proof.f.js +5 -5
- package/fs/effects/memory/module.f.js +4 -4
- package/fs/effects/memory/proof.f.js +3 -3
- package/fs/effects/module.f.js +1 -1
- package/fs/effects/node/memory/module.js +2 -2
- package/fs/effects/node/memory/proof.js +3 -3
- package/fs/effects/node/module.f.js +7 -7
- package/fs/effects/node/module.js +10 -10
- package/fs/effects/node/virtual/module.f.js +7 -7
- package/fs/effects/node/virtual/proof.f.d.ts +1 -0
- package/fs/effects/node/virtual/proof.f.js +11 -4
- package/fs/effects/proof.f.js +2 -2
- package/fs/emergent_testing/all.test.js +2 -2
- package/fs/emergent_testing/example.f.d.ts +1 -1
- package/fs/emergent_testing/module.f.js +6 -6
- package/fs/emergent_testing/proof.f.d.ts +19 -17
- package/fs/emergent_testing/proof.f.js +7 -7
- package/fs/emergent_testing/scenarios/all.js +1 -1
- package/fs/fsc/bnf.f.js +2 -2
- package/fs/fsc/json.f.js +1 -1
- package/fs/fsc/module.f.js +7 -7
- package/fs/fsc/proof.f.js +4 -4
- package/fs/fsm/module.f.js +9 -9
- package/fs/fsm/proof.f.js +7 -7
- package/fs/js/tokenizer/module.f.js +7 -7
- package/fs/js/tokenizer/proof.f.js +5 -5
- package/fs/mcp/module.f.d.ts +13 -13
- package/fs/mcp/module.f.js +8 -8
- package/fs/mcp/proof.f.js +5 -5
- package/fs/mcp/stdio/module.f.d.ts +2 -2
- package/fs/mcp/stdio/module.f.js +10 -10
- package/fs/mcp/stdio/proof.f.js +10 -10
- package/fs/{html → media/html}/module.f.d.ts +3 -3
- package/fs/{html → media/html}/module.f.js +9 -9
- package/fs/media/json/module.f.d.ts +50 -0
- package/fs/{json → media/json}/module.f.js +7 -7
- package/fs/{json → media/json}/parser/module.f.d.ts +2 -2
- package/fs/{json → media/json}/parser/module.f.js +7 -7
- package/fs/{json → media/json}/parser/proof.f.js +6 -6
- package/fs/{json → media/json}/proof.f.js +4 -4
- package/fs/media/json/rpc/module.f.d.ts +114 -0
- package/fs/{json → media/json}/rpc/module.f.js +3 -3
- package/fs/{json → media/json}/rpc/proof.f.js +4 -4
- package/fs/media/json/schema/module.f.d.ts +78 -0
- package/fs/{json → media/json}/schema/module.f.js +2 -2
- package/fs/{json → media/json}/schema/proof.f.js +3 -3
- package/fs/{json → media/json}/serializer/module.f.d.ts +1 -1
- package/fs/{json → media/json}/serializer/module.f.js +2 -2
- package/fs/{json → media/json}/serializer/proof.f.js +2 -2
- package/fs/{json → media/json}/tokenizer/module.f.d.ts +2 -2
- package/fs/{json → media/json}/tokenizer/module.f.js +4 -4
- package/fs/{json → media/json}/tokenizer/proof.f.js +5 -5
- package/fs/media/module.f.d.ts +37 -0
- package/fs/media/module.f.js +24 -0
- package/fs/media/proof.f.d.ts +9 -0
- package/fs/media/proof.f.js +57 -0
- package/fs/media/revision/module.f.d.ts +51 -0
- package/fs/media/revision/module.f.js +110 -0
- package/fs/media/revision/proof.f.d.ts +29 -0
- package/fs/media/revision/proof.f.js +119 -0
- package/fs/{mime → media/type}/module.f.d.ts +6 -6
- package/fs/{mime → media/type}/module.f.js +6 -6
- package/fs/{mime → media/type}/proof.f.js +6 -6
- package/fs/module.f.d.ts +2 -0
- package/fs/{fjs/module.f.js → module.f.js} +8 -8
- package/fs/module.js +4 -0
- package/fs/path/module.f.js +2 -2
- package/fs/{fjs/proof.f.js → proof.f.js} +4 -4
- package/fs/sul/id/module.f.js +9 -9
- package/fs/sul/id/proof.f.js +5 -5
- package/fs/sul/level/hash/module.f.js +3 -3
- package/fs/sul/level/hash/proof.f.js +3 -3
- package/fs/sul/level/literal/module.f.js +5 -5
- package/fs/sul/level/literal/proof.f.js +3 -3
- package/fs/sul/module.f.js +3 -3
- package/fs/sul/proof.f.js +3 -3
- package/fs/text/ascii/proof.f.js +3 -3
- package/fs/text/code_point/module.f.js +2 -2
- package/fs/text/code_point/proof.f.js +1 -1
- package/fs/text/module.f.js +5 -5
- package/fs/text/proof.f.js +4 -4
- package/fs/text/sgr/module.f.js +2 -2
- package/fs/text/sgr/proof.f.js +2 -2
- package/fs/text/utf16/module.f.js +5 -5
- package/fs/text/utf16/proof.f.js +4 -4
- package/fs/text/utf8/module.f.js +22 -17
- package/fs/text/utf8/proof.f.js +5 -5
- package/fs/types/array/module.f.js +1 -1
- package/fs/types/array/proof.f.js +3 -3
- package/fs/types/bigfloat/module.f.js +1 -1
- package/fs/types/bigfloat/proof.f.js +1 -1
- package/fs/types/bigint/module.f.js +2 -2
- package/fs/types/bigint/proof.f.js +3 -3
- package/fs/types/bit_vec/module.f.js +8 -8
- package/fs/types/bit_vec/proof.f.js +5 -5
- package/fs/types/btree/find/module.f.js +1 -1
- package/fs/types/btree/find/proof.f.d.ts +3 -1
- package/fs/types/btree/find/proof.f.js +7 -7
- package/fs/types/btree/module.f.js +2 -2
- package/fs/types/btree/proof.f.d.ts +11 -5
- package/fs/types/btree/proof.f.js +7 -7
- package/fs/types/btree/remove/module.f.js +4 -4
- package/fs/types/btree/remove/proof.f.d.ts +5 -2
- package/fs/types/btree/remove/proof.f.js +5 -5
- package/fs/types/btree/set/module.f.js +3 -3
- package/fs/types/btree/set/proof.f.js +4 -4
- package/fs/types/byte_set/module.f.js +2 -2
- package/fs/types/byte_set/proof.f.js +4 -4
- package/fs/types/function/compare/proof.f.js +1 -1
- package/fs/types/function/operator/proof.f.d.ts +21 -10
- package/fs/types/function/operator/proof.f.js +1 -1
- package/fs/types/function/proof.f.js +1 -1
- package/fs/types/list/module.f.js +2 -2
- package/fs/types/list/proof.f.d.ts +15 -7
- package/fs/types/list/proof.f.js +4 -4
- package/fs/types/map/proof.f.js +2 -2
- package/fs/types/nibble_set/proof.f.js +2 -2
- package/fs/types/nullable/module.f.js +2 -2
- package/fs/types/nullable/proof.f.js +1 -1
- package/fs/types/number/module.f.js +5 -5
- package/fs/types/number/proof.f.js +1 -1
- package/fs/types/object/module.f.js +4 -4
- package/fs/types/object/proof.f.js +1 -1
- package/fs/types/ordered_map/module.f.js +6 -6
- package/fs/types/ordered_map/proof.f.js +3 -3
- package/fs/types/patricia_trie/proof.f.d.ts +11 -5
- package/fs/types/patricia_trie/proof.f.js +2 -2
- package/fs/types/prime_field/module.f.js +2 -2
- package/fs/types/prime_field/proof.f.js +2 -2
- package/fs/types/range/proof.f.js +1 -1
- package/fs/types/range_map/module.f.js +4 -4
- package/fs/types/range_map/proof.f.js +7 -7
- package/fs/types/result/module.js +1 -1
- package/fs/types/result/proof.f.d.ts +9 -4
- package/fs/types/rtti/common/module.f.js +4 -4
- package/fs/types/rtti/module.f.d.ts +2 -2
- package/fs/types/rtti/module.f.js +1 -1
- package/fs/types/rtti/parse/module.f.js +5 -5
- package/fs/types/rtti/parse/proof.f.js +2 -2
- package/fs/types/rtti/ts/module.f.js +1 -1
- package/fs/types/rtti/ts/proof.f.js +2 -2
- package/fs/types/rtti/validate/module.f.js +4 -4
- package/fs/types/rtti/validate/proof.f.js +2 -2
- package/fs/types/sorted_list/module.f.js +3 -3
- package/fs/types/sorted_list/proof.f.js +6 -6
- package/fs/types/sorted_set/module.f.js +1 -1
- package/fs/types/sorted_set/proof.f.js +6 -6
- package/fs/types/string/module.f.js +4 -4
- package/fs/types/string/proof.f.js +2 -2
- package/fs/types/string_set/module.f.js +6 -6
- package/fs/types/string_set/proof.f.js +1 -1
- package/fs/types/ts/proof.f.d.ts +18 -18
- package/fs/types/ts/proof.f.js +1 -1
- package/fs/types/uint8array/module.f.js +4 -4
- package/fs/types/uint8array/proof.f.js +4 -4
- package/fs/website/module.f.js +3 -3
- package/fs/website/proof.f.js +3 -3
- package/package.json +8 -8
- 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/{base128 → basen/base128}/proof.f.d.ts +0 -0
- /package/fs/{cbase32 → basen/cbase32}/proof.f.d.ts +0 -0
- /package/fs/{types → common}/monoid/module.f.js +0 -0
- /package/fs/{types → common}/monoid/proof.f.d.ts +0 -0
- /package/fs/{types → common}/monoid/proof.f.js +0 -0
- /package/fs/{html → media/html}/proof.f.d.ts +0 -0
- /package/fs/{html → media/html}/proof.f.js +0 -0
- /package/fs/{json → media/json}/parser/proof.f.d.ts +0 -0
- /package/fs/{json → media/json}/proof.f.d.ts +0 -0
- /package/fs/{json → media/json}/rpc/proof.f.d.ts +0 -0
- /package/fs/{json → media/json}/schema/proof.f.d.ts +0 -0
- /package/fs/{json → media/json}/serializer/proof.f.d.ts +0 -0
- /package/fs/{json → media/json}/tokenizer/proof.f.d.ts +0 -0
- /package/fs/{mime → media/type}/proof.f.d.ts +0 -0
- /package/fs/{fjs/module.d.ts → module.d.ts} +0 -0
- /package/fs/{fjs/proof.f.d.ts → proof.f.d.ts} +0 -0
package/fs/cas/mcp/module.f.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* | Tool | args | action | result |
|
|
13
13
|
* |------------|-------------------------------|------------------|-------------------------------------|
|
|
14
14
|
* | `cas_add` | `{ content, type? }` | `c.write(...)` | hash (cBase32) |
|
|
15
|
-
* | `cas_get` | `{ hash, content?: boolean }` | `c.read(key)` | JSON `{length,
|
|
15
|
+
* | `cas_get` | `{ hash, content?: boolean }` | `c.read(key)` | JSON `{length,mimeType,type[,uri][,text\|blob]}` |
|
|
16
16
|
* | `cas_list` | `{}` | `c.list()` | hashes, one per line |
|
|
17
17
|
*
|
|
18
18
|
* ## `cas_add` input encoding
|
|
@@ -31,37 +31,56 @@
|
|
|
31
31
|
*
|
|
32
32
|
* ## `cas_get` output
|
|
33
33
|
*
|
|
34
|
-
* Always returns a JSON object `{ length,
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
34
|
+
* Always returns a JSON object `{ length, mimeType, type[, uri][, text | blob] }`.
|
|
35
|
+
* These field names mirror the MCP resource-contents shape (`resources/read`
|
|
36
|
+
* results), so the tool view and the future resource view of a blob use the same
|
|
37
|
+
* vocabulary: `mimeType` (not `mime_type`), `uri` (not `url`) — the resource URI
|
|
38
|
+
* under which the same blob will be readable via `resources/read` — and `text` /
|
|
39
|
+
* `blob` (not a `type`-tagged `content`) for inline payloads. `type` is always
|
|
40
|
+
* present (`'text'` or `'base64'`) as the discriminator for which of `text` /
|
|
41
|
+
* `blob` a later `content: true` fetch would populate — MCP allows extra fields,
|
|
42
|
+
* so `type` and `length` simply stay alongside the required resource-contents
|
|
43
|
+
* fields. A single classifier — the `fs/media/type` detector state machine (length ×
|
|
44
|
+
* magic-byte eliminator × UTF-8 DFA) — produces the same three-way verdict on
|
|
45
|
+
* both paths, so there is no second, divergent copy of the rules:
|
|
39
46
|
*
|
|
40
47
|
* 1. **Magic-byte hit** (PNG/JPEG/GIF/WebP/PDF/ZIP) → `type: 'base64'` with the
|
|
41
|
-
* detected `
|
|
42
|
-
* 2. **Whole-blob-valid UTF-8** → `type: 'text'`, `
|
|
43
|
-
* 3. **Fallback** → `type: 'base64'`, `
|
|
44
|
-
*
|
|
45
|
-
* **Metadata-only (`content: false`, the default) is size-independent
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* `
|
|
56
|
-
*
|
|
57
|
-
* is
|
|
58
|
-
*
|
|
59
|
-
*
|
|
48
|
+
* detected `mimeType`.
|
|
49
|
+
* 2. **Whole-blob-valid UTF-8** → `type: 'text'`, `mimeType: 'text/plain'`.
|
|
50
|
+
* 3. **Fallback** → `type: 'base64'`, `mimeType: 'application/octet-stream'`.
|
|
51
|
+
*
|
|
52
|
+
* **Metadata-only (`content: false`, the default) is size-independent for
|
|
53
|
+
* non-text and oversized blobs.** It folds the read stream through
|
|
54
|
+
* `fs/media/type` `detectStream` and never buffers the blob. Because a single
|
|
55
|
+
* `Vec` caps at `maxLength` bits (128 KiB), the old drain-into-one-`Vec`
|
|
56
|
+
* approach failed on any blob larger than one chunk even when only metadata
|
|
57
|
+
* was asked for; streaming detection returns correct
|
|
58
|
+
* `{ length, mimeType, type[, uri] }` regardless of size. A dialect (e.g.
|
|
59
|
+
* `vnd.fjs.revision`, see `fs/media/module.f.ts`) can only be recognized by
|
|
60
|
+
* parsing the whole blob as JSON, so when the streaming verdict is
|
|
61
|
+
* whole-blob-valid text within the same bounded inline cap as `content: true`
|
|
62
|
+
* (`maxLengthBytes`), one extra bounded read materializes the blob purely to
|
|
63
|
+
* refine `mimeType` through the dialect-aware detector; a larger or non-text
|
|
64
|
+
* blob is never a dialect match and is returned from the streaming verdict
|
|
65
|
+
* alone, unbuffered.
|
|
66
|
+
*
|
|
67
|
+
* **`content: true`** first derives `{ length, mimeType, type }` with the same
|
|
68
|
+
* size-independent `fs/media/type` `detectStream` machine (to decide whether the
|
|
69
|
+
* blob even fits inline), then materializes the bytes (via `collectRead`, bounded
|
|
70
|
+
* by `maxLength`) and re-derives the verdict from the dialect-aware detector
|
|
71
|
+
* (`fs/media/module.f.ts` `detect`) — now that the whole blob is in hand, a
|
|
72
|
+
* dialect match is reflected in the result — before encoding the inline payload
|
|
73
|
+
* by `type` — a `fs/text/utf8` `fromVec` string on `text` (for `type: 'text'`),
|
|
74
|
+
* base64 on `blob` (for `type: 'base64'`). A blob larger than `maxLength` (128
|
|
75
|
+
* KiB) cannot be buffered into one `Vec`, so it is rejected here with a
|
|
76
|
+
* descriptive *"too large"* error (carrying the byte size and `uri`) rather than
|
|
77
|
+
* being misreported as absent; it should be fetched via `uri` or inspected with
|
|
78
|
+
* metadata-only `cas_get`.
|
|
60
79
|
*
|
|
61
80
|
* ## Encoding split: hashes vs. content
|
|
62
81
|
*
|
|
63
82
|
* `Cas<O>` deals in `Vec` (bit vectors); MCP models only `textContent` today.
|
|
64
|
-
* **Hashes** travel as cBase32 (`fs/cbase32`) — the canonical CAS hash format,
|
|
83
|
+
* **Hashes** travel as cBase32 (`fs/basen/cbase32`) — the canonical CAS hash format,
|
|
65
84
|
* shared with the CLI and the on-disk store layout. **Content** encoding is
|
|
66
85
|
* determined at read time as described above.
|
|
67
86
|
*
|
|
@@ -80,24 +99,25 @@
|
|
|
80
99
|
*
|
|
81
100
|
* @module
|
|
82
101
|
*/
|
|
83
|
-
import { string, option, or, boolean } from
|
|
84
|
-
import { stringify } from
|
|
85
|
-
import { pure, decode } from
|
|
86
|
-
import { create } from
|
|
87
|
-
import { cBase32ToVec, vecToCBase32 } from
|
|
88
|
-
import { decode as base64Decode, encode as base64Encode } from
|
|
89
|
-
import { tryUtf8 } from
|
|
90
|
-
import { detectStream } from
|
|
91
|
-
import {
|
|
92
|
-
import {
|
|
93
|
-
import {} from
|
|
94
|
-
import {
|
|
95
|
-
import {
|
|
96
|
-
import {
|
|
97
|
-
import {
|
|
98
|
-
import {
|
|
99
|
-
import {
|
|
100
|
-
import {
|
|
102
|
+
import { string, option, or, boolean } from '../../types/rtti/module.f.js';
|
|
103
|
+
import { stringify } from '../../media/json/module.f.js';
|
|
104
|
+
import { pure, decode } from '../../effects/module.f.js';
|
|
105
|
+
import { create } from '../../effects/memory/module.f.js';
|
|
106
|
+
import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.js';
|
|
107
|
+
import { decode as base64Decode, encode as base64Encode } from '../../basen/base64/module.f.js';
|
|
108
|
+
import { tryUtf8 } from '../../text/module.f.js';
|
|
109
|
+
import { detectStream } from '../../media/type/module.f.js';
|
|
110
|
+
import { detect as detectDialect } from '../../media/module.f.js';
|
|
111
|
+
import { empty, length as bitVecLength, maxLength, maxLengthBytes, msb, vec } from '../../types/bit_vec/module.f.js';
|
|
112
|
+
import { ok, error } from '../../types/result/module.f.js';
|
|
113
|
+
import {} from '../../effects/node/module.f.js';
|
|
114
|
+
import { stdioTransport } from '../../mcp/stdio/module.f.js';
|
|
115
|
+
import { mcpStep, uninitializedState, toolEntry, fromRegistry, errorResult, } from '../../mcp/module.f.js';
|
|
116
|
+
import { fileCas } from '../module.f.js';
|
|
117
|
+
import { fromVec } from '../../text/utf8/module.f.js';
|
|
118
|
+
import { identity } from '../../types/function/module.f.js';
|
|
119
|
+
import { sha256 } from '../../crypto/sha2/module.f.js';
|
|
120
|
+
import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.js';
|
|
101
121
|
// ── Argument schemas (declared once, used for both inputSchema and validate) ─────
|
|
102
122
|
/** Arguments for `cas_add`: content to store, with optional encoding type. */
|
|
103
123
|
export const casAddArgs = {
|
|
@@ -116,7 +136,7 @@ export const casListArgs = {};
|
|
|
116
136
|
* Drains a CAS read stream into a single `Vec`. Used only on the `content: true`
|
|
117
137
|
* path, where the whole blob is needed for inline base64 / UTF-8 transfer; the
|
|
118
138
|
* chunk stream is concatenated and an error item is surfaced as the result.
|
|
119
|
-
* Metadata-only `cas_get` avoids this entirely via `fs/
|
|
139
|
+
* Metadata-only `cas_get` avoids this entirely via `fs/media/type` `detectStream`,
|
|
120
140
|
* which is why it is not bound by the `maxLength` ceiling below.
|
|
121
141
|
*/
|
|
122
142
|
const collectRead = (stream) => {
|
|
@@ -157,45 +177,65 @@ const casToolRegistry = (home) => {
|
|
|
157
177
|
? errorResult('write')
|
|
158
178
|
: okResult(vecToCBase32(hash))));
|
|
159
179
|
}),
|
|
160
|
-
toolEntry('cas_get', 'Inspect a blob by hash. Always returns JSON {length,
|
|
180
|
+
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 => {
|
|
161
181
|
const key = cBase32ToVec(r.hash);
|
|
162
182
|
if (key === null) {
|
|
163
183
|
return pure(errorResult(`invalid cBase32 hash: ${r.hash}`));
|
|
164
184
|
}
|
|
165
|
-
const
|
|
185
|
+
const uri = c.url(key);
|
|
166
186
|
return detectStream(c.read(key)).step(([tag, detected]) => {
|
|
167
187
|
if (tag === 'error') {
|
|
168
188
|
return pure(errorResult(`no such hash: ${r.hash}`));
|
|
169
189
|
}
|
|
170
|
-
const { length, mime_type, type } = detected;
|
|
171
|
-
const meta = { length: Number(length),
|
|
190
|
+
const { length, mime_type: mimeType, type } = detected;
|
|
191
|
+
const meta = { length: Number(length), mimeType, type, uri };
|
|
172
192
|
if (r.content !== true) {
|
|
173
|
-
//
|
|
174
|
-
|
|
193
|
+
// A dialect match can only be decided from the whole parsed blob;
|
|
194
|
+
// only attempt the extra bounded read when it stands a chance
|
|
195
|
+
// (whole-blob text within the same cap `content: true` allows).
|
|
196
|
+
if (type !== 'text' || length > maxLengthBytes) {
|
|
197
|
+
return pure(okResult(toJson(meta)));
|
|
198
|
+
}
|
|
199
|
+
return collectRead(c.read(key)).step(([collectTag, value]) => {
|
|
200
|
+
// Already known to fit from the streaming pass above, so an
|
|
201
|
+
// error here means the hash vanished between reads; fall back
|
|
202
|
+
// to the streaming verdict rather than fail the whole request.
|
|
203
|
+
if (collectTag === 'error') {
|
|
204
|
+
return pure(okResult(toJson(meta)));
|
|
205
|
+
}
|
|
206
|
+
const refined = detectDialect(value);
|
|
207
|
+
return pure(okResult(toJson({ ...meta, mimeType: refined.mime_type })));
|
|
208
|
+
});
|
|
175
209
|
}
|
|
176
210
|
// A single `Vec` caps at `maxLength` bits (`maxLengthBytes` bytes), so
|
|
177
211
|
// a larger blob cannot be buffered for inline transfer. Report the
|
|
178
212
|
// size and point at the size-independent alternatives instead of
|
|
179
213
|
// misreporting an existing blob as `no such hash`.
|
|
180
214
|
if (length > maxLengthBytes) {
|
|
181
|
-
return pure(errorResult(`blob too large to fetch inline (${length} bytes, limit ${maxLengthBytes} bytes); use the
|
|
215
|
+
return pure(errorResult(`blob too large to fetch inline (${length} bytes, limit ${maxLengthBytes} bytes); use the uri field (${uri}) or omit content for metadata`));
|
|
182
216
|
}
|
|
183
217
|
return collectRead(c.read(key)).step(([collectTag, value]) => {
|
|
184
218
|
if (collectTag === 'error') {
|
|
185
219
|
return pure(errorResult(`no such hash: ${r.hash}`));
|
|
186
220
|
}
|
|
187
|
-
|
|
221
|
+
// Re-derive the verdict from the now-materialized blob so a
|
|
222
|
+
// dialect match — only decidable with the whole parsed JSON in
|
|
223
|
+
// hand — is reflected in the inline result too, not just the
|
|
224
|
+
// streaming guess.
|
|
225
|
+
const refined = detectDialect(value);
|
|
226
|
+
const refinedMeta = { length: Number(refined.length), mimeType: refined.mime_type, type: refined.type, uri };
|
|
227
|
+
if (refined.type === 'text') {
|
|
188
228
|
// `type: 'text'` means the detector validated `value` as UTF-8,
|
|
189
229
|
// so `fromVec` is non-null here; guard defensively regardless.
|
|
190
230
|
const str = fromVec(value);
|
|
191
231
|
return pure(str === null
|
|
192
232
|
? errorResult(`content is not byte-aligned: ${r.hash}`)
|
|
193
|
-
: okResult(toJson({ ...
|
|
233
|
+
: okResult(toJson({ ...refinedMeta, text: str })));
|
|
194
234
|
}
|
|
195
235
|
const blob = base64Encode(value);
|
|
196
236
|
return pure(blob === null
|
|
197
237
|
? errorResult(`content is not byte-aligned: ${r.hash}`)
|
|
198
|
-
: okResult(toJson({ ...
|
|
238
|
+
: okResult(toJson({ ...refinedMeta, blob })));
|
|
199
239
|
});
|
|
200
240
|
});
|
|
201
241
|
}),
|
package/fs/cas/mcp/proof.f.d.ts
CHANGED
|
@@ -16,13 +16,15 @@ export declare const proof: {
|
|
|
16
16
|
addGetRoundTrips: () => void;
|
|
17
17
|
getUntypedReturnsText: () => void;
|
|
18
18
|
getTypedReturnsBinaryJson: () => void;
|
|
19
|
+
getMetaRecognizesRevisionDialect: () => void;
|
|
20
|
+
getContentRecognizesRevisionDialect: () => void;
|
|
19
21
|
listEnumeratesStoredHashes: () => void;
|
|
20
22
|
addInvalidContentIsError: () => void;
|
|
21
23
|
addBadLengthContentIsError: () => void;
|
|
22
24
|
addBase64OverLimitIsError: () => void;
|
|
23
25
|
addTextOverLimitIsError: () => void;
|
|
24
26
|
addTextAtLimitSucceeds: () => void;
|
|
25
|
-
|
|
27
|
+
addBase64AtLimitSucceeds: () => void;
|
|
26
28
|
getUnterminatedHashIsError: () => void;
|
|
27
29
|
addMissingContentIsError: () => void;
|
|
28
30
|
getMissingHashArgumentIsError: () => void;
|
package/fs/cas/mcp/proof.f.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
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';
|
|
18
19
|
const initialTestState = { memory: { next: 0, values: {} } };
|
|
19
20
|
const mock = {
|
|
20
21
|
memCreate: value => state => {
|
|
@@ -104,10 +105,13 @@ const item0 = (resp) => resultOf(resp).content[0];
|
|
|
104
105
|
const textOf = (resp) => item0(resp).text;
|
|
105
106
|
// A plain text sample for text add→get round-trips.
|
|
106
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":[]}`;
|
|
107
111
|
// A base64-encoded binary payload for binary add→get round-trips.
|
|
108
112
|
const binarySample = base64Encode(vec8(0x2an));
|
|
109
113
|
// A base64 blob whose leading bytes are the PNG magic-byte signature, so
|
|
110
|
-
// `cas_get` detects its type and returns base64 with
|
|
114
|
+
// `cas_get` detects its type and returns base64 with mimeType image/png.
|
|
111
115
|
const pngSample = base64Encode(u8ListToVec(msb)([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x01]));
|
|
112
116
|
// Returns the RFC 4648 base64 encoding of `n` zero bytes, computed directly
|
|
113
117
|
// without bigint arithmetic — independent of `base64.encode` so these tests
|
|
@@ -141,9 +145,10 @@ const largeMultiChunkBlobMeta = (chunk0, chunk1, expectedType, expectedMime) =>
|
|
|
141
145
|
assert(!resultOf(metaResp).isError);
|
|
142
146
|
const meta = JSON.parse(textOf(metaResp));
|
|
143
147
|
assertEq(meta.type, expectedType);
|
|
144
|
-
assertEq(meta.
|
|
148
|
+
assertEq(meta.mimeType, expectedMime);
|
|
145
149
|
assertEq(meta.length, Number((length(chunk0) + length(chunk1)) / 8n));
|
|
146
|
-
assertEq(meta.
|
|
150
|
+
assertEq(meta.text, undefined);
|
|
151
|
+
assertEq(meta.blob, undefined);
|
|
147
152
|
};
|
|
148
153
|
// A full `maxLengthBytes`-long chunk of repeated ASCII 'a' — valid UTF-8.
|
|
149
154
|
const asciiChunk = repeat(maxLengthBytes)(vec8(0x61n));
|
|
@@ -207,7 +212,7 @@ export const proof = {
|
|
|
207
212
|
// This test originally timed out under `bun test`'s native 5s per-test
|
|
208
213
|
// limit (12-14s observed in CI on PR #1201) — the cost was in
|
|
209
214
|
// `base64Encode`, quadratic before the `baseN.vecToString` fix (see
|
|
210
|
-
// `fs/base64/proof.f.ts` `encodeLargeVecIsSlow`). Now well under budget on
|
|
215
|
+
// `fs/basen/base64/proof.f.ts` `encodeLargeVecIsSlow`). Now well under budget on
|
|
211
216
|
// both engines.
|
|
212
217
|
getContentBase64InflationOverflowWritesInternalError: () => {
|
|
213
218
|
const [root, hash] = seedBlob({})([oversizedBase64Chunk]);
|
|
@@ -271,10 +276,10 @@ export const proof = {
|
|
|
271
276
|
const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash }));
|
|
272
277
|
assert(!resultOf(getResp).isError);
|
|
273
278
|
const result = JSON.parse(textOf(getResp));
|
|
274
|
-
assertEq(result.
|
|
279
|
+
assertEq(result.mimeType, 'text/plain');
|
|
275
280
|
assertEq(result.type, 'text');
|
|
276
281
|
assertEq(result.length, textSample.length);
|
|
277
|
-
assertEq(result.
|
|
282
|
+
assertEq(result.text, undefined);
|
|
278
283
|
},
|
|
279
284
|
// cas_get with content:true returns inline content.
|
|
280
285
|
textAddGetContentRoundTrips: () => {
|
|
@@ -284,8 +289,8 @@ export const proof = {
|
|
|
284
289
|
assert(!resultOf(getResp).isError);
|
|
285
290
|
const result = JSON.parse(textOf(getResp));
|
|
286
291
|
assertEq(result.type, 'text');
|
|
287
|
-
assertEq(result.
|
|
288
|
-
assertEq(result.
|
|
292
|
+
assertEq(result.mimeType, 'text/plain');
|
|
293
|
+
assertEq(result.text, textSample);
|
|
289
294
|
},
|
|
290
295
|
// Binary add→get round-trip: store as base64, retrieve metadata without content.
|
|
291
296
|
binaryAddGetMetaRoundTrips: () => {
|
|
@@ -294,9 +299,9 @@ export const proof = {
|
|
|
294
299
|
const [, getResp] = session(call(2, 'cas_add', { content: binarySample, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
295
300
|
assert(!resultOf(getResp).isError);
|
|
296
301
|
const result = JSON.parse(textOf(getResp));
|
|
297
|
-
assertEq(result.
|
|
302
|
+
assertEq(result.text, undefined);
|
|
298
303
|
assertEq(result.type, 'text');
|
|
299
|
-
assertEq(result.
|
|
304
|
+
assertEq(result.mimeType, 'text/plain');
|
|
300
305
|
},
|
|
301
306
|
// Binary add→get with content:true returns inline base64 content.
|
|
302
307
|
binaryAddGetContentRoundTrips: () => {
|
|
@@ -306,7 +311,7 @@ export const proof = {
|
|
|
306
311
|
assert(!resultOf(getResp).isError);
|
|
307
312
|
const result = JSON.parse(textOf(getResp));
|
|
308
313
|
assertEq(result.type, 'text');
|
|
309
|
-
assertEq(result.
|
|
314
|
+
assertEq(result.text, '*');
|
|
310
315
|
},
|
|
311
316
|
addGetRoundTrips: () => {
|
|
312
317
|
const [addResp] = session(call(2, 'cas_add', { content: textSample }));
|
|
@@ -314,7 +319,7 @@ export const proof = {
|
|
|
314
319
|
const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash, content: true }));
|
|
315
320
|
assert(!resultOf(getResp).isError);
|
|
316
321
|
const result = JSON.parse(textOf(getResp));
|
|
317
|
-
assertEq(result.
|
|
322
|
+
assertEq(result.text, textSample);
|
|
318
323
|
},
|
|
319
324
|
// Text content (no magic-byte match, valid UTF-8) comes back with type:'text' when content requested.
|
|
320
325
|
getUntypedReturnsText: () => {
|
|
@@ -324,9 +329,9 @@ export const proof = {
|
|
|
324
329
|
assertEq(item0(getResp) === null ? null : item0(getResp).type, 'text');
|
|
325
330
|
const result = JSON.parse(textOf(getResp));
|
|
326
331
|
assertEq(result.type, 'text');
|
|
327
|
-
assertEq(result.
|
|
332
|
+
assertEq(result.mimeType, 'text/plain');
|
|
328
333
|
},
|
|
329
|
-
// Bytes with a recognised PNG signature come back as base64 with
|
|
334
|
+
// Bytes with a recognised PNG signature come back as base64 with mimeType image/png when content requested.
|
|
330
335
|
getTypedReturnsBinaryJson: () => {
|
|
331
336
|
const [addResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }));
|
|
332
337
|
const hash = textOf(addResp);
|
|
@@ -335,8 +340,33 @@ export const proof = {
|
|
|
335
340
|
assertEq(item0(getResp).type, 'text');
|
|
336
341
|
const result = JSON.parse(textOf(getResp));
|
|
337
342
|
assertEq(result.type, 'base64');
|
|
338
|
-
assertEq(result.
|
|
339
|
-
assertEq(result.
|
|
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);
|
|
340
370
|
},
|
|
341
371
|
listEnumeratesStoredHashes: () => {
|
|
342
372
|
const [addResp, listResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_list', {}));
|
|
@@ -375,16 +405,14 @@ export const proof = {
|
|
|
375
405
|
assert(!resultOf(resp).isError);
|
|
376
406
|
assert(textOf(resp).length > 0);
|
|
377
407
|
},
|
|
378
|
-
// base64 of exactly maxLengthBytes —
|
|
379
|
-
//
|
|
380
|
-
// maxLength
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
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: () => {
|
|
384
413
|
const [resp] = session(call(2, 'cas_add', { content: base64OfA(maxLengthBytes), type: 'base64' }));
|
|
385
|
-
|
|
386
|
-
assert(textOf(resp).
|
|
387
|
-
assert(textOf(resp).includes('cas add'));
|
|
414
|
+
assert(!resultOf(resp).isError);
|
|
415
|
+
assert(textOf(resp).length > 0);
|
|
388
416
|
},
|
|
389
417
|
getUnterminatedHashIsError: () => {
|
|
390
418
|
const [resp] = session(call(2, 'cas_get', { hash: '0' }));
|
|
@@ -423,10 +451,10 @@ export const proof = {
|
|
|
423
451
|
const [, metaResp] = session(call(2, 'cas_add', { content: 'text content' }), call(3, 'cas_get', { hash }));
|
|
424
452
|
assert(!resultOf(metaResp).isError);
|
|
425
453
|
const meta = JSON.parse(textOf(metaResp));
|
|
426
|
-
assertEq(meta.
|
|
454
|
+
assertEq(meta.mimeType, 'text/plain');
|
|
427
455
|
assertEq(meta.type, 'text');
|
|
428
456
|
assertEq(meta.length, 12);
|
|
429
|
-
assertEq(meta.
|
|
457
|
+
assertEq(meta.text, undefined);
|
|
430
458
|
},
|
|
431
459
|
getMetaBinaryBlob: () => {
|
|
432
460
|
const [addResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }));
|
|
@@ -434,10 +462,10 @@ export const proof = {
|
|
|
434
462
|
const [, metaResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
435
463
|
assert(!resultOf(metaResp).isError);
|
|
436
464
|
const meta = JSON.parse(textOf(metaResp));
|
|
437
|
-
assertEq(meta.
|
|
465
|
+
assertEq(meta.mimeType, 'image/png');
|
|
438
466
|
assertEq(meta.type, 'base64');
|
|
439
467
|
assertEq(meta.length, 10);
|
|
440
|
-
assertEq(meta.
|
|
468
|
+
assertEq(meta.blob, undefined);
|
|
441
469
|
},
|
|
442
470
|
getMetaOctetStreamForUnknownBinary: () => {
|
|
443
471
|
const binaryContent = u8ListToVec(msb)([0xFF, 0xFE, 0x00, 0x01]);
|
|
@@ -447,9 +475,9 @@ export const proof = {
|
|
|
447
475
|
const [, metaResp] = session(call(2, 'cas_add', { content: binaryB64, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
448
476
|
assert(!resultOf(metaResp).isError);
|
|
449
477
|
const meta = JSON.parse(textOf(metaResp));
|
|
450
|
-
assertEq(meta.
|
|
478
|
+
assertEq(meta.mimeType, 'application/octet-stream');
|
|
451
479
|
assertEq(meta.type, 'base64');
|
|
452
|
-
assertEq(meta.
|
|
480
|
+
assertEq(meta.blob, undefined);
|
|
453
481
|
},
|
|
454
482
|
// A NUL-bearing blob is valid UTF-8 yet binary: cas_get must report
|
|
455
483
|
// base64/octet-stream, not text/plain.
|
|
@@ -461,7 +489,7 @@ export const proof = {
|
|
|
461
489
|
const [, metaResp] = session(call(2, 'cas_add', { content: nulB64, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
462
490
|
assert(!resultOf(metaResp).isError);
|
|
463
491
|
const meta = JSON.parse(textOf(metaResp));
|
|
464
|
-
assertEq(meta.
|
|
492
|
+
assertEq(meta.mimeType, 'application/octet-stream');
|
|
465
493
|
assertEq(meta.type, 'base64');
|
|
466
494
|
},
|
|
467
495
|
getMetaMissingHashIsError: () => {
|
|
@@ -482,8 +510,8 @@ export const proof = {
|
|
|
482
510
|
const [, getResp] = session(call(2, 'cas_add', { content: binaryB64, type: 'base64' }), call(3, 'cas_get', { hash, content: true }));
|
|
483
511
|
assert(!resultOf(getResp).isError);
|
|
484
512
|
const result = JSON.parse(textOf(getResp));
|
|
485
|
-
assertEq(result.
|
|
513
|
+
assertEq(result.mimeType, 'application/octet-stream');
|
|
486
514
|
assertEq(result.type, 'base64');
|
|
487
|
-
assertEq(result.
|
|
515
|
+
assertEq(result.blob, binaryB64);
|
|
488
516
|
},
|
|
489
517
|
};
|
package/fs/cas/module.f.js
CHANGED
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { sha256 } from
|
|
7
|
-
import { join, normalize, parse } from
|
|
8
|
-
import { empty, length, maxLengthBytes, msb, vec } from
|
|
9
|
-
import { cBase32ToVec, vecToCBase32 } from
|
|
10
|
-
import { foldStep, forEachStep, okStep, pure } from
|
|
11
|
-
import { access, createExclusive, isNotFound, mkdir, now, randomInt, readBytes, readdir, rename, rm, stat, writeBytes, } from
|
|
12
|
-
import { toOption } from
|
|
13
|
-
import { error, ok, unwrap } from
|
|
14
|
-
import { splitAt } from
|
|
15
|
-
import { nonEmpty, empty as elEmpty } from
|
|
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. */
|
package/fs/cas/proof.f.d.ts
CHANGED
|
@@ -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:
|
|
7
|
-
addAndGetBigFile:
|
|
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
|
|
2
|
-
import { cBase32ToVec, vecToCBase32 } from
|
|
3
|
-
import { computeSync, sha256 } from
|
|
4
|
-
import { fileCas, casAddFile, casUpload } from
|
|
5
|
-
import { pure } from
|
|
6
|
-
import { mkdir, writeFile, rm, readFile, access } from
|
|
7
|
-
import { error, ok } from
|
|
8
|
-
import { emptyState, virtual } from
|
|
9
|
-
import { join } from
|
|
10
|
-
import { nonEmpty, empty } from
|
|
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
|
package/fs/ci/bun/module.f.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
|
-
import { bun } from
|
|
8
|
-
import { clean, install, test, uses } from
|
|
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 [
|
|
10
|
+
export declare const os: readonly ['ubuntu', 'macos', 'windows'];
|
|
11
11
|
export type Os = typeof os[number];
|
|
12
|
-
export declare const architecture: readonly [
|
|
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: {
|
package/fs/ci/common/module.f.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
|
-
import { actions, images } from
|
|
9
|
-
import { option, array, record, string } from
|
|
10
|
-
import {} from
|
|
11
|
-
import { parse as rttiParse } from
|
|
8
|
+
import { actions, images } from '../config/module.f.js';
|
|
9
|
+
import { option, array, record, string } from '../../types/rtti/module.f.js';
|
|
10
|
+
import {} from '../../types/rtti/ts/module.f.js';
|
|
11
|
+
import { parse as rttiParse } from '../../types/rtti/parse/module.f.js';
|
|
12
12
|
export const os = ['ubuntu', 'macos', 'windows'];
|
|
13
13
|
export const architecture = ['intel', 'arm'];
|
|
14
14
|
export const stepSchema = {
|