functionalscript 0.44.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/fjs/asn.1/module.f.mjs +8 -1
- package/fjs/asn.1/proof.f.d.mts +15 -0
- package/fjs/asn.1/proof.f.mjs +31 -3
- package/fjs/asserts/module.f.d.mts +14 -0
- package/fjs/asserts/module.f.mjs +20 -0
- package/fjs/asserts/proof.f.d.mts +3 -0
- package/fjs/asserts/proof.f.mjs +15 -1
- package/fjs/basen/base128/module.f.mjs +22 -5
- package/fjs/basen/base128/proof.f.d.mts +3 -1
- package/fjs/basen/base128/proof.f.mjs +3 -1
- package/fjs/basen/base64/module.f.mjs +7 -3
- package/fjs/basen/base64/proof.f.d.mts +3 -1
- package/fjs/basen/base64/proof.f.mjs +7 -4
- package/fjs/basen/cbase32/proof.f.d.mts +3 -1
- package/fjs/basen/cbase32/proof.f.mjs +3 -1
- package/fjs/bnf/data/module.f.d.mts +32 -6
- package/fjs/bnf/data/module.f.mjs +112 -7
- package/fjs/bnf/data/proof.f.d.mts +2 -0
- package/fjs/bnf/data/proof.f.mjs +74 -4
- package/fjs/bnf/data/types.d.ts +20 -2
- package/fjs/bnf/descent/module.f.d.mts +34 -5
- package/fjs/bnf/descent/module.f.mjs +176 -54
- package/fjs/bnf/descent/proof.f.d.mts +3 -0
- package/fjs/bnf/descent/proof.f.mjs +189 -27
- package/fjs/bnf/descent/types.d.ts +24 -18
- package/fjs/bnf/ll1/module.f.d.mts +74 -6
- package/fjs/bnf/ll1/module.f.mjs +297 -95
- package/fjs/bnf/ll1/proof.f.d.mts +17 -2
- package/fjs/bnf/ll1/proof.f.mjs +372 -137
- package/fjs/bnf/ll1/types.d.ts +25 -27
- package/fjs/bnf/matcher/module.f.d.mts +66 -0
- package/fjs/bnf/matcher/module.f.mjs +81 -0
- package/fjs/bnf/matcher/proof.f.d.mts +10 -0
- package/fjs/bnf/matcher/proof.f.mjs +79 -0
- package/fjs/bnf/matcher/types.d.ts +54 -0
- package/fjs/bnf/module.f.d.mts +15 -6
- package/fjs/bnf/module.f.mjs +52 -11
- package/fjs/bnf/proof.f.d.mts +2 -0
- package/fjs/bnf/proof.f.mjs +79 -3
- package/fjs/bnf/testlib.f.d.mts +31 -0
- package/fjs/bnf/testlib.f.mjs +80 -0
- package/fjs/bnf/token_symbol/module.f.d.mts +4 -3
- package/fjs/bnf/token_symbol/module.f.mjs +7 -6
- package/fjs/bnf/token_symbol/proof.f.mjs +5 -2
- package/fjs/bnf/token_symbol/types.d.ts +2 -2
- package/fjs/bnf/types.d.ts +10 -3
- package/fjs/cas/cli/module.f.d.mts +1 -1
- package/fjs/cas/cli/module.f.mjs +14 -20
- package/fjs/cas/cli/proof.f.d.mts +4 -4
- package/fjs/cas/cli/proof.f.mjs +47 -34
- package/fjs/cas/evo/module.f.d.mts +71 -31
- package/fjs/cas/evo/module.f.mjs +223 -138
- package/fjs/cas/evo/proof.f.d.mts +10 -1
- package/fjs/cas/evo/proof.f.mjs +311 -226
- package/fjs/cas/evo/types.d.ts +45 -9
- package/fjs/cas/module.f.d.mts +18 -32
- package/fjs/cas/module.f.mjs +164 -176
- package/fjs/cas/proof.f.d.mts +11 -8
- package/fjs/cas/proof.f.mjs +259 -147
- package/fjs/cas/types.d.ts +24 -10
- package/fjs/ci/common/module.f.d.mts +1 -1
- package/fjs/ci/common/module.f.mjs +1 -0
- package/fjs/ci/config/module.f.d.mts +3 -3
- package/fjs/ci/config/module.f.mjs +3 -3
- package/fjs/ci/module.f.d.mts +5 -5
- package/fjs/ci/module.f.mjs +8 -7
- package/fjs/ci/nix/module.f.d.mts +7 -5
- package/fjs/ci/nix/module.f.mjs +13 -12
- package/fjs/ci/nix/proof.f.d.mts +2 -0
- package/fjs/ci/nix/proof.f.mjs +4 -4
- package/fjs/ci/proof.f.d.mts +4 -2
- package/fjs/ci/proof.f.mjs +13 -8
- package/fjs/cli/module.f.d.mts +4 -6
- package/fjs/cli/module.f.mjs +4 -8
- package/fjs/cli/proof.f.d.mts +4 -2
- package/fjs/cli/proof.f.mjs +21 -18
- package/fjs/cli/types.d.ts +2 -3
- package/fjs/common/monoid/module.f.d.mts +70 -12
- package/fjs/common/monoid/module.f.mjs +177 -14
- package/fjs/common/monoid/proof.f.d.mts +12 -1
- package/fjs/common/monoid/proof.f.mjs +88 -4
- package/fjs/common/monoid/types.d.ts +23 -0
- package/fjs/crypto/hmac/module.f.mjs +2 -2
- package/fjs/crypto/secp/proof.f.d.mts +3 -1
- package/fjs/crypto/secp/proof.f.mjs +3 -1
- package/fjs/crypto/sha2/module.f.mjs +3 -1
- package/fjs/crypto/sha2/proof.f.d.mts +4 -1
- package/fjs/crypto/sha2/proof.f.mjs +27 -1
- package/fjs/crypto/sha2/types.d.ts +11 -0
- package/fjs/crypto/sign/module.f.mjs +2 -2
- package/fjs/crypto/vdf/module.f.d.mts +23 -4
- package/fjs/crypto/vdf/module.f.mjs +6 -4
- package/fjs/dev/module.f.d.mts +17 -5
- package/fjs/dev/module.f.mjs +60 -30
- package/fjs/dev/update/module.f.d.mts +11 -3
- package/fjs/dev/update/module.f.mjs +16 -11
- package/fjs/dev/update/proof.f.d.mts +4 -3
- package/fjs/dev/update/proof.f.mjs +11 -7
- package/fjs/djs/module.f.d.mts +13 -5
- package/fjs/djs/module.f.mjs +31 -16
- package/fjs/djs/parser/module.f.d.mts +16 -3
- package/fjs/djs/parser/module.f.mjs +135 -16
- package/fjs/djs/parser/proof.f.d.mts +8 -1
- package/fjs/djs/parser/proof.f.mjs +287 -13
- package/fjs/djs/parser/types.d.ts +7 -1
- package/fjs/djs/proof.f.d.mts +20 -1
- package/fjs/djs/proof.f.mjs +190 -12
- package/fjs/djs/serializer/module.f.d.mts +25 -8
- package/fjs/djs/serializer/module.f.mjs +66 -19
- package/fjs/djs/serializer/proof.f.d.mts +5 -0
- package/fjs/djs/serializer/proof.f.mjs +23 -1
- package/fjs/djs/tokenizer/module.f.d.mts +17 -5
- package/fjs/djs/tokenizer/module.f.mjs +120 -73
- package/fjs/djs/tokenizer/proof.f.d.mts +3 -0
- package/fjs/djs/tokenizer/proof.f.mjs +86 -67
- package/fjs/djs/transpiler/module.f.d.mts +13 -19
- package/fjs/djs/transpiler/module.f.mjs +79 -72
- package/fjs/djs/transpiler/types.d.ts +29 -0
- package/fjs/djs/types.d.ts +7 -1
- package/fjs/effects/list/module.f.d.mts +23 -12
- package/fjs/effects/list/module.f.mjs +16 -12
- package/fjs/effects/list/types.d.ts +27 -7
- package/fjs/effects/memory/module.f.d.mts +6 -3
- package/fjs/effects/memory/module.f.mjs +7 -7
- package/fjs/effects/memory/proof.f.d.mts +4 -0
- package/fjs/effects/memory/proof.f.mjs +18 -9
- package/fjs/effects/memory/types.d.ts +4 -3
- package/fjs/effects/mock/module.f.d.mts +28 -4
- package/fjs/effects/mock/module.f.mjs +39 -5
- package/fjs/effects/mock/types.d.ts +10 -1
- package/fjs/effects/module.d.mts +12 -2
- package/fjs/effects/module.f.d.mts +469 -281
- package/fjs/effects/module.f.mjs +538 -299
- package/fjs/effects/module.mjs +11 -2
- package/fjs/effects/node/memory/module.d.mts +7 -2
- package/fjs/effects/node/memory/module.mjs +9 -5
- package/fjs/effects/node/memory/proof.d.mts +2 -0
- package/fjs/effects/node/memory/proof.mjs +12 -5
- package/fjs/effects/node/module.d.mts +8 -2
- package/fjs/effects/node/module.f.d.mts +183 -43
- package/fjs/effects/node/module.f.mjs +260 -62
- package/fjs/effects/node/module.mjs +61 -50
- package/fjs/effects/node/proof.f.d.mts +28 -2
- package/fjs/effects/node/proof.f.mjs +161 -42
- package/fjs/effects/node/types.d.ts +106 -18
- package/fjs/effects/node/virtual/module.f.d.mts +21 -4
- package/fjs/effects/node/virtual/module.f.mjs +113 -70
- package/fjs/effects/node/virtual/proof.f.d.mts +31 -2
- package/fjs/effects/node/virtual/proof.f.mjs +201 -8
- package/fjs/effects/proof.f.d.mts +69 -34
- package/fjs/effects/proof.f.mjs +413 -130
- package/fjs/effects/types.d.ts +161 -33
- package/fjs/emergent_testing/all.test.d.mts +8 -0
- package/fjs/emergent_testing/all.test.mjs +14 -0
- package/fjs/emergent_testing/module.f.d.mts +22 -10
- package/fjs/emergent_testing/module.f.mjs +119 -85
- package/fjs/emergent_testing/proof.f.d.mts +21 -7
- package/fjs/emergent_testing/proof.f.mjs +169 -35
- package/fjs/emergent_testing/types.d.ts +22 -4
- package/fjs/fsc/bnf.f.d.mts +3 -0
- package/fjs/fsc/bnf.f.mjs +4 -1
- package/fjs/fsc/json.f.d.mts +3 -0
- package/fjs/fsc/json.f.mjs +4 -1
- package/fjs/fsc/module.f.d.mts +9 -0
- package/fjs/fsc/module.f.mjs +5 -3
- package/fjs/fsm/module.f.d.mts +14 -4
- package/fjs/fsm/module.f.mjs +54 -37
- package/fjs/fsm/proof.f.d.mts +5 -0
- package/fjs/fsm/proof.f.mjs +87 -115
- package/fjs/js/keywords/module.f.d.mts +52 -0
- package/fjs/js/keywords/module.f.mjs +72 -0
- package/fjs/{emergent_testing/scenarios/return-value.pass.d.ts → js/keywords/proof.f.d.mts} +1 -1
- package/fjs/js/keywords/proof.f.mjs +13 -0
- package/fjs/js/tokenizer/module.f.d.mts +30 -3
- package/fjs/js/tokenizer/module.f.mjs +161 -211
- package/fjs/js/tokenizer/proof.f.d.mts +1 -0
- package/fjs/js/tokenizer/proof.f.mjs +54 -24
- package/fjs/js/tokenizer/types.d.ts +33 -24
- package/fjs/mcp/cas/module.f.d.mts +1 -6
- package/fjs/mcp/cas/module.f.mjs +55 -51
- package/fjs/mcp/cas/proof.f.d.mts +15 -0
- package/fjs/mcp/cas/proof.f.mjs +174 -0
- package/fjs/mcp/evo/module.f.d.mts +19 -10
- package/fjs/mcp/evo/module.f.mjs +48 -27
- package/fjs/mcp/evo/proof.f.d.mts +6 -1
- package/fjs/mcp/evo/proof.f.mjs +115 -31
- package/fjs/mcp/module.f.d.mts +39 -4
- package/fjs/mcp/module.f.mjs +14 -12
- package/fjs/mcp/proof.f.d.mts +13 -1
- package/fjs/mcp/proof.f.mjs +120 -61
- package/fjs/media/html/module.f.d.mts +12 -0
- package/fjs/media/html/module.f.mjs +7 -5
- package/fjs/media/html/proof.f.d.mts +3 -0
- package/fjs/media/html/proof.f.mjs +4 -1
- package/fjs/media/json/extended/module.f.d.mts +82 -0
- package/fjs/media/json/extended/module.f.mjs +153 -0
- package/fjs/media/json/extended/proof.f.d.mts +42 -0
- package/fjs/media/json/extended/proof.f.mjs +127 -0
- package/fjs/media/json/extended/types.d.ts +23 -0
- package/fjs/media/json/module.f.d.mts +8 -2
- package/fjs/media/json/module.f.mjs +43 -41
- package/fjs/media/json/number/module.f.d.mts +59 -0
- package/fjs/media/json/number/module.f.mjs +136 -0
- package/fjs/media/json/number/proof.f.d.mts +24 -0
- package/fjs/media/json/number/proof.f.mjs +86 -0
- package/fjs/media/json/number/types.d.ts +28 -0
- package/fjs/media/json/parser/module.f.d.mts +25 -13
- package/fjs/media/json/parser/module.f.mjs +133 -72
- package/fjs/media/json/parser/proof.f.d.mts +5 -0
- package/fjs/media/json/parser/proof.f.mjs +31 -1
- package/fjs/media/json/parser/types.d.ts +38 -14
- package/fjs/media/json/rtti/module.f.d.mts +1 -1
- package/fjs/media/json/rtti/module.f.mjs +1 -1
- package/fjs/media/json/rtti/proof.f.d.mts +3 -0
- package/fjs/media/json/rtti/proof.f.mjs +12 -12
- package/fjs/media/json/schema/module.f.d.mts +61 -27
- package/fjs/media/json/schema/module.f.mjs +218 -63
- package/fjs/media/json/schema/proof.f.d.mts +41 -1
- package/fjs/media/json/schema/proof.f.mjs +220 -9
- package/fjs/media/json/serializer/module.f.d.mts +44 -0
- package/fjs/media/json/serializer/module.f.mjs +71 -9
- package/fjs/media/json/tokenizer/module.f.d.mts +8 -0
- package/fjs/media/json/tokenizer/module.f.mjs +31 -8
- package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
- package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
- package/fjs/media/json/types.d.ts +36 -10
- package/fjs/media/lock/module.f.d.mts +100 -0
- package/fjs/media/lock/module.f.mjs +125 -0
- package/fjs/media/lock/proof.f.d.mts +33 -0
- package/fjs/media/lock/proof.f.mjs +196 -0
- package/fjs/media/lock/types.d.ts +15 -0
- package/fjs/media/module.f.d.mts +6 -5
- package/fjs/media/module.f.mjs +8 -7
- package/fjs/media/nix/module.f.d.mts +13 -0
- package/fjs/media/nix/module.f.mjs +4 -2
- package/fjs/media/nix/proof.f.d.mts +3 -0
- package/fjs/media/nix/proof.f.mjs +4 -1
- package/fjs/media/note/module.f.d.mts +121 -0
- package/fjs/media/note/module.f.mjs +131 -0
- package/fjs/media/note/proof.f.d.mts +29 -0
- package/fjs/media/note/proof.f.mjs +150 -0
- package/fjs/media/note/types.d.ts +10 -0
- package/fjs/media/proof.f.d.mts +4 -1
- package/fjs/media/proof.f.mjs +40 -21
- package/fjs/media/revision/module.f.d.mts +83 -7
- package/fjs/media/revision/module.f.mjs +127 -23
- package/fjs/media/revision/proof.f.d.mts +14 -0
- package/fjs/media/revision/proof.f.mjs +93 -2
- package/fjs/media/revision/types.d.ts +34 -5
- package/fjs/media/type/module.f.d.mts +53 -18
- package/fjs/media/type/module.f.mjs +87 -97
- package/fjs/media/type/proof.f.d.mts +17 -0
- package/fjs/media/type/proof.f.mjs +73 -13
- package/fjs/module.d.mts +5 -0
- package/fjs/module.f.d.mts +8 -0
- package/fjs/module.f.mjs +33 -10
- package/fjs/module.mjs +6 -0
- package/fjs/nanvm/proof.f.mjs +3 -3
- package/fjs/nanvm/rust/module.f.mjs +1 -1
- package/fjs/nanvm/update/module.f.d.mts +4 -4
- package/fjs/nanvm/update/module.f.mjs +8 -9
- package/fjs/nanvm/update/proof.f.mjs +4 -3
- package/fjs/path/module.f.d.mts +8 -0
- package/fjs/path/module.f.mjs +4 -2
- package/fjs/proof.f.d.mts +7 -3
- package/fjs/proof.f.mjs +41 -12
- package/fjs/protocol/json_rpc/module.f.d.mts +3 -3
- package/fjs/protocol/json_rpc/module.f.mjs +10 -7
- package/fjs/protocol/json_rpc/proof.f.d.mts +6 -0
- package/fjs/protocol/json_rpc/proof.f.mjs +26 -5
- package/fjs/protocol/mcp/module.f.d.mts +10 -13
- package/fjs/protocol/mcp/module.f.mjs +74 -58
- package/fjs/protocol/mcp/proof.f.d.mts +16 -3
- package/fjs/protocol/mcp/proof.f.mjs +224 -69
- package/fjs/protocol/mcp/stdio/module.f.d.mts +17 -11
- package/fjs/protocol/mcp/stdio/module.f.mjs +39 -33
- package/fjs/protocol/mcp/stdio/proof.f.d.mts +5 -3
- package/fjs/protocol/mcp/stdio/proof.f.mjs +46 -13
- package/fjs/protocol/mcp/stdio/types.d.ts +14 -0
- package/fjs/protocol/mcp/types.d.ts +17 -6
- package/fjs/sul/id/module.f.d.mts +4 -0
- package/fjs/sul/id/module.f.mjs +6 -6
- package/fjs/sul/level/hash/module.f.d.mts +4 -1
- package/fjs/sul/level/hash/module.f.mjs +6 -4
- package/fjs/sul/level/hash/proof.f.d.mts +4 -0
- package/fjs/sul/level/hash/proof.f.mjs +7 -4
- package/fjs/sul/level/literal/module.f.d.mts +4 -1
- package/fjs/sul/level/literal/module.f.mjs +4 -3
- package/fjs/sul/level/literal/proof.f.d.mts +3 -0
- package/fjs/sul/level/literal/proof.f.mjs +4 -1
- package/fjs/sul/module.f.d.mts +5 -3
- package/fjs/sul/module.f.mjs +23 -17
- package/fjs/sul/proof.f.d.mts +4 -0
- package/fjs/sul/proof.f.mjs +5 -2
- package/fjs/text/ascii/module.f.d.mts +26 -0
- package/fjs/text/ascii/module.f.mjs +49 -1
- package/fjs/text/ascii/proof.f.d.mts +10 -0
- package/fjs/text/ascii/proof.f.mjs +33 -1
- package/fjs/text/code_point/module.f.d.mts +29 -3
- package/fjs/text/code_point/module.f.mjs +32 -9
- package/fjs/text/code_point/proof.f.d.mts +2 -0
- package/fjs/text/code_point/proof.f.mjs +39 -0
- package/fjs/text/module.f.d.mts +4 -2
- package/fjs/text/module.f.mjs +4 -3
- package/fjs/text/proof.f.d.mts +3 -0
- package/fjs/text/proof.f.mjs +4 -1
- package/fjs/text/sgr/module.f.d.mts +6 -5
- package/fjs/text/sgr/module.f.mjs +8 -8
- package/fjs/text/sgr/proof.f.d.mts +3 -0
- package/fjs/text/sgr/proof.f.mjs +4 -1
- package/fjs/text/utf16/module.f.d.mts +5 -1
- package/fjs/text/utf16/module.f.mjs +38 -35
- package/fjs/text/utf16/proof.f.d.mts +3 -1
- package/fjs/text/utf16/proof.f.mjs +19 -2
- package/fjs/text/utf8/module.f.d.mts +9 -5
- package/fjs/text/utf8/module.f.mjs +20 -20
- package/fjs/types/array/module.f.d.mts +2 -1
- package/fjs/types/array/module.f.mjs +16 -4
- package/fjs/types/bigfloat/module.f.d.mts +21 -1
- package/fjs/types/bigfloat/module.f.mjs +33 -7
- package/fjs/types/bigfloat/proof.f.d.mts +1 -0
- package/fjs/types/bigfloat/proof.f.mjs +46 -0
- package/fjs/types/bigint/module.f.d.mts +4 -0
- package/fjs/types/bigint/module.f.mjs +4 -3
- package/fjs/types/bit_vec/module.f.d.mts +10 -16
- package/fjs/types/bit_vec/module.f.mjs +99 -95
- package/fjs/types/bit_vec/proof.f.d.mts +6 -0
- package/fjs/types/bit_vec/proof.f.mjs +31 -5
- package/fjs/types/btree/find/module.f.d.mts +5 -1
- package/fjs/types/btree/find/module.f.mjs +5 -5
- package/fjs/types/btree/find/proof.f.d.mts +5 -1
- package/fjs/types/btree/find/proof.f.mjs +5 -3
- package/fjs/types/btree/module.f.d.mts +3 -0
- package/fjs/types/btree/module.f.mjs +3 -2
- package/fjs/types/btree/proof.f.d.mts +5 -1
- package/fjs/types/btree/proof.f.mjs +5 -3
- package/fjs/types/btree/remove/module.f.d.mts +11 -0
- package/fjs/types/btree/remove/module.f.mjs +10 -10
- package/fjs/types/btree/remove/proof.f.d.mts +3 -0
- package/fjs/types/btree/remove/proof.f.mjs +4 -1
- package/fjs/types/btree/set/module.f.d.mts +9 -0
- package/fjs/types/btree/set/module.f.mjs +14 -17
- package/fjs/types/btree/set/proof.f.d.mts +3 -0
- package/fjs/types/btree/set/proof.f.mjs +16 -1
- package/fjs/types/btree/types/module.f.d.mts +2 -1
- package/fjs/types/btree/types/module.f.mjs +2 -1
- package/fjs/types/byte_set/module.f.d.mts +20 -4
- package/fjs/types/byte_set/module.f.mjs +18 -8
- package/fjs/types/byte_set/proof.f.d.mts +3 -1
- package/fjs/types/byte_set/proof.f.mjs +10 -8
- package/fjs/types/function/compare/module.f.d.mts +3 -2
- package/fjs/types/function/compare/module.f.mjs +13 -5
- package/fjs/types/function/module.f.d.mts +8 -1
- package/fjs/types/function/module.f.mjs +8 -1
- package/fjs/types/function/operator/module.f.d.mts +2 -1
- package/fjs/types/function/operator/module.f.mjs +2 -2
- package/fjs/types/list/module.f.d.mts +10 -3
- package/fjs/types/list/module.f.mjs +6 -4
- package/fjs/types/list/proof.f.d.mts +4 -1
- package/fjs/types/list/proof.f.mjs +4 -2
- package/fjs/types/nibble_set/module.f.d.mts +2 -1
- package/fjs/types/nibble_set/module.f.mjs +2 -2
- package/fjs/types/nominal/module.f.d.mts +2 -1
- package/fjs/types/nominal/module.f.mjs +2 -1
- package/fjs/types/nullable/module.f.d.mts +26 -6
- package/fjs/types/nullable/module.f.mjs +25 -6
- package/fjs/types/nullable/proof.f.d.mts +4 -0
- package/fjs/types/nullable/proof.f.mjs +15 -0
- package/fjs/types/number/module.f.d.mts +4 -0
- package/fjs/types/number/module.f.mjs +4 -3
- package/fjs/types/object/module.f.d.mts +26 -4
- package/fjs/types/object/module.f.mjs +26 -7
- package/fjs/types/object/proof.f.d.mts +5 -2
- package/fjs/types/object/proof.f.mjs +6 -3
- package/fjs/types/object/structurally_same/module.f.d.mts +39 -0
- package/fjs/types/object/structurally_same/module.f.mjs +59 -0
- package/fjs/types/object/structurally_same/proof.f.d.mts +6 -0
- package/fjs/types/object/structurally_same/proof.f.mjs +73 -0
- package/fjs/types/ordered_map/module.f.d.mts +5 -0
- package/fjs/types/ordered_map/module.f.mjs +5 -4
- package/fjs/types/ordered_map/proof.f.d.mts +3 -0
- package/fjs/types/ordered_map/proof.f.mjs +4 -1
- package/fjs/types/patricia_trie/module.f.d.mts +2 -1
- package/fjs/types/patricia_trie/module.f.mjs +27 -12
- package/fjs/types/patricia_trie/proof.f.d.mts +3 -0
- package/fjs/types/patricia_trie/proof.f.mjs +4 -1
- package/fjs/types/prime_field/module.f.d.mts +3 -1
- package/fjs/types/prime_field/module.f.mjs +3 -2
- package/fjs/types/range/module.f.d.mts +2 -1
- package/fjs/types/range/module.f.mjs +2 -2
- package/fjs/types/range_map/module.f.d.mts +6 -0
- package/fjs/types/range_map/module.f.mjs +6 -9
- package/fjs/types/range_map/proof.f.d.mts +5 -1
- package/fjs/types/range_map/proof.f.mjs +5 -3
- package/fjs/types/range_set/module.f.d.mts +5 -0
- package/fjs/types/range_set/module.f.mjs +1 -0
- package/fjs/types/range_set/proof.f.d.mts +3 -0
- package/fjs/types/range_set/proof.f.mjs +4 -1
- package/fjs/types/result/module.d.mts +8 -0
- package/fjs/types/result/module.f.d.mts +37 -7
- package/fjs/types/result/module.f.mjs +38 -8
- package/fjs/types/result/module.mjs +9 -1
- package/fjs/types/result/proof.f.d.mts +5 -0
- package/fjs/types/result/proof.f.mjs +22 -2
- package/fjs/types/rtti/common/module.f.d.mts +44 -14
- package/fjs/types/rtti/common/module.f.mjs +42 -36
- package/fjs/types/rtti/common/proof.f.d.mts +4 -0
- package/fjs/types/rtti/common/proof.f.mjs +10 -7
- package/fjs/types/rtti/data/module.f.d.mts +134 -0
- package/fjs/types/rtti/data/module.f.mjs +1053 -0
- package/fjs/types/rtti/data/proof.f.d.mts +76 -0
- package/fjs/types/rtti/data/proof.f.mjs +589 -0
- package/fjs/types/rtti/data/types.d.ts +83 -0
- package/fjs/types/rtti/module.f.d.mts +12 -5
- package/fjs/types/rtti/module.f.mjs +7 -5
- package/fjs/types/rtti/parse/module.f.d.mts +48 -0
- package/fjs/types/rtti/parse/module.f.mjs +45 -34
- package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
- package/fjs/types/rtti/parse/proof.f.mjs +51 -52
- package/fjs/types/rtti/proof.f.d.mts +3 -1
- package/fjs/types/rtti/proof.f.mjs +6 -2
- package/fjs/types/rtti/ts/module.f.d.mts +68 -9
- package/fjs/types/rtti/ts/module.f.mjs +258 -30
- package/fjs/types/rtti/ts/proof.f.d.mts +44 -0
- package/fjs/types/rtti/ts/proof.f.mjs +152 -4
- package/fjs/types/rtti/ts/types.d.ts +25 -1
- package/fjs/types/rtti/types.d.ts +40 -0
- package/fjs/types/sorted_list/module.f.d.mts +4 -1
- package/fjs/types/sorted_list/module.f.mjs +21 -12
- package/fjs/types/sorted_list/proof.f.d.mts +3 -1
- package/fjs/types/sorted_list/proof.f.mjs +3 -1
- package/fjs/types/sorted_set/module.f.d.mts +49 -1
- package/fjs/types/sorted_set/module.f.mjs +26 -2
- package/fjs/types/sorted_set/proof.f.d.mts +4 -1
- package/fjs/types/sorted_set/proof.f.mjs +19 -2
- package/fjs/types/string/module.f.d.mts +4 -0
- package/fjs/types/string/module.f.mjs +4 -3
- package/fjs/types/string_set/module.f.d.mts +3 -1
- package/fjs/types/string_set/module.f.mjs +3 -2
- package/fjs/types/ts/module.f.d.mts +2 -0
- package/fjs/types/ts/module.f.mjs +3 -3
- package/fjs/types/ts/proof.f.d.mts +1 -0
- package/fjs/types/ts/proof.f.mjs +5 -0
- package/fjs/types/ts/types.d.ts +6 -1
- package/fjs/types/uint8array/module.f.d.mts +13 -2
- package/fjs/types/uint8array/module.f.mjs +20 -15
- package/fjs/website/module.f.d.mts +5 -2
- package/fjs/website/module.f.mjs +6 -8
- package/fjs/website/proof.f.mjs +2 -1
- package/package.json +3 -3
- package/fjs/asn.1/types.js +0 -6
- package/fjs/asserts/types.js +0 -6
- package/fjs/basen/types.js +0 -6
- package/fjs/bnf/data/types.js +0 -6
- package/fjs/bnf/descent/types.js +0 -6
- package/fjs/bnf/ll1/types.js +0 -6
- package/fjs/bnf/token_symbol/types.js +0 -6
- package/fjs/bnf/types.js +0 -6
- package/fjs/cas/evo/types.js +0 -7
- package/fjs/cas/types.js +0 -6
- package/fjs/ci/common/types.js +0 -1
- package/fjs/ci/nix/types.js +0 -6
- package/fjs/ci/types.js +0 -6
- package/fjs/cli/types.js +0 -6
- package/fjs/common/monoid/types.js +0 -6
- package/fjs/crypto/pow/types.js +0 -6
- package/fjs/crypto/secp/types.js +0 -6
- package/fjs/crypto/sha2/types.js +0 -6
- package/fjs/crypto/sign/types.js +0 -6
- package/fjs/crypto/vdf/types.js +0 -6
- package/fjs/dev/package_json/module.f.d.mts +0 -32
- package/fjs/dev/package_json/module.f.mjs +0 -37
- package/fjs/dev/package_json/proof.f.d.mts +0 -6
- package/fjs/dev/package_json/proof.f.mjs +0 -32
- package/fjs/dev/types.js +0 -6
- package/fjs/djs/ast/types.js +0 -8
- package/fjs/djs/parser/types.js +0 -7
- package/fjs/djs/tokenizer/types.js +0 -7
- package/fjs/djs/types.js +0 -7
- package/fjs/effects/eff/module.f.d.mts +0 -10
- package/fjs/effects/eff/module.f.mjs +0 -68
- package/fjs/effects/eff/proof.f.d.mts +0 -12
- package/fjs/effects/eff/proof.f.mjs +0 -66
- package/fjs/effects/eff/types.d.ts +0 -71
- package/fjs/effects/eff/types.js +0 -6
- package/fjs/effects/list/types.js +0 -6
- package/fjs/effects/memory/types.js +0 -6
- package/fjs/effects/mock/types.js +0 -6
- package/fjs/effects/node/types.js +0 -6
- package/fjs/effects/node/virtual/types.js +0 -7
- package/fjs/effects/types.js +0 -6
- package/fjs/emergent_testing/all.test.d.ts +0 -1
- package/fjs/emergent_testing/all.test.js +0 -5
- package/fjs/emergent_testing/scenarios/all.d.ts +0 -1
- package/fjs/emergent_testing/scenarios/all.js +0 -1
- package/fjs/emergent_testing/scenarios/async-subtests.fail.d.ts +0 -6
- package/fjs/emergent_testing/scenarios/async-subtests.fail.js +0 -9
- package/fjs/emergent_testing/scenarios/async-subtests.pass.d.ts +0 -6
- package/fjs/emergent_testing/scenarios/async-subtests.pass.js +0 -9
- package/fjs/emergent_testing/scenarios/async.fail.d.ts +0 -3
- package/fjs/emergent_testing/scenarios/async.fail.js +0 -6
- package/fjs/emergent_testing/scenarios/async.pass.d.ts +0 -3
- package/fjs/emergent_testing/scenarios/async.pass.js +0 -5
- package/fjs/emergent_testing/scenarios/fail.fail.d.ts +0 -3
- package/fjs/emergent_testing/scenarios/fail.fail.js +0 -3
- package/fjs/emergent_testing/scenarios/return-value.pass.js +0 -4
- package/fjs/emergent_testing/scenarios/thenable.pass.d.ts +0 -5
- package/fjs/emergent_testing/scenarios/thenable.pass.js +0 -11
- package/fjs/emergent_testing/scenarios/thenable2.pass.d.ts +0 -5
- package/fjs/emergent_testing/scenarios/thenable2.pass.js +0 -3
- package/fjs/emergent_testing/scenarios/throw.pass.d.ts +0 -5
- package/fjs/emergent_testing/scenarios/throw.pass.js +0 -3
- package/fjs/emergent_testing/types.js +0 -6
- package/fjs/js/tokenizer/types.js +0 -6
- package/fjs/media/html/types.js +0 -6
- package/fjs/media/json/parser/types.js +0 -6
- package/fjs/media/json/tokenizer/types.js +0 -6
- package/fjs/media/json/types.js +0 -13
- package/fjs/media/nix/types.js +0 -6
- package/fjs/media/revision/types.js +0 -8
- package/fjs/media/type/types.js +0 -6
- package/fjs/media/types.js +0 -7
- package/fjs/nanvm/types.js +0 -11
- package/fjs/protocol/json_rpc/types.js +0 -8
- package/fjs/protocol/mcp/types.js +0 -8
- package/fjs/sul/id/types.js +0 -6
- package/fjs/sul/level/hash/types.js +0 -6
- package/fjs/sul/level/literal/types.js +0 -6
- package/fjs/sul/types.js +0 -6
- package/fjs/text/sgr/types.js +0 -6
- package/fjs/text/types.js +0 -6
- package/fjs/text/utf16/types.js +0 -6
- package/fjs/text/utf8/types.js +0 -6
- package/fjs/types/array/types.js +0 -6
- package/fjs/types/bigfloat/types.js +0 -6
- package/fjs/types/bigint/types.js +0 -6
- package/fjs/types/bit_vec/types.js +0 -6
- package/fjs/types/btree/find/types.js +0 -6
- package/fjs/types/btree/types/types.js +0 -6
- package/fjs/types/byte_set/types.js +0 -6
- package/fjs/types/function/compare/types.js +0 -6
- package/fjs/types/function/operator/types.js +0 -6
- package/fjs/types/function/types.js +0 -6
- package/fjs/types/list/types.js +0 -6
- package/fjs/types/nibble_set/types.js +0 -6
- package/fjs/types/nominal/types.js +0 -6
- package/fjs/types/nullable/types.js +0 -6
- package/fjs/types/object/types.js +0 -8
- package/fjs/types/option/types.js +0 -6
- package/fjs/types/ordered_map/types.js +0 -6
- package/fjs/types/patricia_trie/types.js +0 -6
- package/fjs/types/phantom/types.js +0 -11
- package/fjs/types/prime_field/types.js +0 -6
- package/fjs/types/range/types.js +0 -6
- package/fjs/types/range_map/types.js +0 -6
- package/fjs/types/result/types.js +0 -6
- package/fjs/types/rtti/common/types.js +0 -1
- package/fjs/types/rtti/parse/types.js +0 -1
- package/fjs/types/rtti/ts/types.js +0 -1
- package/fjs/types/rtti/types.js +0 -1
- package/fjs/types/rtti/validate/module.f.d.mts +0 -6
- package/fjs/types/rtti/validate/module.f.mjs +0 -169
- package/fjs/types/rtti/validate/proof.f.d.mts +0 -125
- package/fjs/types/rtti/validate/proof.f.mjs +0 -437
- package/fjs/types/rtti/validate/types.d.ts +0 -6
- package/fjs/types/rtti/validate/types.js +0 -1
- package/fjs/types/sorted_list/types.js +0 -6
- package/fjs/types/sorted_set/types.js +0 -6
- package/fjs/types/string_set/types.js +0 -6
- package/fjs/types/ts/types.js +0 -8
package/fjs/cas/evo/proof.f.mjs
CHANGED
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @import { Cas } from '../types.ts'
|
|
3
|
+
* @import { EvoChannel } from './types.ts'
|
|
4
|
+
* @import { Result } from '../../types/result/types.ts'
|
|
5
|
+
* @import { Effect } from '../../effects/types.ts'
|
|
6
|
+
* @import { IoChannel, NodeOp } from '../../effects/node/types.ts'
|
|
7
|
+
* @import { State } from '../../effects/node/virtual/types.ts'
|
|
3
8
|
* @import { Vec } from '../../types/bit_vec/types.ts'
|
|
4
9
|
* @import { Ok } from '../../types/result/types.ts'
|
|
5
|
-
* @import { IoResult } from '../../effects/node/types.ts'
|
|
6
10
|
* @import { List } from '../../effects/list/types.ts'
|
|
7
11
|
* @import { RevisionData } from './types.ts'
|
|
8
12
|
*/
|
|
9
13
|
|
|
10
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
assert,
|
|
16
|
+
assertEq,
|
|
17
|
+
assertStructurallySame,
|
|
18
|
+
} from '../../asserts/module.f.mjs'
|
|
11
19
|
import { pure } from '../../effects/module.f.mjs'
|
|
20
|
+
import { pureError } from '../../effects/module.f.mjs'
|
|
21
|
+
import { ioError } from '../../effects/node/module.f.mjs'
|
|
12
22
|
import { fileCas } from '../module.f.mjs'
|
|
13
23
|
import { sha256 } from '../../crypto/sha2/module.f.mjs'
|
|
14
24
|
import { emptyState, virtual } from '../../effects/node/virtual/module.f.mjs'
|
|
15
25
|
import { vec, vec8 } from '../../types/bit_vec/module.f.mjs'
|
|
16
26
|
import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.mjs'
|
|
17
27
|
import { unwrap } from '../../types/nullable/module.f.mjs'
|
|
18
|
-
import { ok, error } from '../../types/result/module.f.mjs'
|
|
28
|
+
import { ok, error, unwrap as unwrapResult } from '../../types/result/module.f.mjs'
|
|
19
29
|
import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.mjs'
|
|
20
30
|
import { tryUtf8 } from '../../text/module.f.mjs'
|
|
21
|
-
|
|
22
31
|
import { dialect as revisionDialect } from '../../media/revision/module.f.mjs'
|
|
23
32
|
import {
|
|
24
33
|
buildCache, decodeRevisionBlob, initEvo, evo, emptyCache, syncRevision,
|
|
@@ -31,18 +40,18 @@ const home = '.'
|
|
|
31
40
|
/** @type {Cas<never>} */
|
|
32
41
|
const writeFailingCas = {
|
|
33
42
|
read: () => elEmpty(),
|
|
34
|
-
write: () => pure(error('boom')),
|
|
35
|
-
list: () => pure([]),
|
|
43
|
+
write: () => pure(error(ioError({ message: 'boom' }))),
|
|
44
|
+
list: () => pure(ok([])),
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
// A `Cas<never>` whose `read`
|
|
39
|
-
// shard — what a permission error, a mid-stream I/O failure, or a blob
|
|
40
|
-
// large for `collectRead` to buffer looks like to a caller.
|
|
47
|
+
// A `Cas<never>` whose `read` fails the stream with something that is *not* a
|
|
48
|
+
// missing shard — what a permission error, a mid-stream I/O failure, or a blob
|
|
49
|
+
// too large for `collectRead` to buffer looks like to a caller.
|
|
41
50
|
/** @type {Cas<never>} */
|
|
42
51
|
const readFailingCas = {
|
|
43
|
-
read: () =>
|
|
44
|
-
write: () => pure(error('write not supported')),
|
|
45
|
-
list: () => pure([]),
|
|
52
|
+
read: () => pureError(ioError({ message: 'boom' })),
|
|
53
|
+
write: () => pure(error(ioError({ message: 'write not supported' }))),
|
|
54
|
+
list: () => pure(ok([])),
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
// A `Cas<never>` backed by a fixed set of (hash, content) entries, returned
|
|
@@ -54,45 +63,80 @@ const fixedCas = entries => ({
|
|
|
54
63
|
read: hash => {
|
|
55
64
|
const found = entries.find(([h]) => vecToCBase32(h) === vecToCBase32(hash))
|
|
56
65
|
return found === undefined
|
|
57
|
-
?
|
|
58
|
-
: nonEmpty(
|
|
66
|
+
? pureError(ioError({ message: 'not found' }))
|
|
67
|
+
: nonEmpty(found[1], elEmpty())
|
|
59
68
|
},
|
|
60
|
-
write: () => pure(error('write not supported')),
|
|
61
|
-
list: () => pure(entries.map(([h]) => h)),
|
|
69
|
+
write: () => pure(error(ioError({ message: 'write not supported' }))),
|
|
70
|
+
list: () => pure(ok(entries.map(([h]) => h))),
|
|
62
71
|
})
|
|
63
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Runs `e` against the virtual runner and unwraps the effect channel.
|
|
75
|
+
*
|
|
76
|
+
* These proofs drive a runner that implements every operation they use, so a
|
|
77
|
+
* channel failure here is a broken fixture rather than a case under test, and
|
|
78
|
+
* panicking on one is the wanted outcome.
|
|
79
|
+
*
|
|
80
|
+
* The cases that *are* about failure therefore do not come through here: a
|
|
81
|
+
* rejected revision now travels in the same channel as an undispatchable
|
|
82
|
+
* operation, so unwrapping would panic on the thing under test. Those use the
|
|
83
|
+
* raw `virtual` and `assertEvoError`.
|
|
84
|
+
*
|
|
85
|
+
* @type {(state: State) => <T, E>(e: Effect<NodeOp, T, E>) => readonly [State, T]}
|
|
86
|
+
*/
|
|
87
|
+
const virtualOk = state => e => {
|
|
88
|
+
const [s, r] = virtual(state)(e)
|
|
89
|
+
return [s, unwrapResult(r)]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Asserts that `r` failed with an {@link EvoError} whose message contains
|
|
94
|
+
* `expected`.
|
|
95
|
+
*
|
|
96
|
+
* The tag is checked as well as the text, so a `notImplemented` from a runner
|
|
97
|
+
* that quietly lost a handler cannot pass for a rejected revision. Both now
|
|
98
|
+
* arrive through the same channel, which is the point — telling them apart is
|
|
99
|
+
* what the tag is for.
|
|
100
|
+
*
|
|
101
|
+
* @type {(r: Result<unknown, EvoChannel>, expected: string) => void}
|
|
102
|
+
*/
|
|
103
|
+
const assertEvoError = (r, expected) =>
|
|
104
|
+
assert(
|
|
105
|
+
r[0] === 'error' && r[1][0] === 'evoError' && r[1][1].includes(expected),
|
|
106
|
+
['expected an evo error containing', expected, r])
|
|
107
|
+
|
|
64
108
|
export const proof = {
|
|
65
109
|
buildCacheEmptyStoreYieldsEmptyCache: () => {
|
|
66
110
|
const c = fileCas(sha256)(home)
|
|
67
|
-
const [, cache] =
|
|
68
|
-
|
|
111
|
+
const [, cache] = virtualOk(emptyState)(buildCache(c))
|
|
112
|
+
assertStructurallySame(cache, emptyCache)
|
|
69
113
|
},
|
|
70
114
|
buildCacheSkipsNonRevisionBlob: () => {
|
|
71
115
|
const c = fileCas(sha256)(home)
|
|
72
116
|
const content = vec8(0x41n) // 'A' — valid UTF-8, not revision JSON
|
|
73
|
-
const [state1] = virtual(emptyState)(c.write(nonEmpty(
|
|
74
|
-
const [, cache] =
|
|
117
|
+
const [state1] = virtual(emptyState)(c.write(nonEmpty(content, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
|
|
118
|
+
const [, cache] = virtualOk(state1)(buildCache(c))
|
|
75
119
|
assertEq(Object.keys(cache.bySubject).length, 0)
|
|
76
120
|
},
|
|
77
121
|
decodeRevisionBlobMissingHashIsNull: () => {
|
|
78
122
|
const c = fileCas(sha256)(home)
|
|
79
|
-
const [, revision] =
|
|
123
|
+
const [, revision] = virtualOk(emptyState)(decodeRevisionBlob(c)(vec8(0x99n)))
|
|
80
124
|
assertEq(revision, null)
|
|
81
125
|
},
|
|
82
126
|
decodeRevisionBlobNonUtf8IsNull: () => {
|
|
83
127
|
const c = fileCas(sha256)(home)
|
|
84
128
|
const oddVec = vec(5n)(0b10101n) // not a whole number of bytes
|
|
85
|
-
const [state1, w] = virtual(emptyState)(c.write(nonEmpty(
|
|
129
|
+
const [state1, w] = virtual(emptyState)(c.write(nonEmpty(oddVec, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
|
|
86
130
|
assert(w[0] === 'ok', ['expected write ok', w])
|
|
87
|
-
const [, revision] =
|
|
131
|
+
const [, revision] = virtualOk(state1)(decodeRevisionBlob(c)(w[1]))
|
|
88
132
|
assertEq(revision, null)
|
|
89
133
|
},
|
|
90
134
|
decodeRevisionBlobInvalidJsonIsNull: () => {
|
|
91
135
|
const c = fileCas(sha256)(home)
|
|
92
136
|
const content = vec8(0x7bn) // '{' alone: valid UTF-8, not parseable JSON
|
|
93
|
-
const [state1, w] = virtual(emptyState)(c.write(nonEmpty(
|
|
137
|
+
const [state1, w] = virtual(emptyState)(c.write(nonEmpty(content, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
|
|
94
138
|
assert(w[0] === 'ok', ['expected write ok', w])
|
|
95
|
-
const [, revision] =
|
|
139
|
+
const [, revision] = virtualOk(state1)(decodeRevisionBlob(c)(w[1]))
|
|
96
140
|
assertEq(revision, null)
|
|
97
141
|
},
|
|
98
142
|
decodeRevisionBlobValidRevisionRoundTrips: () => {
|
|
@@ -101,9 +145,9 @@ export const proof = {
|
|
|
101
145
|
const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`
|
|
102
146
|
const bytes = tryUtf8(text)
|
|
103
147
|
assert(bytes !== null, 'expected the sample revision text to encode as UTF-8')
|
|
104
|
-
const [state1, w] = virtual(emptyState)(c.write(nonEmpty(
|
|
148
|
+
const [state1, w] = virtual(emptyState)(c.write(nonEmpty(bytes, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
|
|
105
149
|
assert(w[0] === 'ok', ['expected write ok', w])
|
|
106
|
-
const [, revision] =
|
|
150
|
+
const [, revision] = virtualOk(state1)(decodeRevisionBlob(c)(w[1]))
|
|
107
151
|
assert(revision !== null, 'expected a decoded revision')
|
|
108
152
|
assertEq(revision?.subject, subjectHash)
|
|
109
153
|
},
|
|
@@ -117,9 +161,9 @@ export const proof = {
|
|
|
117
161
|
const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`
|
|
118
162
|
const bytes = tryUtf8(text)
|
|
119
163
|
assert(bytes !== null, 'expected the sample revision text to encode as UTF-8')
|
|
120
|
-
const [state1, w] = virtual(emptyState)(fileCas(sha256)(home).write(nonEmpty(
|
|
164
|
+
const [state1, w] = virtual(emptyState)(fileCas(sha256)(home).write(nonEmpty(bytes, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
|
|
121
165
|
assert(w[0] === 'ok', ['expected write ok', w])
|
|
122
|
-
const [, cache] =
|
|
166
|
+
const [, cache] = virtualOk(state1)(buildCache(c))
|
|
123
167
|
assertEq(cache.bySubject[subjectHash]?.hashes.length, 1)
|
|
124
168
|
assertEq(cache.bySubject[subjectHash]?.hashes[0], vecToCBase32(w[1]))
|
|
125
169
|
},
|
|
@@ -141,8 +185,8 @@ export const proof = {
|
|
|
141
185
|
const childBytes = tryUtf8(childText)
|
|
142
186
|
assert(rootBytes !== null && childBytes !== null, 'expected sample revisions to encode as UTF-8')
|
|
143
187
|
const cas = fixedCas([[childHash, childBytes], [rootHash, rootBytes]])
|
|
144
|
-
const [state0, cacheKey] =
|
|
145
|
-
const [, heads] =
|
|
188
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(cas))
|
|
189
|
+
const [, heads] = virtualOk(state0)(evo(cas)(cacheKey).head('doc'))
|
|
146
190
|
assertEq(heads.length, 1)
|
|
147
191
|
assertEq(heads[0], childCBase32)
|
|
148
192
|
},
|
|
@@ -165,8 +209,8 @@ export const proof = {
|
|
|
165
209
|
const childBytes = tryUtf8(childText)
|
|
166
210
|
assert(rootBytes !== null && childBytes !== null, 'expected sample revisions to encode as UTF-8')
|
|
167
211
|
const cas = fixedCas([[rootHash, rootBytes], [childHash, childBytes]])
|
|
168
|
-
const [state0, cacheKey] =
|
|
169
|
-
const [, heads] =
|
|
212
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(cas))
|
|
213
|
+
const [, heads] = virtualOk(state0)(evo(cas)(cacheKey).head('doc'))
|
|
170
214
|
assertEq(heads.length, 1)
|
|
171
215
|
assertEq(heads[0], vecToCBase32(childHash))
|
|
172
216
|
},
|
|
@@ -176,38 +220,35 @@ export const proof = {
|
|
|
176
220
|
// set), and a keep (an unrelated existing head surviving the fold).
|
|
177
221
|
addRevisionBuildsHeadsAcrossChainAndFork: () => {
|
|
178
222
|
const c = fileCas(sha256)(home)
|
|
179
|
-
const [state0, cacheKey] =
|
|
223
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
180
224
|
const e = evo(c)(cacheKey)
|
|
181
225
|
const snapshotHash = vecToCBase32(vec8(0x22n))
|
|
182
226
|
|
|
183
227
|
/** @type {RevisionData} */
|
|
184
228
|
const rev0 = { parents: [], subject: 'doc', snapshot: snapshotHash }
|
|
185
|
-
const [state1, rev0Result] =
|
|
186
|
-
|
|
187
|
-
const rev0Hash = rev0Result[1]
|
|
229
|
+
const [state1, rev0Result] = virtualOk(state0)(e.add(rev0))
|
|
230
|
+
const rev0Hash = rev0Result
|
|
188
231
|
|
|
189
232
|
// rev1 inherits rev0's snapshot; rev2 states a different one — so the
|
|
190
233
|
// two concurrent children are genuinely distinct blobs (both children
|
|
191
234
|
// of rev0, same generation), not one deduplicated revision.
|
|
192
235
|
/** @type {RevisionData} */
|
|
193
236
|
const rev1 = { parents: [rev0Hash], subject: 'doc' }
|
|
194
|
-
const [state2, rev1Result] =
|
|
195
|
-
|
|
196
|
-
const rev1Hash = rev1Result[1]
|
|
237
|
+
const [state2, rev1Result] = virtualOk(state1)(e.add(rev1))
|
|
238
|
+
const rev1Hash = rev1Result
|
|
197
239
|
|
|
198
240
|
/** @type {RevisionData} */
|
|
199
241
|
const rev2 = { parents: [rev0Hash], subject: 'doc', snapshot: vecToCBase32(vec8(0x23n)) }
|
|
200
|
-
const [state3, rev2Result] =
|
|
201
|
-
|
|
202
|
-
const rev2Hash = rev2Result[1]
|
|
242
|
+
const [state3, rev2Result] = virtualOk(state2)(e.add(rev2))
|
|
243
|
+
const rev2Hash = rev2Result
|
|
203
244
|
|
|
204
|
-
const [state4, heads] =
|
|
245
|
+
const [state4, heads] = virtualOk(state3)(e.head('doc'))
|
|
205
246
|
assertEq(heads.length, 2)
|
|
206
247
|
assert(heads.includes(rev1Hash), ['expected rev1 to remain a head', heads])
|
|
207
248
|
assert(heads.includes(rev2Hash), ['expected rev2 to be a head', heads])
|
|
208
249
|
assert(!heads.includes(rev0Hash), ['expected rev0 to no longer be a head', heads])
|
|
209
250
|
|
|
210
|
-
const [, subjects] =
|
|
251
|
+
const [, subjects] = virtualOk(state4)(e.list())
|
|
211
252
|
assertEq(subjects.length, 1)
|
|
212
253
|
assertEq(subjects[0], 'doc')
|
|
213
254
|
},
|
|
@@ -217,16 +258,14 @@ export const proof = {
|
|
|
217
258
|
// and neither subject appears in the other's result.
|
|
218
259
|
listPartitionsSubjectsByHeadArchivedFlag: () => {
|
|
219
260
|
const c = fileCas(sha256)(home)
|
|
220
|
-
const [state0, cacheKey] =
|
|
261
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
221
262
|
const e = evo(c)(cacheKey)
|
|
222
|
-
const [state1, live] =
|
|
223
|
-
|
|
224
|
-
const [
|
|
225
|
-
assert(gone[0] === 'ok', ['expected the archived add to succeed', gone])
|
|
226
|
-
const [state3, active] = virtual(state2)(e.list())
|
|
263
|
+
const [state1, live] = virtualOk(state0)(e.add({ parents: [], subject: 'live', snapshot: vecToCBase32(vec8(0x40n)) }))
|
|
264
|
+
const [state2, gone] = virtualOk(state1)(e.add({ parents: [], subject: 'gone', snapshot: vecToCBase32(vec8(0x41n)), archived: true }))
|
|
265
|
+
const [state3, active] = virtualOk(state2)(e.list())
|
|
227
266
|
assertEq(active.length, 1)
|
|
228
267
|
assertEq(active[0], 'live')
|
|
229
|
-
const [, archived] =
|
|
268
|
+
const [, archived] = virtualOk(state3)(e.list(true))
|
|
230
269
|
assertEq(archived.length, 1)
|
|
231
270
|
assertEq(archived[0], 'gone')
|
|
232
271
|
},
|
|
@@ -235,22 +274,19 @@ export const proof = {
|
|
|
235
274
|
// — so the subject stays out of the archived-only result.
|
|
236
275
|
listTreatsDisagreeingHeadsAsActive: () => {
|
|
237
276
|
const c = fileCas(sha256)(home)
|
|
238
|
-
const [state0, cacheKey] =
|
|
277
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
239
278
|
const e = evo(c)(cacheKey)
|
|
240
|
-
const [state1, root] =
|
|
241
|
-
assert(root[0] === 'ok', ['expected root ok', root])
|
|
279
|
+
const [state1, root] = virtualOk(state0)(e.add({ parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x42n)) }))
|
|
242
280
|
// The two children differ only in `archived`, which is enough to make
|
|
243
281
|
// them distinct blobs, hence two concurrent heads of one root.
|
|
244
|
-
const [state2
|
|
245
|
-
|
|
246
|
-
const [
|
|
247
|
-
assert(dropped[0] === 'ok', ['expected the archived child ok', dropped])
|
|
248
|
-
const [state4, heads] = virtual(state3)(e.head('doc'))
|
|
282
|
+
const [state2] = virtualOk(state1)(e.add({ parents: [root], subject: 'doc' }))
|
|
283
|
+
const [state3] = virtualOk(state2)(e.add({ parents: [root], subject: 'doc', archived: true }))
|
|
284
|
+
const [state4, heads] = virtualOk(state3)(e.head('doc'))
|
|
249
285
|
assertEq(heads.length, 2)
|
|
250
|
-
const [state5, active] =
|
|
286
|
+
const [state5, active] = virtualOk(state4)(e.list())
|
|
251
287
|
assertEq(active.length, 1)
|
|
252
288
|
assertEq(active[0], 'doc')
|
|
253
|
-
const [, archived] =
|
|
289
|
+
const [, archived] = virtualOk(state5)(e.list(true))
|
|
254
290
|
assertEq(archived.length, 0)
|
|
255
291
|
},
|
|
256
292
|
// `archived` is a property of a revision, not of a subject: a subject
|
|
@@ -258,16 +294,14 @@ export const proof = {
|
|
|
258
294
|
// demotes that revision out of the head set.
|
|
259
295
|
listIgnoresArchivedRevisionsThatAreNoLongerHeads: () => {
|
|
260
296
|
const c = fileCas(sha256)(home)
|
|
261
|
-
const [state0, cacheKey] =
|
|
297
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
262
298
|
const e = evo(c)(cacheKey)
|
|
263
|
-
const [state1, root] =
|
|
264
|
-
|
|
265
|
-
const [
|
|
266
|
-
assert(revived[0] === 'ok', ['expected the unarchived child ok', revived])
|
|
267
|
-
const [state3, active] = virtual(state2)(e.list())
|
|
299
|
+
const [state1, root] = virtualOk(state0)(e.add({ parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x45n)), archived: true }))
|
|
300
|
+
const [state2] = virtualOk(state1)(e.add({ parents: [root], subject: 'doc' }))
|
|
301
|
+
const [state3, active] = virtualOk(state2)(e.list())
|
|
268
302
|
assertEq(active.length, 1)
|
|
269
303
|
assertEq(active[0], 'doc')
|
|
270
|
-
const [, archived] =
|
|
304
|
+
const [, archived] = virtualOk(state3)(e.list(true))
|
|
271
305
|
assertEq(archived.length, 0)
|
|
272
306
|
},
|
|
273
307
|
// A subject can end up with no current head at all, and a status is a
|
|
@@ -283,29 +317,27 @@ export const proof = {
|
|
|
283
317
|
const bytes = tryUtf8(text)
|
|
284
318
|
assert(bytes !== null, 'expected the sample revision text to encode as UTF-8')
|
|
285
319
|
const cas = fixedCas([[selfHash, bytes]])
|
|
286
|
-
const [state0, cacheKey] =
|
|
320
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(cas))
|
|
287
321
|
const e = evo(cas)(cacheKey)
|
|
288
|
-
const [state1, heads] =
|
|
322
|
+
const [state1, heads] = virtualOk(state0)(e.head('doc'))
|
|
289
323
|
assertEq(heads.length, 0)
|
|
290
|
-
const [state2, active] =
|
|
324
|
+
const [state2, active] = virtualOk(state1)(e.list())
|
|
291
325
|
assertEq(active.length, 0)
|
|
292
|
-
const [, archived] =
|
|
326
|
+
const [, archived] = virtualOk(state2)(e.list(true))
|
|
293
327
|
assertEq(archived.length, 0)
|
|
294
328
|
},
|
|
295
329
|
// Adding the exact same revision twice yields the same (deduplicated)
|
|
296
330
|
// content hash and must not duplicate the head entry.
|
|
297
331
|
addRevisionIdempotentOnDuplicateContent: () => {
|
|
298
332
|
const c = fileCas(sha256)(home)
|
|
299
|
-
const [state0, cacheKey] =
|
|
333
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
300
334
|
const e = evo(c)(cacheKey)
|
|
301
335
|
/** @type {RevisionData} */
|
|
302
336
|
const input = { parents: [], subject: 'x', snapshot: vecToCBase32(vec8(0x33n)) }
|
|
303
|
-
const [state1, r1] =
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
assertEq(r1[1], r2[1])
|
|
308
|
-
const [, heads] = virtual(state2)(e.head('x'))
|
|
337
|
+
const [state1, r1] = virtualOk(state0)(e.add(input))
|
|
338
|
+
const [state2, r2] = virtualOk(state1)(e.add(input))
|
|
339
|
+
assertEq(r1, r2)
|
|
340
|
+
const [, heads] = virtualOk(state2)(e.head('x'))
|
|
309
341
|
assertEq(heads.length, 1)
|
|
310
342
|
},
|
|
311
343
|
// Two `add` calls describing the same logical revision but spelling the
|
|
@@ -315,44 +347,39 @@ export const proof = {
|
|
|
315
347
|
// `canonicalHash` exists to prevent.
|
|
316
348
|
addRevisionCanonicalizesParentSpellingBeforeSerializing: () => {
|
|
317
349
|
const c = fileCas(sha256)(home)
|
|
318
|
-
const [state0, cacheKey] =
|
|
350
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
319
351
|
const e = evo(c)(cacheKey)
|
|
320
352
|
/** @type {RevisionData} */
|
|
321
353
|
const root = { parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x2en)) }
|
|
322
|
-
const [state1, rootResult] =
|
|
323
|
-
|
|
324
|
-
const rootHash = rootResult[1]
|
|
354
|
+
const [state1, rootResult] = virtualOk(state0)(e.add(root))
|
|
355
|
+
const rootHash = rootResult
|
|
325
356
|
assert(rootHash !== rootHash.toUpperCase(), 'expected the sample hash to contain letters')
|
|
326
357
|
/** @type {RevisionData} */
|
|
327
358
|
const child1 = { parents: [rootHash], subject: 'doc' }
|
|
328
|
-
const [state2, child1Result] =
|
|
329
|
-
assert(child1Result[0] === 'ok', ['expected child1 ok', child1Result])
|
|
359
|
+
const [state2, child1Result] = virtualOk(state1)(e.add(child1))
|
|
330
360
|
/** @type {RevisionData} */
|
|
331
361
|
const child2 = { parents: [rootHash.toUpperCase()], subject: 'doc' }
|
|
332
|
-
const [state3, child2Result] =
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const [, heads] = virtual(state3)(e.head('doc'))
|
|
362
|
+
const [state3, child2Result] = virtualOk(state2)(e.add(child2))
|
|
363
|
+
assertEq(child1Result, child2Result)
|
|
364
|
+
const [, heads] = virtualOk(state3)(e.head('doc'))
|
|
336
365
|
assertEq(heads.length, 1)
|
|
337
|
-
assertEq(heads[0], child1Result
|
|
366
|
+
assertEq(heads[0], child1Result)
|
|
338
367
|
},
|
|
339
368
|
// `subject` omitted with exactly one parent is inherited from that
|
|
340
369
|
// parent's own `subject`.
|
|
341
370
|
addRevisionResolvesSubjectFromSingleParent: () => {
|
|
342
371
|
const c = fileCas(sha256)(home)
|
|
343
|
-
const [state0, cacheKey] =
|
|
372
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
344
373
|
const e = evo(c)(cacheKey)
|
|
345
374
|
/** @type {RevisionData} */
|
|
346
375
|
const root = { parents: [], subject: 'inherit-me', snapshot: vecToCBase32(vec8(0x44n)) }
|
|
347
|
-
const [state1, rootResult] =
|
|
348
|
-
|
|
349
|
-
const rootHash = rootResult[1]
|
|
376
|
+
const [state1, rootResult] = virtualOk(state0)(e.add(root))
|
|
377
|
+
const rootHash = rootResult
|
|
350
378
|
/** @type {RevisionData} */
|
|
351
379
|
const child = { parents: [rootHash] }
|
|
352
|
-
const [state2, childResult] =
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
const [, heads] = virtual(state2)(e.head('inherit-me'))
|
|
380
|
+
const [state2, childResult] = virtualOk(state1)(e.add(child))
|
|
381
|
+
const childHash = childResult
|
|
382
|
+
const [, heads] = virtualOk(state2)(e.head('inherit-me'))
|
|
356
383
|
assertEq(heads.length, 1)
|
|
357
384
|
assertEq(heads[0], childHash)
|
|
358
385
|
},
|
|
@@ -362,21 +389,19 @@ export const proof = {
|
|
|
362
389
|
// inherits the parent's stored snapshot and gets `generation` 1.
|
|
363
390
|
addComputesGenerationAndResolvesSnapshot: () => {
|
|
364
391
|
const c = fileCas(sha256)(home)
|
|
365
|
-
const [state0, cacheKey] =
|
|
392
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
366
393
|
const e = evo(c)(cacheKey)
|
|
367
394
|
const subjectHash = vecToCBase32(vec8(0x71n))
|
|
368
|
-
const [state1, rootResult] =
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
const [state2, root] = virtual(state1)(decodeRevisionBlob(c)(rootHashVec))
|
|
395
|
+
const [state1, rootResult] = virtualOk(state0)(e.add({ parents: [], subject: subjectHash }))
|
|
396
|
+
const rootHashVec = unwrap(cBase32ToVec(rootResult))
|
|
397
|
+
const [state2, root] = virtualOk(state1)(decodeRevisionBlob(c)(rootHashVec))
|
|
372
398
|
assert(root !== null, 'expected the stored root to decode')
|
|
373
399
|
assertEq(root?.generation, 0)
|
|
374
400
|
assertEq(root?.snapshot, subjectHash)
|
|
375
401
|
|
|
376
|
-
const [state3, childResult] =
|
|
377
|
-
|
|
378
|
-
const
|
|
379
|
-
const [, child] = virtual(state3)(decodeRevisionBlob(c)(childHashVec))
|
|
402
|
+
const [state3, childResult] = virtualOk(state2)(e.add({ parents: [rootResult], subject: subjectHash }))
|
|
403
|
+
const childHashVec = unwrap(cBase32ToVec(childResult))
|
|
404
|
+
const [, child] = virtualOk(state3)(decodeRevisionBlob(c)(childHashVec))
|
|
380
405
|
assert(child !== null, 'expected the stored child to decode')
|
|
381
406
|
assertEq(child?.generation, 1)
|
|
382
407
|
assertEq(child?.snapshot, subjectHash)
|
|
@@ -385,24 +410,19 @@ export const proof = {
|
|
|
385
410
|
// and one at generation 1 yield generation 3.
|
|
386
411
|
addComputesMergeGenerationFromMaxOfParents: () => {
|
|
387
412
|
const c = fileCas(sha256)(home)
|
|
388
|
-
const [state0, cacheKey] =
|
|
413
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
389
414
|
const e = evo(c)(cacheKey)
|
|
390
415
|
const snap = vecToCBase32(vec8(0x72n))
|
|
391
416
|
// Mainline chain: root(gen0) → a(gen1) → b(gen2).
|
|
392
|
-
const [state1, root] =
|
|
393
|
-
|
|
394
|
-
const [
|
|
395
|
-
assert(a[0] === 'ok', ['expected a ok', a])
|
|
396
|
-
const [state3, b] = virtual(state2)(e.add({ parents: [a[1]], subject: 'm', snapshot: snap }))
|
|
397
|
-
assert(b[0] === 'ok', ['expected b ok', b])
|
|
417
|
+
const [state1, root] = virtualOk(state0)(e.add({ parents: [], subject: 'm', snapshot: snap }))
|
|
418
|
+
const [state2, a] = virtualOk(state1)(e.add({ parents: [root], subject: 'm', snapshot: snap }))
|
|
419
|
+
const [state3, b] = virtualOk(state2)(e.add({ parents: [a], subject: 'm', snapshot: snap }))
|
|
398
420
|
// Side branch off root: c(gen1).
|
|
399
|
-
const [state4, cRev] =
|
|
400
|
-
assert(cRev[0] === 'ok', ['expected c ok', cRev])
|
|
421
|
+
const [state4, cRev] = virtualOk(state3)(e.add({ parents: [root], subject: 'm', snapshot: snap }))
|
|
401
422
|
// Merge of b(gen2) and c(gen1) → gen3.
|
|
402
|
-
const [state5, merge] =
|
|
403
|
-
|
|
404
|
-
const
|
|
405
|
-
const [, mergeRev] = virtual(state5)(decodeRevisionBlob(c)(mergeHashVec))
|
|
423
|
+
const [state5, merge] = virtualOk(state4)(e.add({ parents: [b, cRev], subject: 'm', snapshot: snap }))
|
|
424
|
+
const mergeHashVec = unwrap(cBase32ToVec(merge))
|
|
425
|
+
const [, mergeRev] = virtualOk(state5)(decodeRevisionBlob(c)(mergeHashVec))
|
|
406
426
|
assert(mergeRev !== null, 'expected the stored merge to decode')
|
|
407
427
|
assertEq(mergeRev?.generation, 3)
|
|
408
428
|
},
|
|
@@ -412,25 +432,22 @@ export const proof = {
|
|
|
412
432
|
// byte-identical to the same `add` without it.
|
|
413
433
|
addIgnoresSuppliedGeneration: () => {
|
|
414
434
|
const c = fileCas(sha256)(home)
|
|
415
|
-
const [state0, cacheKey] =
|
|
435
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
416
436
|
const e = evo(c)(cacheKey)
|
|
417
437
|
const subjectHash = vecToCBase32(vec8(0x73n))
|
|
418
|
-
const [state1, claimed] =
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
assertEq(claimed[1], plain[1])
|
|
423
|
-
const [, revision] = virtual(state2)(e.revision(claimed[1]))
|
|
438
|
+
const [state1, claimed] = virtualOk(state0)(e.add({ parents: [], subject: subjectHash, generation: 42 }))
|
|
439
|
+
const [state2, plain] = virtualOk(state1)(e.add({ parents: [], subject: subjectHash }))
|
|
440
|
+
assertEq(claimed, plain)
|
|
441
|
+
const [, revision] = virtual(state2)(e.revision(claimed))
|
|
424
442
|
assert(revision[0] === 'ok', ['expected revision ok', revision])
|
|
425
443
|
assertEq(revision[1].generation, 0)
|
|
426
444
|
},
|
|
427
445
|
addRevisionSubjectRequiredForZeroParents: () => {
|
|
428
446
|
const c = fileCas(sha256)(home)
|
|
429
|
-
const [state0, cacheKey] =
|
|
447
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
430
448
|
const e = evo(c)(cacheKey)
|
|
431
449
|
const [, result] = virtual(state0)(e.add({ parents: [] }))
|
|
432
|
-
|
|
433
|
-
assert(result[0] === 'error' && result[1].includes('subject is required'), ['unexpected message', result])
|
|
450
|
+
assertEvoError(result, 'subject is required')
|
|
434
451
|
},
|
|
435
452
|
// Zero parents and no input `snapshot`: `resolveSnapshot` falls back to
|
|
436
453
|
// `subject` as the reference, which must itself be a hash. A subject with
|
|
@@ -439,40 +456,36 @@ export const proof = {
|
|
|
439
456
|
// an explicit `snapshot`.
|
|
440
457
|
addRevisionNonHashSubjectWithoutSnapshotIsError: () => {
|
|
441
458
|
const c = fileCas(sha256)(home)
|
|
442
|
-
const [state0, cacheKey] =
|
|
459
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
443
460
|
const e = evo(c)(cacheKey)
|
|
444
461
|
const [, result] = virtual(state0)(e.add({ parents: [], subject: 'not-a-hash!' }))
|
|
445
|
-
|
|
446
|
-
assert(result[0] === 'error' && result[1].includes('subject must be a valid hash'), ['unexpected message', result])
|
|
462
|
+
assertEvoError(result, 'subject must be a valid hash')
|
|
447
463
|
},
|
|
448
464
|
addRevisionInvalidParentHashIsError: () => {
|
|
449
465
|
const c = fileCas(sha256)(home)
|
|
450
|
-
const [state0, cacheKey] =
|
|
466
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
451
467
|
const e = evo(c)(cacheKey)
|
|
452
468
|
const [, result] = virtual(state0)(e.add({ parents: ['not a valid cbase32!'] }))
|
|
453
|
-
|
|
454
|
-
assert(result[0] === 'error' && result[1].includes('invalid parent hash'), ['unexpected message', result])
|
|
469
|
+
assertEvoError(result, 'invalid parent hash')
|
|
455
470
|
},
|
|
456
471
|
addRevisionParentNotARevisionIsError: () => {
|
|
457
472
|
const c = fileCas(sha256)(home)
|
|
458
|
-
const [state0, cacheKey] =
|
|
473
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
459
474
|
const e = evo(c)(cacheKey)
|
|
460
475
|
const missingParent = vecToCBase32(vec8(0x55n))
|
|
461
476
|
const [, result] = virtual(state0)(e.add({ parents: [missingParent] }))
|
|
462
|
-
|
|
463
|
-
assert(result[0] === 'error' && result[1].includes('parent is not a revision blob'), ['unexpected message', result])
|
|
477
|
+
assertEvoError(result, 'parent is not a revision blob')
|
|
464
478
|
},
|
|
465
479
|
// An explicit `subject` must not bypass parent validation: a missing
|
|
466
480
|
// parent is still an error even though `subject` alone would otherwise
|
|
467
481
|
// resolve without looking at any parent.
|
|
468
482
|
addRevisionValidatesParentsEvenWithExplicitSubject: () => {
|
|
469
483
|
const c = fileCas(sha256)(home)
|
|
470
|
-
const [state0, cacheKey] =
|
|
484
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
471
485
|
const e = evo(c)(cacheKey)
|
|
472
486
|
const missingParent = vecToCBase32(vec8(0x66n))
|
|
473
487
|
const [, result] = virtual(state0)(e.add({ parents: [missingParent], subject: 'doc' }))
|
|
474
|
-
|
|
475
|
-
assert(result[0] === 'error' && result[1].includes('parent is not a revision blob'), ['unexpected message', result])
|
|
488
|
+
assertEvoError(result, 'parent is not a revision blob')
|
|
476
489
|
},
|
|
477
490
|
// A revision models one step in the evolution of a single subject: a
|
|
478
491
|
// parent that actually belongs to a *different* subject must be
|
|
@@ -482,27 +495,24 @@ export const proof = {
|
|
|
482
495
|
// never remove the parent from its real subject's head set.
|
|
483
496
|
addRevisionRejectsCrossSubjectParent: () => {
|
|
484
497
|
const c = fileCas(sha256)(home)
|
|
485
|
-
const [state0, cacheKey] =
|
|
498
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
486
499
|
const e = evo(c)(cacheKey)
|
|
487
500
|
/** @type {RevisionData} */
|
|
488
501
|
const rootA = { parents: [], subject: 'A', snapshot: vecToCBase32(vec8(0x67n)) }
|
|
489
|
-
const [state1, rootAResult] =
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
assertEq(result[0], 'error')
|
|
493
|
-
assert(result[0] === 'error' && result[1].includes('different subject'), ['unexpected message', result])
|
|
502
|
+
const [state1, rootAResult] = virtualOk(state0)(e.add(rootA))
|
|
503
|
+
const [, result] = virtual(state1)(e.add({ parents: [rootAResult], subject: 'B' }))
|
|
504
|
+
assertEvoError(result, 'different subject')
|
|
494
505
|
},
|
|
495
506
|
// With multiple parents, a failure on an earlier one short-circuits the
|
|
496
507
|
// fold — the second (here, a well-formed but nonexistent) parent is
|
|
497
508
|
// never even looked up.
|
|
498
509
|
addRevisionShortCircuitsOnFirstInvalidParentAmongMultiple: () => {
|
|
499
510
|
const c = fileCas(sha256)(home)
|
|
500
|
-
const [state0, cacheKey] =
|
|
511
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
501
512
|
const e = evo(c)(cacheKey)
|
|
502
513
|
const missingParent = vecToCBase32(vec8(0x99n))
|
|
503
514
|
const [, result] = virtual(state0)(e.add({ parents: ['not a valid cbase32!', missingParent], subject: 'doc' }))
|
|
504
|
-
|
|
505
|
-
assert(result[0] === 'error' && result[1].includes('invalid parent hash'), ['unexpected message', result])
|
|
515
|
+
assertEvoError(result, 'invalid parent hash')
|
|
506
516
|
},
|
|
507
517
|
// Two *valid* parents without an explicit `snapshot` cannot resolve one
|
|
508
518
|
// (no single parent snapshot to inherit) — a distinct failure from a
|
|
@@ -510,37 +520,35 @@ export const proof = {
|
|
|
510
520
|
// boundary now that the format requires an explicit `snapshot`.
|
|
511
521
|
addRevisionInvalidReferencesIsError: () => {
|
|
512
522
|
const c = fileCas(sha256)(home)
|
|
513
|
-
const [state0, cacheKey] =
|
|
523
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
514
524
|
const e = evo(c)(cacheKey)
|
|
515
525
|
/** @type {RevisionData} */
|
|
516
526
|
const rootA = { parents: [], subject: 'merge', snapshot: vecToCBase32(vec8(0x77n)) }
|
|
517
|
-
const [state1, rootAResult] =
|
|
518
|
-
assert(rootAResult[0] === 'ok', ['expected rootA ok', rootAResult])
|
|
527
|
+
const [state1, rootAResult] = virtualOk(state0)(e.add(rootA))
|
|
519
528
|
/** @type {RevisionData} */
|
|
520
529
|
const rootB = { parents: [], subject: 'merge', snapshot: vecToCBase32(vec8(0x88n)) }
|
|
521
|
-
const [state2, rootBResult] =
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
530
|
+
const [state2, rootBResult] = virtualOk(state1)(e.add(rootB))
|
|
531
|
+
const [, result] = virtual(state2)(e.add({ parents: [rootAResult, rootBResult], subject: 'merge' }))
|
|
532
|
+
assert(
|
|
533
|
+
result[0] === 'error' && result[1][0] === 'evoError'
|
|
534
|
+
&& !result[1][1].includes('parent is not a revision blob'),
|
|
535
|
+
['unexpected message', result])
|
|
526
536
|
},
|
|
527
537
|
addRevisionTooLargeToEncodeIsError: () => {
|
|
528
538
|
const c = fileCas(sha256)(home)
|
|
529
|
-
const [state0, cacheKey] =
|
|
539
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
530
540
|
const e = evo(c)(cacheKey)
|
|
531
541
|
const hugeSubject = 'x'.repeat(200_000)
|
|
532
542
|
/** @type {RevisionData} */
|
|
533
543
|
const input = { parents: [], subject: hugeSubject, snapshot: vecToCBase32(vec8(0x88n)) }
|
|
534
544
|
const [, result] = virtual(state0)(e.add(input))
|
|
535
|
-
|
|
536
|
-
assert(result[0] === 'error' && result[1] === 'revision too large to encode', ['unexpected message', result])
|
|
545
|
+
assertEvoError(result, 'revision too large to encode')
|
|
537
546
|
},
|
|
538
547
|
addRevisionWriteFailureIsError: () => {
|
|
539
|
-
const [, cacheKey] =
|
|
548
|
+
const [, cacheKey] = virtualOk(emptyState)(initEvo(writeFailingCas))
|
|
540
549
|
const e = evo(writeFailingCas)(cacheKey)
|
|
541
550
|
const [, result] = virtual(emptyState)(e.add({ parents: [], subject: vecToCBase32(vec8(0x99n)) }))
|
|
542
|
-
|
|
543
|
-
assert(result[0] === 'error' && result[1] === 'failed to write revision to CAS', ['unexpected message', result])
|
|
551
|
+
assertEvoError(result, 'failed to write revision to CAS')
|
|
544
552
|
},
|
|
545
553
|
// Every way `revision` can fail is its own message, not one `null`: an
|
|
546
554
|
// undecodable hash, a hash the store has nothing under, a read that failed
|
|
@@ -549,41 +557,37 @@ export const proof = {
|
|
|
549
557
|
// stores of arbitrary content); this read keeps them apart.
|
|
550
558
|
revisionInvalidHashIsError: () => {
|
|
551
559
|
const c = fileCas(sha256)(home)
|
|
552
|
-
const [state0, cacheKey] =
|
|
560
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
553
561
|
const e = evo(c)(cacheKey)
|
|
554
562
|
const [, result] = virtual(state0)(e.revision('not a valid cbase32!'))
|
|
555
|
-
|
|
556
|
-
assert(result[0] === 'error' && result[1].includes('invalid hash'), ['unexpected message', result])
|
|
563
|
+
assertEvoError(result, 'invalid hash')
|
|
557
564
|
},
|
|
558
565
|
revisionMissingHashIsError: () => {
|
|
559
566
|
const c = fileCas(sha256)(home)
|
|
560
|
-
const [state0, cacheKey] =
|
|
567
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
561
568
|
const e = evo(c)(cacheKey)
|
|
562
569
|
const [, result] = virtual(state0)(e.revision(vecToCBase32(vec8(0x9an))))
|
|
563
|
-
|
|
564
|
-
assert(result[0] === 'error' && result[1].includes('revision not found'), ['unexpected message', result])
|
|
570
|
+
assertEvoError(result, 'revision not found')
|
|
565
571
|
},
|
|
566
572
|
// Regression: a read failure that is not a missing shard — a permission or
|
|
567
573
|
// mid-stream I/O error, or a blob too large to buffer into one `Vec` —
|
|
568
574
|
// must not be reported as "not found". The blob may well be there; saying
|
|
569
575
|
// it is absent would be a false answer, not merely a vague one.
|
|
570
576
|
revisionReadFailureIsNotReportedAsMissing: () => {
|
|
571
|
-
const [state0, cacheKey] =
|
|
577
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(readFailingCas))
|
|
572
578
|
const e = evo(readFailingCas)(cacheKey)
|
|
573
579
|
const [, result] = virtual(state0)(e.revision(vecToCBase32(vec8(0x9bn))))
|
|
574
|
-
|
|
575
|
-
assert(result[0] === 'error' && result[1].includes('failed to read revision'), ['unexpected message', result])
|
|
580
|
+
assertEvoError(result, 'failed to read revision')
|
|
576
581
|
},
|
|
577
582
|
revisionNonRevisionBlobIsError: () => {
|
|
578
583
|
const c = fileCas(sha256)(home)
|
|
579
|
-
const [state0, cacheKey] =
|
|
584
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
580
585
|
const e = evo(c)(cacheKey)
|
|
581
586
|
const content = vec8(0x41n) // 'A' — valid UTF-8, not revision JSON
|
|
582
|
-
const [state1, w] = virtual(state0)(c.write(nonEmpty(
|
|
587
|
+
const [state1, w] = virtual(state0)(c.write(nonEmpty(content, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
|
|
583
588
|
assert(w[0] === 'ok', ['expected write ok', w])
|
|
584
589
|
const [, result] = virtual(state1)(e.revision(vecToCBase32(w[1])))
|
|
585
|
-
|
|
586
|
-
assert(result[0] === 'error' && result[1].includes('not a revision blob'), ['unexpected message', result])
|
|
590
|
+
assertEvoError(result, 'not a revision blob')
|
|
587
591
|
},
|
|
588
592
|
// A blob written by somebody else can spell its references in any accepted
|
|
589
593
|
// cbase32 alias (case, `i`/`l`/`o`). What `revision` returns is
|
|
@@ -591,7 +595,7 @@ export const proof = {
|
|
|
591
595
|
// directly instead of knowing about cbase32 aliasing.
|
|
592
596
|
revisionCanonicalizesReferenceSpellings: () => {
|
|
593
597
|
const c = fileCas(sha256)(home)
|
|
594
|
-
const [state0, cacheKey] =
|
|
598
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
595
599
|
const e = evo(c)(cacheKey)
|
|
596
600
|
const parentCanonical = vecToCBase32(vec8(0xffn))
|
|
597
601
|
const snapshotCanonical = vecToCBase32(vec8(0xfen))
|
|
@@ -603,7 +607,7 @@ export const proof = {
|
|
|
603
607
|
const text = `{"dialect":"${revisionDialect}","subject":"doc","parents":["${parentAlias}"],"snapshot":"${snapshotAlias}","generation":1}`
|
|
604
608
|
const bytes = tryUtf8(text)
|
|
605
609
|
assert(bytes !== null, 'expected the sample revision text to encode as UTF-8')
|
|
606
|
-
const [state1, w] = virtual(state0)(c.write(nonEmpty(
|
|
610
|
+
const [state1, w] = virtual(state0)(c.write(nonEmpty(bytes, /** @satisfies {List<never, Vec, IoChannel>} */ (elEmpty()))))
|
|
607
611
|
assert(w[0] === 'ok', ['expected write ok', w])
|
|
608
612
|
const [, result] = virtual(state1)(e.revision(vecToCBase32(w[1])))
|
|
609
613
|
assert(result[0] === 'ok', ['expected revision ok', result])
|
|
@@ -618,78 +622,160 @@ export const proof = {
|
|
|
618
622
|
// can be added again unchanged — same content, same hash.
|
|
619
623
|
revisionRoundTripsThroughAdd: () => {
|
|
620
624
|
const c = fileCas(sha256)(home)
|
|
621
|
-
const [state0, cacheKey] =
|
|
625
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
622
626
|
const e = evo(c)(cacheKey)
|
|
623
627
|
const subjectHash = vecToCBase32(vec8(0x74n))
|
|
624
|
-
const [state1, root] =
|
|
625
|
-
|
|
626
|
-
const [
|
|
627
|
-
assert(child[0] === 'ok', ['expected child ok', child])
|
|
628
|
-
const [state3, result] = virtual(state2)(e.revision(child[1]))
|
|
628
|
+
const [state1, root] = virtualOk(state0)(e.add({ parents: [], subject: subjectHash }))
|
|
629
|
+
const [state2, child] = virtualOk(state1)(e.add({ parents: [root], archived: true }))
|
|
630
|
+
const [state3, result] = virtual(state2)(e.revision(child))
|
|
629
631
|
assert(result[0] === 'ok', ['expected revision ok', result])
|
|
630
632
|
assertEq(result[1].subject, subjectHash)
|
|
631
633
|
assertEq(result[1].snapshot, subjectHash)
|
|
632
634
|
assertEq(result[1].generation, 1)
|
|
633
635
|
assertEq(result[1].archived, true)
|
|
634
636
|
assertEq(result[1].parents.length, 1)
|
|
635
|
-
assertEq(result[1].parents[0], root
|
|
636
|
-
const [, readded] =
|
|
637
|
-
|
|
638
|
-
assertEq(readded[1], child[1])
|
|
637
|
+
assertEq(result[1].parents[0], root)
|
|
638
|
+
const [, readded] = virtualOk(state3)(e.add(result[1]))
|
|
639
|
+
assertEq(readded, child)
|
|
639
640
|
},
|
|
640
641
|
revisionLockRoundTripsAndCanonicalizes: () => {
|
|
641
642
|
const c = fileCas(sha256)(home)
|
|
642
|
-
const [state0, cacheKey] =
|
|
643
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
643
644
|
const e = evo(c)(cacheKey)
|
|
644
645
|
const canonical = vecToCBase32(vec8(0xffn))
|
|
645
646
|
const alias = canonical.toUpperCase()
|
|
646
|
-
const [state1, added] =
|
|
647
|
+
const [state1, added] = virtualOk(state0)(e.add({
|
|
647
648
|
parents: [], subject: 'doc', snapshot: canonical,
|
|
648
649
|
lock: { dependency: alias },
|
|
649
650
|
}))
|
|
650
|
-
|
|
651
|
-
|
|
651
|
+
const [, result] = virtual(state1)(e.revision(added))
|
|
652
|
+
assert(result[0] === 'ok', ['expected revision ok', result])
|
|
653
|
+
const lock = result[1].lock
|
|
654
|
+
assert(typeof lock === 'object', ['expected an inline lock map', lock])
|
|
655
|
+
assertEq(lock.dependency, canonical)
|
|
656
|
+
},
|
|
657
|
+
// A `lock` naming a `vnd.fjs.lock` blob is stored and read back as the
|
|
658
|
+
// reference it is: validated as a hash and re-spelled canonically, like
|
|
659
|
+
// `snapshot`, and never followed — resolving one is a resolver's job.
|
|
660
|
+
revisionSharedLockReferenceRoundTripsAndCanonicalizes: () => {
|
|
661
|
+
const c = fileCas(sha256)(home)
|
|
662
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
663
|
+
const e = evo(c)(cacheKey)
|
|
664
|
+
const canonical = vecToCBase32(vec8(0xffn))
|
|
665
|
+
const alias = canonical.toUpperCase()
|
|
666
|
+
const [state1, added] = virtualOk(state0)(e.add({
|
|
667
|
+
parents: [], subject: 'doc', snapshot: canonical, lock: alias,
|
|
668
|
+
}))
|
|
669
|
+
const [state2, result] = virtual(state1)(e.revision(added))
|
|
670
|
+
assert(result[0] === 'ok', ['expected revision ok', result])
|
|
671
|
+
assertEq(result[1].lock, canonical)
|
|
672
|
+
const [, readded] = virtualOk(state2)(e.add(result[1]))
|
|
673
|
+
assertEq(readded, added)
|
|
674
|
+
},
|
|
675
|
+
// A shared-lock reference is checked as a hash on the way in, so a
|
|
676
|
+
// non-cBase32 one fails the write instead of being stored unresolvable.
|
|
677
|
+
revisionInvalidSharedLockReferenceIsRejected: () => {
|
|
678
|
+
const c = fileCas(sha256)(home)
|
|
679
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
680
|
+
const e = evo(c)(cacheKey)
|
|
681
|
+
const [, result] = virtual(state0)(e.add({
|
|
682
|
+
parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0xffn)),
|
|
683
|
+
lock: 'https://example.com/lock',
|
|
684
|
+
}))
|
|
685
|
+
assertEvoError(result, 'lock reference is not a valid hash')
|
|
686
|
+
},
|
|
687
|
+
// A nested lock map round-trips with its scope structure intact, and
|
|
688
|
+
// canonicalization recurses into it: an alias spelling bound inside a
|
|
689
|
+
// scope comes back canonical, like a direct one.
|
|
690
|
+
revisionNestedLockRoundTripsAndCanonicalizes: () => {
|
|
691
|
+
const c = fileCas(sha256)(home)
|
|
692
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
693
|
+
const e = evo(c)(cacheKey)
|
|
694
|
+
const canonical = vecToCBase32(vec8(0xffn))
|
|
695
|
+
const alias = canonical.toUpperCase()
|
|
696
|
+
const [state1, added] = virtualOk(state0)(e.add({
|
|
697
|
+
parents: [], subject: 'doc', snapshot: canonical,
|
|
698
|
+
lock: { B: { D: alias }, C: canonical },
|
|
699
|
+
}))
|
|
700
|
+
const [state2, result] = virtual(state1)(e.revision(added))
|
|
652
701
|
assert(result[0] === 'ok', ['expected revision ok', result])
|
|
653
|
-
|
|
702
|
+
const lock = result[1].lock
|
|
703
|
+
assert(typeof lock === 'object', ['expected an inline lock map', lock])
|
|
704
|
+
const scope = lock.B
|
|
705
|
+
assert(typeof scope === 'object', ['expected a nested scope', scope])
|
|
706
|
+
assertEq(scope.D, canonical)
|
|
707
|
+
// The read value is still valid `add` input, and re-adding it reuses
|
|
708
|
+
// the same address — the round trip loses nothing.
|
|
709
|
+
const [, readded] = virtualOk(state2)(e.add(result[1]))
|
|
710
|
+
assertEq(readded, added)
|
|
711
|
+
},
|
|
712
|
+
// Semantic checking recurses: a non-hash string inside a nested scope
|
|
713
|
+
// fails the write, like one at the root.
|
|
714
|
+
revisionInvalidNestedLockValueIsRejected: () => {
|
|
715
|
+
const c = fileCas(sha256)(home)
|
|
716
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
717
|
+
const e = evo(c)(cacheKey)
|
|
718
|
+
const [, result] = virtual(state0)(e.add({
|
|
719
|
+
parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x42n)),
|
|
720
|
+
lock: { B: { dependency: 'not a hash!' } },
|
|
721
|
+
}))
|
|
722
|
+
assertEvoError(result, 'B/dependency')
|
|
654
723
|
},
|
|
655
724
|
revisionAbsentAndEmptyLocksRemainDistinct: () => {
|
|
656
725
|
const c = fileCas(sha256)(home)
|
|
657
|
-
const [state0, cacheKey] =
|
|
726
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
658
727
|
const e = evo(c)(cacheKey)
|
|
659
728
|
const snapshot = vecToCBase32(vec8(0x42n))
|
|
660
|
-
const [state1, absent] =
|
|
661
|
-
|
|
662
|
-
const [
|
|
663
|
-
assert(empty[0] === 'ok', ['expected add ok', empty])
|
|
664
|
-
const [state3, absentRead] = virtual(state2)(e.revision(absent[1]))
|
|
729
|
+
const [state1, absent] = virtualOk(state0)(e.add({ parents: [], subject: 'absent', snapshot }))
|
|
730
|
+
const [state2, empty] = virtualOk(state1)(e.add({ parents: [], subject: 'empty', snapshot, lock: {} }))
|
|
731
|
+
const [state3, absentRead] = virtual(state2)(e.revision(absent))
|
|
665
732
|
assert(absentRead[0] === 'ok', ['expected revision ok', absentRead])
|
|
666
|
-
const [, emptyRead] = virtual(state3)(e.revision(empty
|
|
733
|
+
const [, emptyRead] = virtual(state3)(e.revision(empty))
|
|
667
734
|
assert(emptyRead[0] === 'ok', ['expected revision ok', emptyRead])
|
|
668
735
|
assertEq(absentRead[1].lock, undefined)
|
|
669
736
|
assertEq(Object.keys(emptyRead[1].lock ?? {}).length, 0)
|
|
670
737
|
},
|
|
671
738
|
revisionInvalidLockValueIsRejected: () => {
|
|
672
739
|
const c = fileCas(sha256)(home)
|
|
673
|
-
const [state0, cacheKey] =
|
|
740
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
674
741
|
const e = evo(c)(cacheKey)
|
|
675
742
|
const [, result] = virtual(state0)(e.add({
|
|
676
743
|
parents: [], subject: 'doc', snapshot: vecToCBase32(vec8(0x42n)),
|
|
677
744
|
lock: { dependency: 'not a hash!' },
|
|
678
745
|
}))
|
|
679
|
-
|
|
746
|
+
assertEvoError(result, 'dependency')
|
|
680
747
|
},
|
|
681
748
|
equivalentLockOrdersReuseOneCasAddress: () => {
|
|
682
749
|
const c = fileCas(sha256)(home)
|
|
683
|
-
const [state0, cacheKey] =
|
|
750
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
751
|
+
const e = evo(c)(cacheKey)
|
|
752
|
+
const a = vecToCBase32(vec8(0x1an))
|
|
753
|
+
const b = vecToCBase32(vec8(0x2bn))
|
|
754
|
+
const snapshot = vecToCBase32(vec8(0x3cn))
|
|
755
|
+
const [state1, first] = virtualOk(state0)(e.add({ parents: [], subject: 'doc', snapshot, lock: { '2': b, '10': a } }))
|
|
756
|
+
const [, second] = virtualOk(state1)(e.add({ lock: { '10': a, '2': b }, snapshot, subject: 'doc', parents: [] }))
|
|
757
|
+
assertEq(second, first)
|
|
758
|
+
},
|
|
759
|
+
// Canonical serialization reaches into nested scopes, so two adds
|
|
760
|
+
// differing only in the property order of a nested map — at the root and
|
|
761
|
+
// inside the scope alike — deduplicate to one blob rather than leaving
|
|
762
|
+
// two heads.
|
|
763
|
+
equivalentNestedLockOrdersReuseOneCasAddress: () => {
|
|
764
|
+
const c = fileCas(sha256)(home)
|
|
765
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
684
766
|
const e = evo(c)(cacheKey)
|
|
685
767
|
const a = vecToCBase32(vec8(0x1an))
|
|
686
768
|
const b = vecToCBase32(vec8(0x2bn))
|
|
687
769
|
const snapshot = vecToCBase32(vec8(0x3cn))
|
|
688
|
-
const [state1, first] =
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
770
|
+
const [state1, first] = virtualOk(state0)(e.add({
|
|
771
|
+
parents: [], subject: 'doc', snapshot,
|
|
772
|
+
lock: { B: { '2': b, '10': a }, A: a },
|
|
773
|
+
}))
|
|
774
|
+
const [, second] = virtualOk(state1)(e.add({
|
|
775
|
+
parents: [], subject: 'doc', snapshot,
|
|
776
|
+
lock: { A: a, B: { '10': a, '2': b } },
|
|
777
|
+
}))
|
|
778
|
+
assertEq(second, first)
|
|
693
779
|
},
|
|
694
780
|
// A raw CAS write (e.g. `cas_add`) of valid revision content is folded
|
|
695
781
|
// into the cache exactly as `addRevision` would, without going through
|
|
@@ -697,29 +783,29 @@ export const proof = {
|
|
|
697
783
|
// same store consistent (see `fjs/mcp`).
|
|
698
784
|
syncRevisionFoldsValidRevisionIntoCache: () => {
|
|
699
785
|
const c = fileCas(sha256)(home)
|
|
700
|
-
const [state0, cacheKey] =
|
|
786
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
701
787
|
const subjectHash = vecToCBase32(vec8(0x13n))
|
|
702
788
|
const text = `{"dialect":"${revisionDialect}","subject":"${subjectHash}","parents":[],"snapshot":"${subjectHash}","generation":0}`
|
|
703
789
|
const bytes = tryUtf8(text)
|
|
704
790
|
assert(bytes !== null, 'expected sample revision to encode as UTF-8')
|
|
705
791
|
const hashVec = vec8(0x14n)
|
|
706
|
-
const [state1] =
|
|
707
|
-
const [, heads] =
|
|
792
|
+
const [state1] = virtualOk(state0)(syncRevision(cacheKey)(hashVec)(bytes))
|
|
793
|
+
const [, heads] = virtualOk(state1)(evo(c)(cacheKey).head(subjectHash))
|
|
708
794
|
assertEq(heads.length, 1)
|
|
709
795
|
assertEq(heads[0], vecToCBase32(hashVec))
|
|
710
796
|
},
|
|
711
797
|
syncRevisionIgnoresNonRevisionContent: () => {
|
|
712
798
|
const c = fileCas(sha256)(home)
|
|
713
|
-
const [state0, cacheKey] =
|
|
714
|
-
const [state1] =
|
|
715
|
-
const [, subjects] =
|
|
799
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
800
|
+
const [state1] = virtualOk(state0)(syncRevision(cacheKey)(vec8(0x15n))(vec8(0x41n)))
|
|
801
|
+
const [, subjects] = virtualOk(state1)(evo(c)(cacheKey).list())
|
|
716
802
|
assertEq(subjects.length, 0)
|
|
717
803
|
},
|
|
718
804
|
evoHeadUnknownSubjectIsEmpty: () => {
|
|
719
805
|
const c = fileCas(sha256)(home)
|
|
720
|
-
const [state0, cacheKey] =
|
|
806
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
721
807
|
const e = evo(c)(cacheKey)
|
|
722
|
-
const [, heads] =
|
|
808
|
+
const [, heads] = virtualOk(state0)(e.head('nope'))
|
|
723
809
|
assertEq(heads.length, 0)
|
|
724
810
|
},
|
|
725
811
|
// Regression: subjects are arbitrary caller-supplied strings, so one can
|
|
@@ -729,21 +815,20 @@ export const proof = {
|
|
|
729
815
|
// `.hashes` — the cache must use an own-property lookup instead.
|
|
730
816
|
evoHeadUnknownPrototypeNamedSubjectIsEmpty: () => {
|
|
731
817
|
const c = fileCas(sha256)(home)
|
|
732
|
-
const [state0, cacheKey] =
|
|
818
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
733
819
|
const e = evo(c)(cacheKey)
|
|
734
|
-
const [, heads] =
|
|
820
|
+
const [, heads] = virtualOk(state0)(e.head('constructor'))
|
|
735
821
|
assertEq(heads.length, 0)
|
|
736
822
|
},
|
|
737
823
|
evoAddAndHeadHandlePrototypeNamedSubject: () => {
|
|
738
824
|
const c = fileCas(sha256)(home)
|
|
739
|
-
const [state0, cacheKey] =
|
|
825
|
+
const [state0, cacheKey] = virtualOk(emptyState)(initEvo(c))
|
|
740
826
|
const e = evo(c)(cacheKey)
|
|
741
827
|
/** @type {RevisionData} */
|
|
742
828
|
const input = { parents: [], subject: 'toString', snapshot: vecToCBase32(vec8(0x16n)) }
|
|
743
|
-
const [state1, result] =
|
|
744
|
-
|
|
745
|
-
const [, heads] = virtual(state1)(e.head('toString'))
|
|
829
|
+
const [state1, result] = virtualOk(state0)(e.add(input))
|
|
830
|
+
const [, heads] = virtualOk(state1)(e.head('toString'))
|
|
746
831
|
assertEq(heads.length, 1)
|
|
747
|
-
assertEq(heads[0], result
|
|
832
|
+
assertEq(heads[0], result)
|
|
748
833
|
},
|
|
749
834
|
}
|