functionalscript 0.35.2 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/basen/base64/module.f.js +71 -0
- package/fs/{base64 → basen/base64}/proof.f.d.ts +2 -0
- package/fs/{base64 → basen/base64}/proof.f.js +17 -2
- 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.d.ts +8 -4
- package/fs/cas/mcp/module.f.js +124 -72
- package/fs/cas/mcp/proof.f.d.ts +3 -8
- package/fs/cas/mcp/proof.f.js +122 -186
- package/fs/cas/module.f.js +12 -19
- 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 +20 -21
- package/fs/ci/config/module.f.js +8 -10
- 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 +19 -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.d.ts +15 -0
- package/fs/effects/module.f.js +15 -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 +10 -20
- package/fs/effects/node/module.js +13 -11
- package/fs/effects/node/proof.f.d.ts +3 -0
- package/fs/effects/node/proof.f.js +19 -1
- package/fs/effects/node/virtual/module.f.js +7 -8
- 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.d.ts +5 -0
- package/fs/effects/proof.f.js +22 -1
- 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.d.ts +1 -0
- package/fs/fsc/module.f.js +11 -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.d.ts +1 -1
- package/fs/js/tokenizer/module.f.js +14 -8
- package/fs/js/tokenizer/proof.f.js +64 -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 +24 -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.d.ts +6 -0
- package/fs/path/module.f.js +8 -2
- package/fs/path/proof.f.d.ts +1 -0
- package/fs/path/proof.f.js +40 -2
- package/fs/{fjs/proof.f.d.ts → proof.f.d.ts} +1 -0
- package/fs/{fjs/proof.f.js → proof.f.js} +10 -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.d.ts +6 -0
- package/fs/text/utf8/module.f.js +37 -19
- package/fs/text/utf8/proof.f.js +58 -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.f.d.ts +4 -0
- package/fs/types/result/module.f.js +4 -0
- package/fs/types/result/module.js +1 -1
- package/fs/types/result/proof.f.d.ts +9 -3
- package/fs/types/result/proof.f.js +12 -2
- 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/base64/module.f.js +0 -60
- 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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { assertEq } from '../asserts/module.f.js';
|
|
2
|
+
import { msb, u8ListToVec, repeat, vec8 } from '../types/bit_vec/module.f.js';
|
|
3
|
+
import { detect } from './module.f.js';
|
|
4
|
+
import { dialect } from './revision/module.f.js';
|
|
5
|
+
// All test strings here are ASCII, so char code === UTF-8 byte value.
|
|
6
|
+
const utf8Bytes = (s) => u8ListToVec(msb)([...s].map(c => c.charCodeAt(0)));
|
|
7
|
+
const revisionJson = `{"dialect":"${dialect}","subject":"8","parents":[]}`;
|
|
8
|
+
export const proof = {
|
|
9
|
+
// A valid revision blob is recognized and reported under its derived media type.
|
|
10
|
+
validRevision: () => {
|
|
11
|
+
const m = detect(utf8Bytes(revisionJson));
|
|
12
|
+
assertEq(m.type, 'text');
|
|
13
|
+
assertEq(m.mime_type, 'application/vnd.fjs.revision+json');
|
|
14
|
+
},
|
|
15
|
+
// Key order carries no meaning: a valid revision whose `dialect` key is
|
|
16
|
+
// not first must still be detected.
|
|
17
|
+
keyOrderIndependent: () => {
|
|
18
|
+
const text = `{"parents":[],"subject":"8","dialect":"${dialect}"}`;
|
|
19
|
+
const m = detect(utf8Bytes(text));
|
|
20
|
+
assertEq(m.mime_type, 'application/vnd.fjs.revision+json');
|
|
21
|
+
},
|
|
22
|
+
// Structurally invalid revision (bad multi-parent snapshot rule) falls
|
|
23
|
+
// through to the ordinary text/plain classification.
|
|
24
|
+
invalidRevisionFallsThrough: () => {
|
|
25
|
+
const text = `{"dialect":"${dialect}","subject":"8","parents":["8","r"]}`;
|
|
26
|
+
const m = detect(utf8Bytes(text));
|
|
27
|
+
assertEq(m.type, 'text');
|
|
28
|
+
assertEq(m.mime_type, 'text/plain');
|
|
29
|
+
},
|
|
30
|
+
// Ordinary JSON with no `dialect` field at all falls through unchanged.
|
|
31
|
+
ordinaryJsonFallsThrough: () => {
|
|
32
|
+
const m = detect(utf8Bytes('{"hello":"world"}'));
|
|
33
|
+
assertEq(m.type, 'text');
|
|
34
|
+
assertEq(m.mime_type, 'text/plain');
|
|
35
|
+
},
|
|
36
|
+
// Plain (non-JSON) text is unaffected.
|
|
37
|
+
plainTextFallsThrough: () => {
|
|
38
|
+
const m = detect(utf8Bytes('hello'));
|
|
39
|
+
assertEq(m.type, 'text');
|
|
40
|
+
assertEq(m.mime_type, 'text/plain');
|
|
41
|
+
},
|
|
42
|
+
// Binary content (magic-byte hit) is unaffected by dialect detection.
|
|
43
|
+
binaryFallsThrough: () => {
|
|
44
|
+
const m = detect(u8ListToVec(msb)([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
|
|
45
|
+
assertEq(m.type, 'base64');
|
|
46
|
+
assertEq(m.mime_type, 'image/png');
|
|
47
|
+
},
|
|
48
|
+
// A large blob is still bounded by the single-`Vec` `maxLength` cap; this
|
|
49
|
+
// only exercises that dialect detection doesn't change size handling for
|
|
50
|
+
// non-JSON content within that bound.
|
|
51
|
+
largeNonJsonWithinBound: () => {
|
|
52
|
+
const a = repeat(1000n)(vec8(0x61n)); // 1,000 bytes of 'a'
|
|
53
|
+
const m = detect(a);
|
|
54
|
+
assertEq(m.type, 'text');
|
|
55
|
+
assertEq(m.mime_type, 'text/plain');
|
|
56
|
+
},
|
|
57
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type ValidationError } from '../../types/rtti/validate/module.f.ts';
|
|
2
|
+
import type { Ts } from '../../types/rtti/ts/module.f.ts';
|
|
3
|
+
import type { Unknown } from '../json/module.f.ts';
|
|
4
|
+
import { type Result } from '../../types/result/module.f.ts';
|
|
5
|
+
/**
|
|
6
|
+
* Format tag: names the dialect of this BLOB. The media type it is served
|
|
7
|
+
* with is derived mechanically: `application/` + `dialect` + `+json`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const dialect: 'vnd.fjs.revision';
|
|
10
|
+
/** The media type derived from {@link dialect}: `application/vnd.fjs.revision+json`. */
|
|
11
|
+
export declare const mediaType: "application/vnd.fjs.revision+json";
|
|
12
|
+
/**
|
|
13
|
+
* rtti schema for the snapshot-reference type: a cbase32 CAS hash string.
|
|
14
|
+
*
|
|
15
|
+
* This is `string` at the rtti (structural) level — rtti schemas can't
|
|
16
|
+
* express string-content refinements — so cbase32 decodability, and the
|
|
17
|
+
* rejection of `https://` bridge URLs and any other location-addressed
|
|
18
|
+
* reference form, is enforced by {@link isHash} / {@link validate} below,
|
|
19
|
+
* not by this schema on its own.
|
|
20
|
+
*/
|
|
21
|
+
export declare const hash: import("../../types/rtti/module.f.ts").String;
|
|
22
|
+
/**
|
|
23
|
+
* rtti schema for a `revision` BLOB. See the README for the full semantics of
|
|
24
|
+
* each field; `dialect` is the type discriminant, matched here as an exact
|
|
25
|
+
* literal so structural validation alone rejects any other dialect's blob.
|
|
26
|
+
*/
|
|
27
|
+
export declare const revisionSchema: {
|
|
28
|
+
readonly dialect: "vnd.fjs.revision";
|
|
29
|
+
readonly subject: import("../../types/rtti/module.f.ts").String;
|
|
30
|
+
readonly parents: import("../../types/rtti/module.f.ts").Type1<"array", import("../../types/rtti/module.f.ts").String>;
|
|
31
|
+
readonly snapshot: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
32
|
+
readonly generation: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Number, undefined]>;
|
|
33
|
+
readonly archived: import("../../types/rtti/module.f.ts").Or<readonly [true, undefined]>;
|
|
34
|
+
};
|
|
35
|
+
/** The TypeScript type derived from {@link revisionSchema} — the single source of truth. */
|
|
36
|
+
export type Revision = Ts<typeof revisionSchema>;
|
|
37
|
+
/** True when `s` decodes as a cbase32 CAS hash (rejects `https://` and any other non-cbase32 string). */
|
|
38
|
+
export declare const isHash: (s: string) => boolean;
|
|
39
|
+
/** Either a structural validation error or a semantic (snapshot-reference) error message. */
|
|
40
|
+
export type RevisionError = ValidationError | string;
|
|
41
|
+
/**
|
|
42
|
+
* Validates an already-parsed JSON value as a `revision` BLOB: structural
|
|
43
|
+
* (rtti) validation followed by the snapshot-reference semantic checks.
|
|
44
|
+
*/
|
|
45
|
+
export declare const validate: (value: Unknown) => Result<Revision, RevisionError>;
|
|
46
|
+
/**
|
|
47
|
+
* Decodes `text` as a `revision` BLOB: JSON-parses it, then validates it per
|
|
48
|
+
* {@link validate}. Detection is semantic, not syntactic — any JSON that
|
|
49
|
+
* satisfies the schema is a revision, regardless of key order or whitespace.
|
|
50
|
+
*/
|
|
51
|
+
export declare const decodeText: (text: string) => Result<Revision, RevisionError>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vnd.fjs.revision` — one step in the evolution of a mutable object on top
|
|
3
|
+
* of an immutable content-addressable store.
|
|
4
|
+
*
|
|
5
|
+
* A revision BLOB links back to its parent revision(s) (a DAG, not just a
|
|
6
|
+
* chain, so concurrent edits can merge) and carries the full materialized
|
|
7
|
+
* content of that step — never an incremental diff (see the versioning rule
|
|
8
|
+
* in the README). This module is the pure format only: the rtti schema, the
|
|
9
|
+
* `dialect` tag, and decode/validate. No store access, no effects — head
|
|
10
|
+
* resolution, materialization, and reverse indexes are a separate, deferred
|
|
11
|
+
* concern.
|
|
12
|
+
*
|
|
13
|
+
* See `README.md` for the full spec.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { array, number, option, string } from '../../types/rtti/module.f.js';
|
|
18
|
+
import { validate as rttiValidate } from '../../types/rtti/validate/module.f.js';
|
|
19
|
+
import { parse as jsonParse } from '../json/parser/module.f.js';
|
|
20
|
+
import { tokenize as jsonTokenize } from '../json/tokenizer/module.f.js';
|
|
21
|
+
import { stringToList } from '../../text/utf16/module.f.js';
|
|
22
|
+
import { cBase32ToVec } from '../../basen/cbase32/module.f.js';
|
|
23
|
+
import { error, ok } from '../../types/result/module.f.js';
|
|
24
|
+
/**
|
|
25
|
+
* Format tag: names the dialect of this BLOB. The media type it is served
|
|
26
|
+
* with is derived mechanically: `application/` + `dialect` + `+json`.
|
|
27
|
+
*/
|
|
28
|
+
export const dialect = 'vnd.fjs.revision';
|
|
29
|
+
/** The media type derived from {@link dialect}: `application/vnd.fjs.revision+json`. */
|
|
30
|
+
export const mediaType = `application/${dialect}+json`;
|
|
31
|
+
/**
|
|
32
|
+
* rtti schema for the snapshot-reference type: a cbase32 CAS hash string.
|
|
33
|
+
*
|
|
34
|
+
* This is `string` at the rtti (structural) level — rtti schemas can't
|
|
35
|
+
* express string-content refinements — so cbase32 decodability, and the
|
|
36
|
+
* rejection of `https://` bridge URLs and any other location-addressed
|
|
37
|
+
* reference form, is enforced by {@link isHash} / {@link validate} below,
|
|
38
|
+
* not by this schema on its own.
|
|
39
|
+
*/
|
|
40
|
+
export const hash = string;
|
|
41
|
+
/**
|
|
42
|
+
* rtti schema for a `revision` BLOB. See the README for the full semantics of
|
|
43
|
+
* each field; `dialect` is the type discriminant, matched here as an exact
|
|
44
|
+
* literal so structural validation alone rejects any other dialect's blob.
|
|
45
|
+
*/
|
|
46
|
+
export const revisionSchema = {
|
|
47
|
+
dialect,
|
|
48
|
+
subject: string,
|
|
49
|
+
parents: array(hash),
|
|
50
|
+
snapshot: option(hash),
|
|
51
|
+
generation: option(number),
|
|
52
|
+
archived: option(true),
|
|
53
|
+
};
|
|
54
|
+
/** Structural-only validator: checks the shape, not the snapshot-reference semantics. */
|
|
55
|
+
const validateShape = rttiValidate(revisionSchema);
|
|
56
|
+
/** True when `s` decodes as a cbase32 CAS hash (rejects `https://` and any other non-cbase32 string). */
|
|
57
|
+
export const isHash = (s) => cBase32ToVec(s) !== null;
|
|
58
|
+
/**
|
|
59
|
+
* Checks the snapshot-reference semantics of an already shape-valid revision:
|
|
60
|
+
* every parent must be a hash; `snapshot`, when present, must be a hash;
|
|
61
|
+
* when `snapshot` is absent, zero parents fall back to `subject` (which must
|
|
62
|
+
* then be a hash), exactly one parent inherits that parent's snapshot, and
|
|
63
|
+
* more than one parent without an explicit `snapshot` is invalid — there is
|
|
64
|
+
* no single parent snapshot to inherit, and falling back to `subject` would
|
|
65
|
+
* silently lose the merge result.
|
|
66
|
+
*/
|
|
67
|
+
const checkReferences = (r) => {
|
|
68
|
+
for (const p of r.parents) {
|
|
69
|
+
if (!isHash(p)) {
|
|
70
|
+
return error(`parent is not a valid hash: ${p}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (r.snapshot !== undefined) {
|
|
74
|
+
return isHash(r.snapshot) ? ok(r) : error(`snapshot is not a valid hash: ${r.snapshot}`);
|
|
75
|
+
}
|
|
76
|
+
if (r.parents.length === 0) {
|
|
77
|
+
return isHash(r.subject)
|
|
78
|
+
? ok(r)
|
|
79
|
+
: error(`subject must be a valid hash when snapshot is absent and there are no parents: ${r.subject}`);
|
|
80
|
+
}
|
|
81
|
+
if (r.parents.length === 1) {
|
|
82
|
+
return ok(r);
|
|
83
|
+
}
|
|
84
|
+
return error('snapshot is required when a revision has more than one parent');
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Validates an already-parsed JSON value as a `revision` BLOB: structural
|
|
88
|
+
* (rtti) validation followed by the snapshot-reference semantic checks.
|
|
89
|
+
*/
|
|
90
|
+
export const validate = (value) => {
|
|
91
|
+
const [t, v] = validateShape(value);
|
|
92
|
+
if (t === 'error') {
|
|
93
|
+
return error(v);
|
|
94
|
+
}
|
|
95
|
+
return checkReferences(v);
|
|
96
|
+
};
|
|
97
|
+
/** Parses `text` as JSON without relying on `JSON.parse` (the shared `fs/media/json` parser/tokenizer). */
|
|
98
|
+
const parseJson = (text) => jsonParse(jsonTokenize(stringToList(text)));
|
|
99
|
+
/**
|
|
100
|
+
* Decodes `text` as a `revision` BLOB: JSON-parses it, then validates it per
|
|
101
|
+
* {@link validate}. Detection is semantic, not syntactic — any JSON that
|
|
102
|
+
* satisfies the schema is a revision, regardless of key order or whitespace.
|
|
103
|
+
*/
|
|
104
|
+
export const decodeText = (text) => {
|
|
105
|
+
const [t, v] = parseJson(text);
|
|
106
|
+
if (t === 'error') {
|
|
107
|
+
return error(v);
|
|
108
|
+
}
|
|
109
|
+
return validate(v);
|
|
110
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const proof: {
|
|
2
|
+
dialectAndMediaType: () => void;
|
|
3
|
+
isHash: {
|
|
4
|
+
validHash: () => void;
|
|
5
|
+
httpsRejected: () => void;
|
|
6
|
+
arbitraryStringRejected: () => void;
|
|
7
|
+
};
|
|
8
|
+
validate: {
|
|
9
|
+
firstRevisionFallsBackToSubjectHash: () => void;
|
|
10
|
+
firstRevisionRejectsNonHashSubject: () => void;
|
|
11
|
+
explicitSnapshotAllowsArbitrarySubject: () => void;
|
|
12
|
+
singleParentInheritsSnapshot: () => void;
|
|
13
|
+
multiParentWithoutSnapshotRejected: () => void;
|
|
14
|
+
multiParentWithSnapshotAccepted: () => void;
|
|
15
|
+
httpsRejectedInParents: () => void;
|
|
16
|
+
httpsRejectedInSnapshot: () => void;
|
|
17
|
+
httpsRejectedAsFallbackSubject: () => void;
|
|
18
|
+
archivedAccepted: () => void;
|
|
19
|
+
wrongDialectRejected: () => void;
|
|
20
|
+
missingSubjectRejected: () => void;
|
|
21
|
+
extraFieldsAccepted: () => void;
|
|
22
|
+
};
|
|
23
|
+
decodeText: {
|
|
24
|
+
validJson: () => void;
|
|
25
|
+
keyOrderIndependent: () => void;
|
|
26
|
+
malformedJsonRejected: () => void;
|
|
27
|
+
ordinaryJsonRejected: () => void;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { assert, assertEq } from '../../asserts/module.f.js';
|
|
2
|
+
import { dialect, mediaType, isHash, validate, decodeText } from './module.f.js';
|
|
3
|
+
// Valid cbase32 hashes (round-tripped in fs/basen/cbase32/proof.f.ts): single
|
|
4
|
+
// cbase32 symbols, cheap to write inline here.
|
|
5
|
+
const h1 = '8';
|
|
6
|
+
const h2 = 'r';
|
|
7
|
+
const revisionOf = (extra) => ({
|
|
8
|
+
dialect,
|
|
9
|
+
subject: h1,
|
|
10
|
+
parents: [],
|
|
11
|
+
...extra,
|
|
12
|
+
});
|
|
13
|
+
export const proof = {
|
|
14
|
+
dialectAndMediaType: () => {
|
|
15
|
+
assertEq(dialect, 'vnd.fjs.revision');
|
|
16
|
+
assertEq(mediaType, 'application/vnd.fjs.revision+json');
|
|
17
|
+
},
|
|
18
|
+
isHash: {
|
|
19
|
+
validHash: () => assert(isHash(h1)),
|
|
20
|
+
httpsRejected: () => assert(!isHash('https://example.com/blob/abc')),
|
|
21
|
+
arbitraryStringRejected: () => assert(!isHash('not-a-hash!')),
|
|
22
|
+
},
|
|
23
|
+
validate: {
|
|
24
|
+
// Zero parents, no `snapshot`: `subject` is the fallback snapshot
|
|
25
|
+
// reference and must itself be a hash.
|
|
26
|
+
firstRevisionFallsBackToSubjectHash: () => {
|
|
27
|
+
const [t] = validate(revisionOf({}));
|
|
28
|
+
assertEq(t, 'ok');
|
|
29
|
+
},
|
|
30
|
+
// Zero parents, no `snapshot`, `subject` is not a hash: invalid.
|
|
31
|
+
firstRevisionRejectsNonHashSubject: () => {
|
|
32
|
+
const [t] = validate(revisionOf({ subject: 'my-config' }));
|
|
33
|
+
assertEq(t, 'error');
|
|
34
|
+
},
|
|
35
|
+
// `snapshot` present: `subject` may be an arbitrary string.
|
|
36
|
+
explicitSnapshotAllowsArbitrarySubject: () => {
|
|
37
|
+
const [t] = validate(revisionOf({ subject: 'my-config', snapshot: h2 }));
|
|
38
|
+
assertEq(t, 'ok');
|
|
39
|
+
},
|
|
40
|
+
// Exactly one parent, no `snapshot`: the parent's snapshot is inherited,
|
|
41
|
+
// and `subject` may be an arbitrary string.
|
|
42
|
+
singleParentInheritsSnapshot: () => {
|
|
43
|
+
const [t] = validate(revisionOf({ subject: 'my-config', parents: [h2] }));
|
|
44
|
+
assertEq(t, 'ok');
|
|
45
|
+
},
|
|
46
|
+
// More than one parent with no `snapshot`: invalid — no single parent
|
|
47
|
+
// snapshot to inherit, and falling back to `subject` would silently
|
|
48
|
+
// lose the merge result.
|
|
49
|
+
multiParentWithoutSnapshotRejected: () => {
|
|
50
|
+
const [t] = validate(revisionOf({ subject: 'my-config', parents: [h1, h2] }));
|
|
51
|
+
assertEq(t, 'error');
|
|
52
|
+
},
|
|
53
|
+
// A merge revision with an explicit `snapshot` is valid.
|
|
54
|
+
multiParentWithSnapshotAccepted: () => {
|
|
55
|
+
const [t, v] = validate(revisionOf({ subject: 'my-config', parents: [h1, h2], snapshot: h2 }));
|
|
56
|
+
assertEq(t, 'ok');
|
|
57
|
+
assertEq(v.snapshot, h2);
|
|
58
|
+
},
|
|
59
|
+
// `https://` bridge URLs are rejected wherever a hash is required.
|
|
60
|
+
httpsRejectedInParents: () => {
|
|
61
|
+
const [t] = validate(revisionOf({ subject: 'my-config', parents: ['https://example.com/x'], snapshot: h2 }));
|
|
62
|
+
assertEq(t, 'error');
|
|
63
|
+
},
|
|
64
|
+
httpsRejectedInSnapshot: () => {
|
|
65
|
+
const [t] = validate(revisionOf({ subject: 'my-config', snapshot: 'https://example.com/x' }));
|
|
66
|
+
assertEq(t, 'error');
|
|
67
|
+
},
|
|
68
|
+
httpsRejectedAsFallbackSubject: () => {
|
|
69
|
+
const [t] = validate(revisionOf({ subject: 'https://example.com/x' }));
|
|
70
|
+
assertEq(t, 'error');
|
|
71
|
+
},
|
|
72
|
+
// `archived` follows the presence-only `option(true)` idiom.
|
|
73
|
+
archivedAccepted: () => {
|
|
74
|
+
const [t] = validate(revisionOf({ archived: true }));
|
|
75
|
+
assertEq(t, 'ok');
|
|
76
|
+
},
|
|
77
|
+
// Wrong dialect tag: structural validation rejects it outright.
|
|
78
|
+
wrongDialectRejected: () => {
|
|
79
|
+
const [t] = validate({ dialect: 'vnd.fjs.other', subject: h1, parents: [] });
|
|
80
|
+
assertEq(t, 'error');
|
|
81
|
+
},
|
|
82
|
+
// Missing required fields: rejected.
|
|
83
|
+
missingSubjectRejected: () => {
|
|
84
|
+
const [t] = validate({ dialect, parents: [] });
|
|
85
|
+
assertEq(t, 'error');
|
|
86
|
+
},
|
|
87
|
+
// rtti structs are open: extra fields don't break validation — the
|
|
88
|
+
// additive forward-compatibility path the versioning rule relies on.
|
|
89
|
+
extraFieldsAccepted: () => {
|
|
90
|
+
const [t] = validate(revisionOf({ future: 'field' }));
|
|
91
|
+
assertEq(t, 'ok');
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
decodeText: {
|
|
95
|
+
validJson: () => {
|
|
96
|
+
const [t, v] = decodeText(JSON.stringify(revisionOf({ generation: 0 })));
|
|
97
|
+
assertEq(t, 'ok');
|
|
98
|
+
assertEq(v.subject, h1);
|
|
99
|
+
},
|
|
100
|
+
// Key order carries no meaning: detection parses the JSON and
|
|
101
|
+
// validates the parsed value, so `dialect` need not be first.
|
|
102
|
+
keyOrderIndependent: () => {
|
|
103
|
+
const text = `{"parents":[],"subject":"${h1}","dialect":"${dialect}"}`;
|
|
104
|
+
const [t] = decodeText(text);
|
|
105
|
+
assertEq(t, 'ok');
|
|
106
|
+
},
|
|
107
|
+
malformedJsonRejected: () => {
|
|
108
|
+
const [t] = decodeText('{not json');
|
|
109
|
+
assertEq(t, 'error');
|
|
110
|
+
},
|
|
111
|
+
// Ordinary JSON that isn't a revision at all falls through as an error
|
|
112
|
+
// here — `fs/media`'s composed detector is what falls back to the
|
|
113
|
+
// ordinary detector; this module only reports validity.
|
|
114
|
+
ordinaryJsonRejected: () => {
|
|
115
|
+
const [t] = decodeText('{"hello":"world"}');
|
|
116
|
+
assertEq(t, 'error');
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
};
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
*
|
|
33
33
|
* @module
|
|
34
34
|
*/
|
|
35
|
-
import { type Vec } from '
|
|
36
|
-
import type { Nullable } from '
|
|
37
|
-
import { type Effect, type Operation } from '
|
|
38
|
-
import type { List } from '
|
|
39
|
-
import type { IoResult } from '
|
|
40
|
-
import { type Utf8State } from '
|
|
35
|
+
import { type Vec } from '../../types/bit_vec/module.f.ts';
|
|
36
|
+
import type { Nullable } from '../../types/nullable/module.f.ts';
|
|
37
|
+
import { type Effect, type Operation } from '../../effects/module.f.ts';
|
|
38
|
+
import type { List } from '../../effects/list/module.f.ts';
|
|
39
|
+
import type { IoResult } from '../../effects/node/module.f.ts';
|
|
40
|
+
import { type Utf8State } from '../../text/utf8/module.f.ts';
|
|
41
41
|
/**
|
|
42
42
|
* Detects the MIME type of `bytes` from its leading magic-byte signature.
|
|
43
43
|
*
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
*
|
|
33
33
|
* @module
|
|
34
34
|
*/
|
|
35
|
-
import { msb, fromSentinel, length, u8List } from
|
|
36
|
-
import { iterable } from
|
|
37
|
-
import { pure } from
|
|
38
|
-
import { ok, error } from
|
|
39
|
-
import { isValidCodePoint, isTextCodePoint } from
|
|
40
|
-
import { utf8ByteToCodePointOp } from
|
|
35
|
+
import { msb, fromSentinel, length, u8List } from '../../types/bit_vec/module.f.js';
|
|
36
|
+
import { iterable } from '../../types/list/module.f.js';
|
|
37
|
+
import { pure } from '../../effects/module.f.js';
|
|
38
|
+
import { ok, error } from '../../types/result/module.f.js';
|
|
39
|
+
import { isValidCodePoint, isTextCodePoint } from '../../text/code_point/module.f.js';
|
|
40
|
+
import { utf8ByteToCodePointOp } from '../../text/utf8/module.f.js';
|
|
41
41
|
const { startsWith, removeFront } = msb;
|
|
42
42
|
// Each signature is written as a hex literal whose leading `1` nibble is a
|
|
43
43
|
// sentinel marking the start of the byte run (so leading zero bytes survive)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { assert, assertEq } from
|
|
2
|
-
import { msb, u8ListToVec, vec8, repeat, empty } from
|
|
3
|
-
import { decode } from
|
|
4
|
-
import { nonEmpty, empty as emptyList } from
|
|
5
|
-
import { ok } from
|
|
6
|
-
import { detect, detectStream, detectVec } from
|
|
1
|
+
import { assert, assertEq } from '../../asserts/module.f.js';
|
|
2
|
+
import { msb, u8ListToVec, vec8, repeat, empty } from '../../types/bit_vec/module.f.js';
|
|
3
|
+
import { decode } from '../../effects/module.f.js';
|
|
4
|
+
import { nonEmpty, empty as emptyList } from '../../effects/list/module.f.js';
|
|
5
|
+
import { ok } from '../../types/result/module.f.js';
|
|
6
|
+
import { detect, detectStream, detectVec } from './module.f.js';
|
|
7
7
|
// Builds a big-endian `Vec` from a list of byte values — mirrors how the CAS
|
|
8
8
|
// store would hold the leading bytes of a stored blob.
|
|
9
9
|
const bytes = (...b) => u8ListToVec(msb)(b);
|
package/fs/module.f.d.ts
ADDED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { compile } from
|
|
7
|
-
import { main as testMain } from
|
|
8
|
-
import { commands as casCommands } from
|
|
9
|
-
import { main as ciMain } from
|
|
10
|
-
import { import_ } from
|
|
11
|
-
import { dispatch } from
|
|
12
|
-
import { casMcpServer } from
|
|
13
|
-
import { pure } from
|
|
6
|
+
import { compile } from './djs/module.f.js';
|
|
7
|
+
import { main as testMain } from './emergent_testing/module.f.js';
|
|
8
|
+
import { commands as casCommands } from './cas/cli/module.f.js';
|
|
9
|
+
import { main as ciMain } from './ci/module.f.js';
|
|
10
|
+
import { import_ } from './effects/node/module.f.js';
|
|
11
|
+
import { dispatch } from './cli/module.f.js';
|
|
12
|
+
import { casMcpServer } from './cas/mcp/module.f.js';
|
|
13
|
+
import { pure } from './effects/module.f.js';
|
|
14
14
|
const commands = [
|
|
15
15
|
{
|
|
16
16
|
names: ['test', 't'],
|
package/fs/module.js
ADDED
package/fs/path/module.f.d.ts
CHANGED
|
@@ -38,3 +38,9 @@ export declare const join: (...list: readonly string[]) => string;
|
|
|
38
38
|
* E.g. `relativize('/repo', '/repo/fs/a.ts')` → `'./fs/a.ts'`.
|
|
39
39
|
*/
|
|
40
40
|
export declare const relativize: (base: string, path: string) => string;
|
|
41
|
+
/**
|
|
42
|
+
* Returns `true` when `prefix` is a strict ancestor of `path` in segment space:
|
|
43
|
+
* every segment of `prefix` matches the corresponding segment of `path`, and
|
|
44
|
+
* `path` has at least one additional segment.
|
|
45
|
+
*/
|
|
46
|
+
export declare const isProperPrefix: (prefix: readonly string[], path: readonly string[]) => boolean;
|
package/fs/path/module.f.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fold, last, take, length, concat as listConcat, toArray } from
|
|
2
|
-
import { join as listJoin, concat as stringConcat } from
|
|
1
|
+
import { fold, last, take, length, concat as listConcat, toArray } from '../types/list/module.f.js';
|
|
2
|
+
import { join as listJoin, concat as stringConcat } from '../types/string/module.f.js';
|
|
3
3
|
const foldNormalizeOp = input => state => {
|
|
4
4
|
switch (input) {
|
|
5
5
|
case '':
|
|
@@ -63,3 +63,9 @@ export const join = (...list) => list.join('/');
|
|
|
63
63
|
* E.g. `relativize('/repo', '/repo/fs/a.ts')` → `'./fs/a.ts'`.
|
|
64
64
|
*/
|
|
65
65
|
export const relativize = (base, path) => base !== '' && path.startsWith(base) ? `.${path.slice(base.length)}` : path;
|
|
66
|
+
/**
|
|
67
|
+
* Returns `true` when `prefix` is a strict ancestor of `path` in segment space:
|
|
68
|
+
* every segment of `prefix` matches the corresponding segment of `path`, and
|
|
69
|
+
* `path` has at least one additional segment.
|
|
70
|
+
*/
|
|
71
|
+
export const isProperPrefix = (prefix, path) => prefix.length < path.length && prefix.every((seg, i) => seg === path[i]);
|
package/fs/path/proof.f.d.ts
CHANGED
package/fs/path/proof.f.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { concat, join, normalize, relativize, toPosix } from "./module.f.js";
|
|
1
|
+
import { concat, isProperPrefix, join, normalize, relativize, toPosix } from "./module.f.js";
|
|
2
2
|
const normalizeTest = [
|
|
3
3
|
() => {
|
|
4
4
|
const norm = normalize("dir/file.json");
|
|
@@ -129,4 +129,42 @@ const toPosixTest = [
|
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
131
|
];
|
|
132
|
-
|
|
132
|
+
const isProperPrefixTest = [
|
|
133
|
+
() => {
|
|
134
|
+
const r = isProperPrefix(['a', 'b'], ['a', 'b', 'c']);
|
|
135
|
+
if (r !== true) {
|
|
136
|
+
throw r;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
() => {
|
|
140
|
+
const r = isProperPrefix(['a', 'b'], ['a', 'b']);
|
|
141
|
+
if (r !== false) {
|
|
142
|
+
throw r;
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
() => {
|
|
146
|
+
const r = isProperPrefix(['a', 'x'], ['a', 'b', 'c']);
|
|
147
|
+
if (r !== false) {
|
|
148
|
+
throw r;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
() => {
|
|
152
|
+
const r = isProperPrefix(['a', 'b', 'c'], ['a', 'b']);
|
|
153
|
+
if (r !== false) {
|
|
154
|
+
throw r;
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
() => {
|
|
158
|
+
const r = isProperPrefix([], ['a']);
|
|
159
|
+
if (r !== true) {
|
|
160
|
+
throw r;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
() => {
|
|
164
|
+
const r = isProperPrefix([], []);
|
|
165
|
+
if (r !== false) {
|
|
166
|
+
throw r;
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
export const proof = { normalizeTest, concatTest, joinTest, relativizeTest, toPosixTest, isProperPrefixTest };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { assert, assertEq } from
|
|
2
|
-
import { pure } from
|
|
3
|
-
import { defaultNodeProgramOptions, emptyState, virtual } from
|
|
4
|
-
import { main } from
|
|
1
|
+
import { assert, assertEq } from './asserts/module.f.js';
|
|
2
|
+
import { pure } from './effects/module.f.js';
|
|
3
|
+
import { defaultNodeProgramOptions, emptyState, virtual } from './effects/node/virtual/module.f.js';
|
|
4
|
+
import { main } from './module.f.js';
|
|
5
5
|
const makeOptions = (args) => ({ ...defaultNodeProgramOptions, args });
|
|
6
6
|
const run = (root) => (args) => virtual({ ...emptyState, root })(main(makeOptions(args)));
|
|
7
7
|
const appMain = ({ args }) => pure(args.length);
|
|
@@ -22,6 +22,12 @@ export const proof = {
|
|
|
22
22
|
// `run` strips the command and file name, so `main` sees two arguments
|
|
23
23
|
assertEq(code, 2);
|
|
24
24
|
},
|
|
25
|
+
mcp: () => {
|
|
26
|
+
// stdin is empty in the virtual environment, so the server sees EOF
|
|
27
|
+
// immediately and shuts down cleanly, exercising the `mcp` handler.
|
|
28
|
+
const [, code] = run({})(['mcp']);
|
|
29
|
+
assertEq(code, 0);
|
|
30
|
+
},
|
|
25
31
|
throw: {
|
|
26
32
|
runImportError: () => {
|
|
27
33
|
run({})(['run', 'missing.f.ts']);
|
package/fs/sul/id/module.f.js
CHANGED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
|
-
import { toArray } from
|
|
9
|
-
import { length, msb, uint, uintChunkList, unpack, vec } from
|
|
10
|
-
import { assertEq } from
|
|
11
|
-
import { utf8 } from
|
|
12
|
-
import { secp256r1 } from
|
|
13
|
-
import { base32 } from
|
|
14
|
-
import { literal3ToVec } from
|
|
15
|
-
import { log2 } from
|
|
16
|
-
import { asBase, asNominal } from
|
|
8
|
+
import { toArray } from '../../types/list/module.f.js';
|
|
9
|
+
import { length, msb, uint, uintChunkList, unpack, vec } from '../../types/bit_vec/module.f.js';
|
|
10
|
+
import { assertEq } from '../../asserts/module.f.js';
|
|
11
|
+
import { utf8 } from '../../text/module.f.js';
|
|
12
|
+
import { secp256r1 } from '../../crypto/secp/module.f.js';
|
|
13
|
+
import { base32 } from '../../crypto/sha2/module.f.js';
|
|
14
|
+
import { literal3ToVec } from '../level/literal/module.f.js';
|
|
15
|
+
import { log2 } from '../../types/bigint/module.f.js';
|
|
16
|
+
import { asBase, asNominal } from '../../types/nominal/module.f.js';
|
|
17
17
|
// 32 bytes = 256 bits.
|
|
18
18
|
//
|
|
19
19
|
// 0 1
|
package/fs/sul/id/proof.f.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { assert, assertEq } from
|
|
2
|
-
import { mask } from
|
|
3
|
-
import { vec } from
|
|
4
|
-
import { compress, hashId, isHash, isRaw, level3Id, rawId } from
|
|
5
|
-
import { asBase } from
|
|
1
|
+
import { assert, assertEq } from '../../asserts/module.f.js';
|
|
2
|
+
import { mask } from '../../types/bigint/module.f.js';
|
|
3
|
+
import { vec } from '../../types/bit_vec/module.f.js';
|
|
4
|
+
import { compress, hashId, isHash, isRaw, level3Id, rawId } from './module.f.js';
|
|
5
|
+
import { asBase } from '../../types/nominal/module.f.js';
|
|
6
6
|
// literal3ToVec bit patterns for symbols used below:
|
|
7
7
|
// 0 → vec(8n)(0n) 4 → vec(8n)(3n)
|
|
8
8
|
// 1 → vec(8n)(1n) 10 → vec(8n)(5n)
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { emptyState, patriciaTrie } from
|
|
7
|
-
import { compress } from
|
|
8
|
-
import { asBase } from
|
|
6
|
+
import { emptyState, patriciaTrie } from '../../../types/patricia_trie/module.f.js';
|
|
7
|
+
import { compress } from '../../id/module.f.js';
|
|
8
|
+
import { asBase } from '../../../types/nominal/module.f.js';
|
|
9
9
|
/**
|
|
10
10
|
* Returns a streaming encoder for hash-level symbols.
|
|
11
11
|
*
|