functionalscript 0.30.0 → 0.31.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 +18 -22
- package/fs/asn.1/proof.f.d.ts +5 -0
- package/fs/asn.1/proof.f.js +38 -2
- package/fs/base64/module.f.d.ts +9 -0
- package/fs/base64/module.f.js +60 -0
- package/fs/base64/proof.f.d.ts +11 -0
- package/fs/base64/proof.f.js +117 -0
- package/fs/base_n/module.f.d.ts +44 -0
- package/fs/base_n/module.f.js +54 -0
- package/fs/base_n/proof.f.d.ts +10 -0
- package/fs/base_n/proof.f.js +86 -0
- package/fs/bnf/data/module.f.d.ts +4 -9
- package/fs/bnf/data/module.f.js +4 -3
- package/fs/bnf/module.f.d.ts +3 -4
- package/fs/bnf/module.f.js +3 -2
- package/fs/bnf/proof.f.d.ts +9 -0
- package/fs/bnf/proof.f.js +98 -1
- package/fs/cas/mcp/module.f.d.ts +40 -0
- package/fs/cas/mcp/module.f.js +223 -0
- package/fs/cas/mcp/proof.f.d.ts +30 -0
- package/fs/cas/mcp/proof.f.js +310 -0
- package/fs/cas/module.f.d.ts +9 -3
- package/fs/cas/module.f.js +104 -69
- package/fs/cas/proof.f.d.ts +2 -0
- package/fs/cas/proof.f.js +30 -1
- package/fs/cbase32/module.f.d.ts +1 -1
- package/fs/cbase32/module.f.js +19 -40
- package/fs/cbase32/proof.f.d.ts +1 -0
- package/fs/cbase32/proof.f.js +15 -0
- package/fs/ci/config/module.f.d.ts +6 -6
- package/fs/ci/config/module.f.js +6 -6
- package/fs/ci/deno/module.f.js +2 -2
- package/fs/ci/module.f.d.ts +5 -0
- package/fs/ci/module.f.js +2 -3
- package/fs/ci/proof.f.d.ts +1 -0
- package/fs/ci/proof.f.js +9 -3
- package/fs/cli/module.f.d.ts +3 -1
- package/fs/cli/module.f.js +22 -12
- package/fs/cli/proof.f.d.ts +5 -0
- package/fs/cli/proof.f.js +76 -0
- package/fs/crypto/hmac/proof.f.d.ts +1 -0
- package/fs/crypto/hmac/proof.f.js +8 -0
- package/fs/crypto/secp/proof.f.d.ts +1 -0
- package/fs/crypto/secp/proof.f.js +10 -0
- package/fs/crypto/sign/module.f.js +1 -4
- package/fs/dev/module.f.d.ts +5 -3
- package/fs/dev/module.f.js +13 -2
- package/fs/dev/package_json/proof.f.d.ts +1 -0
- package/fs/dev/package_json/proof.f.js +5 -0
- package/fs/dev/proof.f.d.ts +1 -0
- package/fs/dev/proof.f.js +12 -1
- package/fs/djs/ast/module.f.d.ts +1 -1
- package/fs/djs/ast/proof.f.d.ts +5 -0
- package/fs/djs/ast/proof.f.js +17 -0
- package/fs/djs/module.f.d.ts +1 -1
- package/fs/djs/module.f.js +2 -3
- package/fs/djs/serializer/module.f.js +24 -11
- package/fs/djs/transpiler/module.f.js +3 -4
- package/fs/effects/memory/module.f.d.ts +37 -0
- package/fs/effects/memory/module.f.js +24 -0
- package/fs/effects/memory/proof.f.d.ts +6 -0
- package/fs/effects/memory/proof.f.js +64 -0
- package/fs/effects/mock/module.f.d.ts +7 -2
- package/fs/effects/mock/module.f.js +12 -8
- package/fs/effects/module.d.ts +1 -1
- package/fs/effects/module.f.d.ts +41 -0
- package/fs/effects/module.f.js +25 -0
- package/fs/effects/module.js +6 -7
- package/fs/effects/node/memory/module.d.ts +20 -0
- package/fs/effects/node/memory/module.js +44 -0
- package/fs/effects/node/memory/proof.d.ts +10 -0
- package/fs/effects/node/memory/proof.js +35 -0
- package/fs/effects/node/module.d.ts +1 -1
- package/fs/effects/node/module.f.d.ts +54 -8
- package/fs/effects/node/module.f.js +55 -4
- package/fs/effects/node/module.js +166 -135
- package/fs/effects/node/proof.f.d.ts +10 -0
- package/fs/effects/node/proof.f.js +88 -21
- package/fs/effects/node/virtual/module.f.d.ts +6 -0
- package/fs/effects/node/virtual/module.f.js +48 -16
- package/fs/effects/node/virtual/proof.f.d.ts +17 -0
- package/fs/effects/node/virtual/proof.f.js +79 -0
- package/fs/effects/proof.f.d.ts +5 -0
- package/fs/effects/proof.f.js +54 -34
- package/fs/emergent_testing/module.f.d.ts +6 -1
- package/fs/emergent_testing/module.f.js +8 -3
- package/fs/emergent_testing/proof.f.d.ts +11 -0
- package/fs/emergent_testing/proof.f.js +52 -16
- package/fs/fjs/module.f.js +2 -2
- package/fs/fjs/proof.f.d.ts +8 -0
- package/fs/fjs/proof.f.js +39 -0
- package/fs/fsc/module.f.d.ts +5 -0
- package/fs/fsc/module.f.js +17 -27
- package/fs/fsc/proof.f.d.ts +8 -0
- package/fs/fsc/proof.f.js +48 -2
- package/fs/fsm/module.f.d.ts +2 -3
- package/fs/fsm/module.f.js +1 -1
- package/fs/html/module.f.d.ts +2 -3
- package/fs/html/module.f.js +7 -6
- package/fs/html/proof.f.d.ts +1 -0
- package/fs/html/proof.f.js +7 -0
- package/fs/js/tokenizer/module.f.js +1 -1
- package/fs/js/tokenizer/proof.f.js +7 -0
- package/fs/json/module.f.d.ts +9 -2
- package/fs/json/module.f.js +9 -7
- package/fs/json/parser/module.f.js +4 -6
- package/fs/json/parser/proof.f.js +19 -10
- package/fs/json/proof.f.d.ts +2 -1
- package/fs/json/proof.f.js +33 -6
- package/fs/json/rpc/module.f.d.ts +5 -0
- package/fs/json/rpc/module.f.js +16 -11
- package/fs/json/schema/module.f.d.ts +15 -14
- package/fs/json/schema/proof.f.d.ts +3 -0
- package/fs/json/schema/proof.f.js +16 -1
- package/fs/mcp/module.f.d.ts +103 -5
- package/fs/mcp/module.f.js +139 -2
- package/fs/mcp/proof.f.d.ts +34 -0
- package/fs/mcp/proof.f.js +207 -0
- package/fs/mcp/stdio/module.f.d.ts +39 -0
- package/fs/mcp/stdio/module.f.js +54 -0
- package/fs/mcp/stdio/proof.f.d.ts +10 -0
- package/fs/mcp/stdio/proof.f.js +92 -0
- package/fs/mime/module.f.d.ts +39 -0
- package/fs/mime/module.f.js +78 -0
- package/fs/mime/proof.f.d.ts +15 -0
- package/fs/mime/proof.f.js +30 -0
- package/fs/path/module.f.d.ts +8 -0
- package/fs/path/module.f.js +10 -3
- package/fs/path/proof.f.d.ts +1 -0
- package/fs/path/proof.f.js +40 -2
- package/fs/text/sgr/proof.f.js +30 -2
- package/fs/text/utf8/module.f.d.ts +7 -0
- package/fs/text/utf8/module.f.js +85 -31
- package/fs/text/utf8/proof.f.d.ts +1 -0
- package/fs/text/utf8/proof.f.js +60 -1
- package/fs/types/bigfloat/module.f.d.ts +3 -0
- package/fs/types/bigfloat/module.f.js +30 -31
- package/fs/types/bigint/module.f.d.ts +24 -2
- package/fs/types/bigint/module.f.js +30 -2
- package/fs/types/bigint/proof.f.d.ts +7 -0
- package/fs/types/bigint/proof.f.js +47 -16
- package/fs/types/bit_vec/module.f.d.ts +18 -0
- package/fs/types/bit_vec/module.f.js +18 -0
- package/fs/types/bit_vec/proof.f.d.ts +1 -0
- package/fs/types/bit_vec/proof.f.js +13 -1
- package/fs/types/function/operator/proof.f.d.ts +2 -0
- package/fs/types/function/operator/proof.f.js +20 -2
- package/fs/types/list/module.f.js +1 -4
- package/fs/types/list/proof.f.d.ts +1 -1
- package/fs/types/list/proof.f.js +14 -6
- package/fs/types/map/proof.f.d.ts +1 -0
- package/fs/types/map/proof.f.js +10 -1
- package/fs/types/nibble_set/module.f.d.ts +13 -0
- package/fs/types/nibble_set/module.f.js +16 -0
- package/fs/types/object/module.f.d.ts +12 -8
- package/fs/types/object/module.f.js +9 -2
- package/fs/types/object/proof.f.d.ts +14 -0
- package/fs/types/object/proof.f.js +4 -0
- package/fs/types/ordered_map/proof.f.d.ts +1 -0
- package/fs/types/ordered_map/proof.f.js +39 -79
- package/fs/types/phantom/module.f.d.ts +22 -0
- package/fs/types/phantom/module.f.js +11 -0
- package/fs/types/prime_field/proof.f.d.ts +7 -0
- package/fs/types/prime_field/proof.f.js +63 -122
- package/fs/types/rtti/common/module.f.d.ts +4 -4
- package/fs/types/rtti/common/module.f.js +1 -1
- package/fs/types/rtti/module.f.d.ts +2 -3
- package/fs/types/rtti/parse/module.f.js +0 -1
- package/fs/types/rtti/ts/module.f.d.ts +26 -32
- package/fs/types/rtti/ts/module.f.js +2 -2
- package/fs/types/rtti/ts/proof.f.js +3 -3
- package/fs/types/rtti/validate/module.f.js +0 -1
- package/fs/types/sorted_list/module.f.d.ts +6 -0
- package/fs/types/sorted_list/module.f.js +11 -0
- package/fs/types/sorted_set/module.f.js +2 -8
- package/fs/types/ts/module.f.d.ts +1 -1
- package/fs/types/ts/module.f.js +3 -1
- package/fs/types/ts/proof.f.js +3 -2
- package/fs/website/proof.f.d.ts +1 -0
- package/fs/website/proof.f.js +8 -1
- package/package.json +3 -3
|
@@ -0,0 +1,310 @@
|
|
|
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, read, write } from "../../effects/memory/module.f.js";
|
|
5
|
+
import { msb, u8ListToVec, vec8 } from "../../types/bit_vec/module.f.js";
|
|
6
|
+
import { vecToCBase32 } from "../../cbase32/module.f.js";
|
|
7
|
+
import { encode as base64Encode } from "../../base64/module.f.js";
|
|
8
|
+
import { sha256 } from "../../crypto/sha2/module.f.js";
|
|
9
|
+
import { utf8 } from "../../text/module.f.js";
|
|
10
|
+
import { cas } from "../module.f.js";
|
|
11
|
+
import { mcpStep, uninitializedState, } from "../../mcp/module.f.js";
|
|
12
|
+
import {} from "../../effects/node/module.f.js";
|
|
13
|
+
import { casConfig, casMcpHandlers } from "./module.f.js";
|
|
14
|
+
const initialTestState = { memory: { next: 0, values: {} }, files: {} };
|
|
15
|
+
const mock = {
|
|
16
|
+
memCreate: value => state => {
|
|
17
|
+
const id = `k${state.memory.next}`;
|
|
18
|
+
const key = asNominal(id);
|
|
19
|
+
return [
|
|
20
|
+
{ ...state, memory: { next: state.memory.next + 1, values: { ...state.memory.values, [id]: value } } },
|
|
21
|
+
key,
|
|
22
|
+
];
|
|
23
|
+
},
|
|
24
|
+
memRead: key => state => [state, state.memory.values[asBase(key)]],
|
|
25
|
+
memWrite: (key, value) => state => {
|
|
26
|
+
const id = asBase(key);
|
|
27
|
+
return [{ ...state, memory: { ...state.memory, values: { ...state.memory.values, [id]: value } } }, undefined];
|
|
28
|
+
},
|
|
29
|
+
readFile: path => state => {
|
|
30
|
+
const v = state.files[path];
|
|
31
|
+
return v !== undefined
|
|
32
|
+
? [state, ['ok', v]]
|
|
33
|
+
: [state, ['error', new Error(`ENOENT: ${path}`)]];
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const runMem = (effect) => run(mock)(initialTestState)(effect)[1];
|
|
37
|
+
const runMemWithFiles = (files) => (effect) => run(mock)({ memory: { next: 0, values: {} }, files })(effect)[1];
|
|
38
|
+
const memKvStore = (mapKey) => ({
|
|
39
|
+
read: (key) => read(mapKey).step(m => pure(m[vecToCBase32(key)]?.[1])),
|
|
40
|
+
write: (key, value) => read(mapKey).step(m => write(mapKey, { ...m, [vecToCBase32(key)]: [key, value] })),
|
|
41
|
+
list: () => read(mapKey).step(m => pure(Object.values(m).map(([k]) => k))),
|
|
42
|
+
});
|
|
43
|
+
// ── Session driver ──────────────────────────────────────────────────────────────
|
|
44
|
+
// Feeds each message to `step` in order, collecting every response.
|
|
45
|
+
const feed = (step) => (msgs) => {
|
|
46
|
+
const go = (i, acc) => i === msgs.length
|
|
47
|
+
? pure(acc)
|
|
48
|
+
: step(msgs[i]).step(r => go(i + 1, [...acc, r]));
|
|
49
|
+
return go(0, []);
|
|
50
|
+
};
|
|
51
|
+
// Runs a full session over a fresh in-memory CAS, returning all responses.
|
|
52
|
+
const runSession = (msgs) => runMem(create({}).step(mapKey => create(uninitializedState).step(sessionKey => {
|
|
53
|
+
const c = cas(sha256)(memKvStore(mapKey));
|
|
54
|
+
const step = mcpStep(casConfig)(casMcpHandlers(c))(sessionKey);
|
|
55
|
+
return feed(step)(msgs);
|
|
56
|
+
})));
|
|
57
|
+
// Runs a session with a mocked filesystem (for cas_add with type:'url' tests).
|
|
58
|
+
const runSessionWithFiles = (files) => (msgs) => runMemWithFiles(files)(create({}).step(mapKey => create(uninitializedState).step(sessionKey => {
|
|
59
|
+
const c = cas(sha256)(memKvStore(mapKey));
|
|
60
|
+
const step = mcpStep(casConfig)(casMcpHandlers(c))(sessionKey);
|
|
61
|
+
return feed(step)(msgs);
|
|
62
|
+
})));
|
|
63
|
+
// ── Messages ────────────────────────────────────────────────────────────────────
|
|
64
|
+
const init = { jsonrpc: '2.0', method: 'initialize', id: 1,
|
|
65
|
+
params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'c', version: '0' } } };
|
|
66
|
+
const initialized = { jsonrpc: '2.0', method: 'notifications/initialized' };
|
|
67
|
+
const call = (id, name, args) => ({ jsonrpc: '2.0', method: 'tools/call', id, params: { name, arguments: args } });
|
|
68
|
+
const list = (id) => ({ jsonrpc: '2.0', method: 'tools/list', id });
|
|
69
|
+
// Runs `init`, `notifications/initialized`, then `msgs`; returns the tool responses.
|
|
70
|
+
const session = (...msgs) => runSession([init, initialized, ...msgs]).slice(2);
|
|
71
|
+
const resultOf = (resp) => resp.result;
|
|
72
|
+
const item0 = (resp) => resultOf(resp).content[0];
|
|
73
|
+
const textOf = (resp) => item0(resp).text;
|
|
74
|
+
// A plain text sample for text add→get round-trips.
|
|
75
|
+
const textSample = 'hello, world!';
|
|
76
|
+
// A base64-encoded binary payload for binary add→get round-trips.
|
|
77
|
+
const binarySample = base64Encode(vec8(0x2an));
|
|
78
|
+
// A base64 blob whose leading bytes are the PNG magic-byte signature, so
|
|
79
|
+
// `cas_get` detects its type and returns base64 with mime_type image/png.
|
|
80
|
+
const pngSample = base64Encode(u8ListToVec(msb)([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x01]));
|
|
81
|
+
// ── Tests ───────────────────────────────────────────────────────────────────────
|
|
82
|
+
export const proof = {
|
|
83
|
+
toolsListAdvertisesThreeTools: () => {
|
|
84
|
+
const [resp] = runSession([init, initialized, list(2)]).slice(2);
|
|
85
|
+
const tools = resp.result.tools;
|
|
86
|
+
assertEq(tools.length, 3);
|
|
87
|
+
assertEq(tools.map(t => t.name).join(','), 'cas_add,cas_get,cas_list');
|
|
88
|
+
const add = resp.result.tools[0];
|
|
89
|
+
assertEq(add.inputSchema.type, 'object');
|
|
90
|
+
},
|
|
91
|
+
addReturnsHash: () => {
|
|
92
|
+
const [resp] = session(call(2, 'cas_add', { content: textSample }));
|
|
93
|
+
assert(!resultOf(resp).isError);
|
|
94
|
+
assert(textOf(resp).length > 0);
|
|
95
|
+
},
|
|
96
|
+
// Text add→get round-trip: store as UTF-8 text, retrieve metadata.
|
|
97
|
+
textAddGetMetaRoundTrips: () => {
|
|
98
|
+
const [addResp] = session(call(2, 'cas_add', { content: textSample }));
|
|
99
|
+
const hash = textOf(addResp);
|
|
100
|
+
const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash }));
|
|
101
|
+
assert(!resultOf(getResp).isError);
|
|
102
|
+
const result = JSON.parse(textOf(getResp));
|
|
103
|
+
assertEq(result.mime_type, 'text/plain');
|
|
104
|
+
assertEq(result.type, 'text');
|
|
105
|
+
assertEq(result.length, textSample.length);
|
|
106
|
+
assertEq(result.content, undefined);
|
|
107
|
+
},
|
|
108
|
+
// cas_get with content:true returns inline content.
|
|
109
|
+
textAddGetContentRoundTrips: () => {
|
|
110
|
+
const [addResp] = session(call(2, 'cas_add', { content: textSample }));
|
|
111
|
+
const hash = textOf(addResp);
|
|
112
|
+
const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash, content: true }));
|
|
113
|
+
assert(!resultOf(getResp).isError);
|
|
114
|
+
const result = JSON.parse(textOf(getResp));
|
|
115
|
+
assertEq(result.type, 'text');
|
|
116
|
+
assertEq(result.mime_type, 'text/plain');
|
|
117
|
+
assertEq(result.content, textSample);
|
|
118
|
+
},
|
|
119
|
+
// Binary add→get round-trip: store as base64, retrieve metadata without content.
|
|
120
|
+
binaryAddGetMetaRoundTrips: () => {
|
|
121
|
+
const [addResp] = session(call(2, 'cas_add', { content: binarySample, type: 'base64' }));
|
|
122
|
+
const hash = textOf(addResp);
|
|
123
|
+
const [, getResp] = session(call(2, 'cas_add', { content: binarySample, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
124
|
+
assert(!resultOf(getResp).isError);
|
|
125
|
+
const result = JSON.parse(textOf(getResp));
|
|
126
|
+
assertEq(result.content, undefined);
|
|
127
|
+
assertEq(result.type, 'text');
|
|
128
|
+
assertEq(result.mime_type, 'text/plain');
|
|
129
|
+
},
|
|
130
|
+
// Binary add→get with content:true returns inline base64 content.
|
|
131
|
+
binaryAddGetContentRoundTrips: () => {
|
|
132
|
+
const [addResp] = session(call(2, 'cas_add', { content: binarySample, type: 'base64' }));
|
|
133
|
+
const hash = textOf(addResp);
|
|
134
|
+
const [, getResp] = session(call(2, 'cas_add', { content: binarySample, type: 'base64' }), call(3, 'cas_get', { hash, content: true }));
|
|
135
|
+
assert(!resultOf(getResp).isError);
|
|
136
|
+
const result = JSON.parse(textOf(getResp));
|
|
137
|
+
assertEq(result.type, 'text');
|
|
138
|
+
assertEq(result.content, '*');
|
|
139
|
+
},
|
|
140
|
+
addGetRoundTrips: () => {
|
|
141
|
+
const [addResp] = session(call(2, 'cas_add', { content: textSample }));
|
|
142
|
+
const hash = textOf(addResp);
|
|
143
|
+
const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash, content: true }));
|
|
144
|
+
assert(!resultOf(getResp).isError);
|
|
145
|
+
const result = JSON.parse(textOf(getResp));
|
|
146
|
+
assertEq(result.content, textSample);
|
|
147
|
+
},
|
|
148
|
+
// Text content (no magic-byte match, valid UTF-8) comes back with type:'text' when content requested.
|
|
149
|
+
getUntypedReturnsText: () => {
|
|
150
|
+
const [addResp] = session(call(2, 'cas_add', { content: textSample }));
|
|
151
|
+
const hash = textOf(addResp);
|
|
152
|
+
const [, getResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_get', { hash, content: true }));
|
|
153
|
+
assertEq(item0(getResp) === null ? null : item0(getResp).type, 'text');
|
|
154
|
+
const result = JSON.parse(textOf(getResp));
|
|
155
|
+
assertEq(result.type, 'text');
|
|
156
|
+
assertEq(result.mime_type, 'text/plain');
|
|
157
|
+
},
|
|
158
|
+
// Bytes with a recognised PNG signature come back as base64 with mime_type image/png when content requested.
|
|
159
|
+
getTypedReturnsBinaryJson: () => {
|
|
160
|
+
const [addResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }));
|
|
161
|
+
const hash = textOf(addResp);
|
|
162
|
+
const [, getResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }), call(3, 'cas_get', { hash, content: true }));
|
|
163
|
+
assert(!resultOf(getResp).isError);
|
|
164
|
+
assertEq(item0(getResp).type, 'text');
|
|
165
|
+
const result = JSON.parse(textOf(getResp));
|
|
166
|
+
assertEq(result.type, 'base64');
|
|
167
|
+
assertEq(result.mime_type, 'image/png');
|
|
168
|
+
assertEq(result.content, pngSample);
|
|
169
|
+
},
|
|
170
|
+
listEnumeratesStoredHashes: () => {
|
|
171
|
+
const [addResp, listResp] = session(call(2, 'cas_add', { content: textSample }), call(3, 'cas_list', {}));
|
|
172
|
+
const hash = textOf(addResp);
|
|
173
|
+
assert(!resultOf(listResp).isError);
|
|
174
|
+
assertEq(textOf(listResp), hash);
|
|
175
|
+
},
|
|
176
|
+
addInvalidContentIsError: () => {
|
|
177
|
+
const [resp] = session(call(2, 'cas_add', { content: 'not valid!', type: 'base64' }));
|
|
178
|
+
assertEq(resultOf(resp).isError, true);
|
|
179
|
+
},
|
|
180
|
+
addBadLengthContentIsError: () => {
|
|
181
|
+
const [resp] = session(call(2, 'cas_add', { content: 'A', type: 'base64' }));
|
|
182
|
+
assertEq(resultOf(resp).isError, true);
|
|
183
|
+
},
|
|
184
|
+
getUnterminatedHashIsError: () => {
|
|
185
|
+
const [resp] = session(call(2, 'cas_get', { hash: '0' }));
|
|
186
|
+
assertEq(resultOf(resp).isError, true);
|
|
187
|
+
},
|
|
188
|
+
addMissingContentIsError: () => {
|
|
189
|
+
const [resp] = session(call(2, 'cas_add', {}));
|
|
190
|
+
assertEq(resultOf(resp).isError, true);
|
|
191
|
+
},
|
|
192
|
+
getMissingHashArgumentIsError: () => {
|
|
193
|
+
const [resp] = session(call(2, 'cas_get', {}));
|
|
194
|
+
assertEq(resultOf(resp).isError, true);
|
|
195
|
+
},
|
|
196
|
+
getInvalidHashIsError: () => {
|
|
197
|
+
const [resp] = session(call(2, 'cas_get', { hash: 'not valid!' }));
|
|
198
|
+
assertEq(resultOf(resp).isError, true);
|
|
199
|
+
},
|
|
200
|
+
getMissingHashIsError: () => {
|
|
201
|
+
const absent = vecToCBase32(vec8(0x99n));
|
|
202
|
+
const [resp] = session(call(2, 'cas_get', { hash: absent }));
|
|
203
|
+
assertEq(resultOf(resp).isError, true);
|
|
204
|
+
},
|
|
205
|
+
unknownToolIsError: () => {
|
|
206
|
+
const [resp] = session(call(2, 'cas_remove', { hash: binarySample }));
|
|
207
|
+
assertEq(resultOf(resp).isError, true);
|
|
208
|
+
},
|
|
209
|
+
toolErrorIsNotJsonRpcError: () => {
|
|
210
|
+
const [resp] = session(call(2, 'cas_add', { content: 'not valid!', type: 'base64' }));
|
|
211
|
+
assert(!('error' in resp));
|
|
212
|
+
assert('result' in resp);
|
|
213
|
+
},
|
|
214
|
+
// cas_add with type:'url' reads a file and stores it.
|
|
215
|
+
addUrlStoresFileAndReturnsHash: () => {
|
|
216
|
+
const fileContent = utf8('hello from file');
|
|
217
|
+
const [addUrlResp] = runSessionWithFiles({ '/tmp/hello.txt': fileContent })([
|
|
218
|
+
init, initialized,
|
|
219
|
+
call(2, 'cas_add', { content: '/tmp/hello.txt', type: 'url' }),
|
|
220
|
+
]).slice(2);
|
|
221
|
+
assert(!resultOf(addUrlResp).isError);
|
|
222
|
+
assert(textOf(addUrlResp).length > 0);
|
|
223
|
+
},
|
|
224
|
+
addUrlRoundTrips: () => {
|
|
225
|
+
const fileContent = utf8('round-trip content');
|
|
226
|
+
const msgs = runSessionWithFiles({ '/tmp/rt.txt': fileContent })([
|
|
227
|
+
init, initialized,
|
|
228
|
+
call(2, 'cas_add', { content: '/tmp/rt.txt', type: 'url' }),
|
|
229
|
+
]).slice(2);
|
|
230
|
+
const hash = textOf(msgs[0]);
|
|
231
|
+
const msgs2 = runSessionWithFiles({ '/tmp/rt.txt': fileContent })([
|
|
232
|
+
init, initialized,
|
|
233
|
+
call(2, 'cas_add', { content: '/tmp/rt.txt', type: 'url' }),
|
|
234
|
+
call(3, 'cas_get', { hash, content: true }),
|
|
235
|
+
]).slice(2);
|
|
236
|
+
assert(!resultOf(msgs2[1]).isError);
|
|
237
|
+
const result = JSON.parse(textOf(msgs2[1]));
|
|
238
|
+
assertEq(result.type, 'text');
|
|
239
|
+
assertEq(result.content, 'round-trip content');
|
|
240
|
+
},
|
|
241
|
+
addUrlMissingFileIsError: () => {
|
|
242
|
+
const [resp] = runSessionWithFiles({})([
|
|
243
|
+
init, initialized,
|
|
244
|
+
call(2, 'cas_add', { content: '/nonexistent/path.txt', type: 'url' }),
|
|
245
|
+
]).slice(2);
|
|
246
|
+
assertEq(resultOf(resp).isError, true);
|
|
247
|
+
},
|
|
248
|
+
// cas_get without content:true returns only metadata.
|
|
249
|
+
getMetaReturnsLengthAndMimeType: () => {
|
|
250
|
+
const fileContent = utf8('text content');
|
|
251
|
+
const [addResp] = runSessionWithFiles({ '/f': fileContent })([
|
|
252
|
+
init, initialized,
|
|
253
|
+
call(2, 'cas_add', { content: '/f', type: 'url' }),
|
|
254
|
+
]).slice(2);
|
|
255
|
+
const hash = textOf(addResp);
|
|
256
|
+
const [, metaResp2] = runSessionWithFiles({ '/f': fileContent })([
|
|
257
|
+
init, initialized,
|
|
258
|
+
call(2, 'cas_add', { content: '/f', type: 'url' }),
|
|
259
|
+
call(3, 'cas_get', { hash }),
|
|
260
|
+
]).slice(2);
|
|
261
|
+
assert(!resultOf(metaResp2).isError);
|
|
262
|
+
const meta = JSON.parse(textOf(metaResp2));
|
|
263
|
+
assertEq(meta.mime_type, 'text/plain');
|
|
264
|
+
assertEq(meta.type, 'text');
|
|
265
|
+
assertEq(meta.length, Number(BigInt(/* 'text content'.length */ 12)));
|
|
266
|
+
assertEq(meta.content, undefined);
|
|
267
|
+
},
|
|
268
|
+
getMetaBinaryBlob: () => {
|
|
269
|
+
const [addResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }));
|
|
270
|
+
const hash = textOf(addResp);
|
|
271
|
+
const [, metaResp] = session(call(2, 'cas_add', { content: pngSample, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
272
|
+
assert(!resultOf(metaResp).isError);
|
|
273
|
+
const meta = JSON.parse(textOf(metaResp));
|
|
274
|
+
assertEq(meta.mime_type, 'image/png');
|
|
275
|
+
assertEq(meta.type, 'base64');
|
|
276
|
+
assertEq(meta.length, 10);
|
|
277
|
+
assertEq(meta.content, undefined);
|
|
278
|
+
},
|
|
279
|
+
getMetaOctetStreamForUnknownBinary: () => {
|
|
280
|
+
const binaryContent = u8ListToVec(msb)([0xFF, 0xFE, 0x00, 0x01]);
|
|
281
|
+
const binaryB64 = base64Encode(binaryContent);
|
|
282
|
+
const [addResp] = session(call(2, 'cas_add', { content: binaryB64, type: 'base64' }));
|
|
283
|
+
const hash = textOf(addResp);
|
|
284
|
+
const [, metaResp] = session(call(2, 'cas_add', { content: binaryB64, type: 'base64' }), call(3, 'cas_get', { hash }));
|
|
285
|
+
assert(!resultOf(metaResp).isError);
|
|
286
|
+
const meta = JSON.parse(textOf(metaResp));
|
|
287
|
+
assertEq(meta.mime_type, 'application/octet-stream');
|
|
288
|
+
assertEq(meta.type, 'base64');
|
|
289
|
+
assertEq(meta.content, undefined);
|
|
290
|
+
},
|
|
291
|
+
getMetaNoUrlWhenToUrlAbsent: () => {
|
|
292
|
+
const content = utf8('no url');
|
|
293
|
+
const binaryB64 = base64Encode(content);
|
|
294
|
+
const [addResp] = session(call(2, 'cas_add', { content: binaryB64 }));
|
|
295
|
+
const hash = textOf(addResp);
|
|
296
|
+
const [, metaResp] = session(call(2, 'cas_add', { content: binaryB64 }), call(3, 'cas_get', { hash }));
|
|
297
|
+
assert(!resultOf(metaResp).isError);
|
|
298
|
+
const meta = JSON.parse(textOf(metaResp));
|
|
299
|
+
assertEq(meta.url, undefined);
|
|
300
|
+
},
|
|
301
|
+
getMetaMissingHashIsError: () => {
|
|
302
|
+
const absent = vecToCBase32(vec8(0x77n));
|
|
303
|
+
const [resp] = session(call(2, 'cas_get', { hash: absent }));
|
|
304
|
+
assertEq(resultOf(resp).isError, true);
|
|
305
|
+
},
|
|
306
|
+
getMetaInvalidHashIsError: () => {
|
|
307
|
+
const [resp] = session(call(2, 'cas_get', { hash: 'bad!' }));
|
|
308
|
+
assertEq(resultOf(resp).isError, true);
|
|
309
|
+
},
|
|
310
|
+
};
|
package/fs/cas/module.f.d.ts
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
import { type Sha2 } from '../crypto/sha2/module.f.ts';
|
|
7
7
|
import type { Vec } from '../types/bit_vec/module.f.ts';
|
|
8
8
|
import { type Effect, type Operation } from '../effects/module.f.ts';
|
|
9
|
-
import { type
|
|
9
|
+
import { type Access, type All, type Mkdir, type NodeProgramOptions, type Read, type Readdir, type ReadFile, type Write, type WriteFile } from '../effects/node/module.f.ts';
|
|
10
|
+
import { type Commands } from '../cli/module.f.ts';
|
|
11
|
+
import type { MemOp } from '../effects/memory/module.f.ts';
|
|
10
12
|
export type KvStore<O extends Operation> = {
|
|
11
13
|
/** Reads a value by key; returns `undefined` when the key does not exist. */
|
|
12
14
|
readonly read: (key: Vec) => Effect<O, Vec | undefined>;
|
|
@@ -17,10 +19,13 @@ export type KvStore<O extends Operation> = {
|
|
|
17
19
|
};
|
|
18
20
|
/** A key/value tuple where index `0` is the key and index `1` is the value. */
|
|
19
21
|
export type Kv = readonly [Vec, Vec];
|
|
22
|
+
/** Converts a content key to its sharded relative CAS file path. */
|
|
23
|
+
export declare const toPath: (key: Vec) => string;
|
|
24
|
+
export type FileKvStoreOperation = ReadFile | Mkdir | WriteFile | Access | Readdir;
|
|
20
25
|
/**
|
|
21
26
|
* Creates a filesystem-backed key/value store under the provided root path.
|
|
22
27
|
*/
|
|
23
|
-
export declare const fileKvStore: (path: string) => KvStore<
|
|
28
|
+
export declare const fileKvStore: (path: string) => KvStore<FileKvStoreOperation>;
|
|
24
29
|
export type Cas<O extends Operation> = {
|
|
25
30
|
/** Reads content by hash; returns `undefined` when not found. */
|
|
26
31
|
readonly read: (key: Vec) => Effect<O, Vec | undefined>;
|
|
@@ -33,4 +38,5 @@ export type Cas<O extends Operation> = {
|
|
|
33
38
|
* Builds a content-addressable storage facade from a SHA-2 implementation.
|
|
34
39
|
*/
|
|
35
40
|
export declare const cas: (sha2: Sha2) => <O extends Operation>(_: KvStore<O>) => Cas<O>;
|
|
36
|
-
export declare const
|
|
41
|
+
export declare const commands: Commands<FileKvStoreOperation | Write | All | MemOp | Read>;
|
|
42
|
+
export declare const main: (options: NodeProgramOptions) => Effect<All | Read | Write | FileKvStoreOperation | MemOp, number>;
|
package/fs/cas/module.f.js
CHANGED
|
@@ -4,45 +4,70 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { computeSync, sha256 } from "../crypto/sha2/module.f.js";
|
|
7
|
-
import { parse } from "../path/module.f.js";
|
|
7
|
+
import { join, normalize, parse } from "../path/module.f.js";
|
|
8
8
|
import { cBase32ToVec, vecToCBase32 } from "../cbase32/module.f.js";
|
|
9
9
|
import { forEachStep, pure } from "../effects/module.f.js";
|
|
10
|
-
import { errorExit, log, mkdir, readdir, readFile, writeFile } from "../effects/node/module.f.js";
|
|
10
|
+
import { access, errorExit, isNotFound, log, mkdir, readdir, readFile, writeFile } from "../effects/node/module.f.js";
|
|
11
11
|
import { dispatch } from "../cli/module.f.js";
|
|
12
|
+
import { casMcpServer } from "./mcp/module.f.js";
|
|
12
13
|
import { toOption } from "../types/nullable/module.f.js";
|
|
13
14
|
import { unwrap } from "../types/result/module.f.js";
|
|
15
|
+
import { splitAt } from "../types/string/module.f.js";
|
|
14
16
|
const o = { withFileTypes: true };
|
|
15
|
-
const
|
|
17
|
+
const split2 = splitAt(2);
|
|
16
18
|
const prefix = '.cas';
|
|
17
19
|
/** Converts a content key to its sharded relative CAS file path. */
|
|
18
|
-
const toPath = (key) => {
|
|
20
|
+
export const toPath = (key) => {
|
|
19
21
|
const s = vecToCBase32(key);
|
|
20
|
-
const [a, bc] =
|
|
21
|
-
const [b, c] =
|
|
22
|
-
return
|
|
22
|
+
const [a, bc] = split2(s);
|
|
23
|
+
const [b, c] = split2(bc);
|
|
24
|
+
return join(prefix, a, b, c);
|
|
23
25
|
};
|
|
24
26
|
/**
|
|
25
27
|
* Creates a filesystem-backed key/value store under the provided root path.
|
|
26
28
|
*/
|
|
27
|
-
export const fileKvStore = (path) =>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
29
|
+
export const fileKvStore = (path) => {
|
|
30
|
+
const storePrefix = join(path, prefix);
|
|
31
|
+
const normalizedStorePrefix = normalize(storePrefix);
|
|
32
|
+
return {
|
|
33
|
+
read: (key) => readFile(join(path, toPath(key))).step(r => {
|
|
34
|
+
if (r[0] === 'ok') {
|
|
35
|
+
return pure(r[1]);
|
|
36
|
+
}
|
|
37
|
+
// A missing file means "no such content"; surface anything else.
|
|
38
|
+
if (isNotFound(r[1])) {
|
|
39
|
+
return pure(undefined);
|
|
40
|
+
}
|
|
41
|
+
throw r[1];
|
|
42
|
+
}),
|
|
43
|
+
write: (key, value) => {
|
|
44
|
+
const p = toPath(key);
|
|
45
|
+
const parts = parse(p);
|
|
46
|
+
const dir = join(path, ...parts.slice(0, -1));
|
|
47
|
+
// TODO: error handling
|
|
48
|
+
return mkdir(dir, { recursive: true })
|
|
49
|
+
.step(() => writeFile(join(path, p), value))
|
|
50
|
+
.step(() => pure(undefined));
|
|
51
|
+
},
|
|
52
|
+
list: () =>
|
|
53
|
+
// A fresh store has no `.cas` directory yet. Treat *only* that case as an
|
|
54
|
+
// empty store, mirroring how `read` maps a missing file to `undefined`.
|
|
55
|
+
// A `.cas` that exists but cannot be read (permissions, corruption) is a
|
|
56
|
+
// genuine storage error and is surfaced, not masked as "no hashes".
|
|
57
|
+
access(storePrefix).step(a => {
|
|
58
|
+
if (a[0] === 'error') {
|
|
59
|
+
if (isNotFound(a[1])) {
|
|
60
|
+
return pure([]);
|
|
61
|
+
}
|
|
62
|
+
throw a[1];
|
|
63
|
+
}
|
|
64
|
+
return readdir(storePrefix, { recursive: true })
|
|
65
|
+
.step(r => pure(unwrap(r).flatMap(({ name, parentPath, isFile }) => toOption(isFile
|
|
66
|
+
? cBase32ToVec(normalize(parentPath).substring(normalizedStorePrefix.length).replaceAll('/', '') + name)
|
|
67
|
+
: null))));
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
};
|
|
46
71
|
/**
|
|
47
72
|
* Builds a content-addressable storage facade from a SHA-2 implementation.
|
|
48
73
|
*/
|
|
@@ -58,50 +83,60 @@ export const cas = (sha2) => {
|
|
|
58
83
|
list,
|
|
59
84
|
});
|
|
60
85
|
};
|
|
61
|
-
export const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.step(() => pure(0));
|
|
75
|
-
},
|
|
86
|
+
export const commands = [
|
|
87
|
+
{
|
|
88
|
+
names: ['add'],
|
|
89
|
+
description: 'Store file content and print its hash',
|
|
90
|
+
handler: ({ home, args: [path, ...rest] }) => {
|
|
91
|
+
if (path === undefined || rest.length !== 0) {
|
|
92
|
+
return errorExit("'cas add' expects one parameter");
|
|
93
|
+
}
|
|
94
|
+
const c = cas(sha256)(fileKvStore(home));
|
|
95
|
+
return readFile(path)
|
|
96
|
+
.step(v => c.write(unwrap(v)))
|
|
97
|
+
.step(hash => log(vecToCBase32(hash)))
|
|
98
|
+
.step(() => pure(0));
|
|
76
99
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
names: ['get'],
|
|
103
|
+
description: 'Restore content by hash into a file',
|
|
104
|
+
handler: ({ home, args: [hashCBase32, path, ...rest] }) => {
|
|
105
|
+
if (hashCBase32 === undefined || path === undefined || rest.length !== 0) {
|
|
106
|
+
return errorExit("'cas get' expects two parameters");
|
|
107
|
+
}
|
|
108
|
+
const hash = cBase32ToVec(hashCBase32);
|
|
109
|
+
if (hash === null) {
|
|
110
|
+
return errorExit(`invalid hash format: ${hashCBase32}`);
|
|
111
|
+
}
|
|
112
|
+
const c = cas(sha256)(fileKvStore(home));
|
|
113
|
+
return c.read(hash)
|
|
114
|
+
.step(v => {
|
|
115
|
+
const result = v === undefined
|
|
116
|
+
? errorExit(`no such hash: ${hashCBase32}`)
|
|
117
|
+
: writeFile(path, v)
|
|
118
|
+
.step(() => pure(0));
|
|
119
|
+
return result;
|
|
120
|
+
});
|
|
97
121
|
},
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
names: ['list'],
|
|
125
|
+
description: 'List all stored content hashes',
|
|
126
|
+
handler: ({ home }) => {
|
|
127
|
+
const c = cas(sha256)(fileKvStore(home));
|
|
128
|
+
return c.list()
|
|
102
129
|
.step(forEachStep(j => log(vecToCBase32(j))))
|
|
103
|
-
.step(() => pure(0))
|
|
130
|
+
.step(() => pure(0));
|
|
104
131
|
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
names: ['mcp'],
|
|
135
|
+
description: 'Run an MCP server over stdio exposing the CAS as tools',
|
|
136
|
+
handler: ({ home }) => {
|
|
137
|
+
const c = cas(sha256)(fileKvStore(home));
|
|
138
|
+
return casMcpServer(c, hash => join(home, toPath(hash))).step(() => pure(0));
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
export const main = dispatch(commands);
|
package/fs/cas/proof.f.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export declare const proof: {
|
|
|
6
6
|
mainGetWrongArgs: () => void;
|
|
7
7
|
mainGetInvalidHash: () => void;
|
|
8
8
|
mainList: () => void;
|
|
9
|
+
mainListEmptyStore: () => void;
|
|
10
|
+
mainListCorruptStore: () => void;
|
|
9
11
|
mainNoCmd: () => void;
|
|
10
12
|
mainUnknownCmd: () => void;
|
|
11
13
|
casWrite: () => void;
|
package/fs/cas/proof.f.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { cas,
|
|
1
|
+
import { cas, commands } from "./module.f.js";
|
|
2
2
|
import { sha256 } from "../crypto/sha2/module.f.js";
|
|
3
3
|
import { empty, length, vec8 } from "../types/bit_vec/module.f.js";
|
|
4
4
|
import { pure } from "../effects/module.f.js";
|
|
5
5
|
import { run } from "../effects/mock/module.f.js";
|
|
6
6
|
import { emptyState, virtual } from "../effects/node/virtual/module.f.js";
|
|
7
|
+
import { dispatch } from "../cli/module.f.js";
|
|
7
8
|
const makeOptions = (args) => ({
|
|
8
9
|
args,
|
|
9
10
|
env: {},
|
|
11
|
+
home: '.',
|
|
10
12
|
std: { stdout: { isTTY: false }, stderr: { isTTY: false } },
|
|
11
13
|
testContext: { test: async () => { } },
|
|
12
14
|
bunTestContext: { test: async () => { } },
|
|
13
15
|
playwrightTestContext: { test: async () => { } },
|
|
14
16
|
engine: 'node',
|
|
15
17
|
});
|
|
18
|
+
const main = dispatch(commands);
|
|
16
19
|
export const proof = {
|
|
17
20
|
mainAdd: () => {
|
|
18
21
|
const content = vec8(0x2an);
|
|
@@ -89,6 +92,32 @@ export const proof = {
|
|
|
89
92
|
throw ['expected exit 0', exitCode];
|
|
90
93
|
}
|
|
91
94
|
},
|
|
95
|
+
mainListEmptyStore: () => {
|
|
96
|
+
// A fresh directory has no `.cas` yet; listing must succeed (empty),
|
|
97
|
+
// not crash unwrapping a readdir ENOENT.
|
|
98
|
+
const [finalState, exitCode] = virtual(emptyState)(main(makeOptions(['list'])));
|
|
99
|
+
if (exitCode !== 0) {
|
|
100
|
+
throw ['expected exit 0', exitCode];
|
|
101
|
+
}
|
|
102
|
+
if (finalState.stdout !== '') {
|
|
103
|
+
throw ['expected empty stdout', finalState.stdout];
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
mainListCorruptStore: () => {
|
|
107
|
+
// `.cas` exists but is a file, not a directory: a real storage error
|
|
108
|
+
// that must surface, not be masked as an empty list.
|
|
109
|
+
const state = { ...emptyState, root: { '.cas': vec8(0x2an) } };
|
|
110
|
+
let threw = false;
|
|
111
|
+
try {
|
|
112
|
+
virtual(state)(main(makeOptions(['list'])));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
threw = true;
|
|
116
|
+
}
|
|
117
|
+
if (!threw) {
|
|
118
|
+
throw 'expected list to surface the storage error';
|
|
119
|
+
}
|
|
120
|
+
},
|
|
92
121
|
mainNoCmd: () => {
|
|
93
122
|
const [finalState, exitCode] = virtual(emptyState)(main(makeOptions([])));
|
|
94
123
|
if (exitCode !== 1) {
|
package/fs/cbase32/module.f.d.ts
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
import { type Vec } from "../types/bit_vec/module.f.ts";
|
|
7
7
|
import type { Nullable } from "../types/nullable/module.f.ts";
|
|
8
8
|
export declare const vec5xToCBase32: (v: Vec) => string;
|
|
9
|
-
export declare const vecToCBase32: (v: Vec) => string;
|
|
10
9
|
export declare const cBase32ToVec5x: (s: string) => Nullable<Vec>;
|
|
10
|
+
export declare const vecToCBase32: (v: Vec) => string;
|
|
11
11
|
export declare const cBase32ToVec: (s: string) => Nullable<Vec>;
|