functionalscript 0.45.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 +10 -0
- package/fjs/asn.1/proof.f.mjs +16 -0
- package/fjs/basen/base128/module.f.mjs +22 -5
- 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 -3
- package/fjs/bnf/data/types.d.ts +20 -2
- package/fjs/bnf/descent/module.f.d.mts +28 -14
- package/fjs/bnf/descent/module.f.mjs +122 -77
- package/fjs/bnf/descent/proof.f.d.mts +2 -0
- package/fjs/bnf/descent/proof.f.mjs +117 -31
- package/fjs/bnf/descent/types.d.ts +12 -14
- package/fjs/bnf/ll1/module.f.d.mts +64 -22
- package/fjs/bnf/ll1/module.f.mjs +214 -154
- package/fjs/bnf/ll1/proof.f.d.mts +15 -2
- package/fjs/bnf/ll1/proof.f.mjs +323 -149
- package/fjs/bnf/ll1/types.d.ts +16 -24
- 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/testlib.f.d.mts +31 -0
- package/fjs/bnf/testlib.f.mjs +80 -0
- 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 +1 -3
- package/fjs/cas/cli/proof.f.mjs +44 -33
- package/fjs/cas/evo/module.f.d.mts +64 -18
- package/fjs/cas/evo/module.f.mjs +148 -70
- package/fjs/cas/evo/proof.f.d.mts +10 -1
- package/fjs/cas/evo/proof.f.mjs +305 -223
- package/fjs/cas/evo/types.d.ts +45 -9
- package/fjs/cas/module.f.d.mts +18 -32
- package/fjs/cas/module.f.mjs +129 -128
- 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/config/module.f.d.mts +2 -2
- package/fjs/ci/config/module.f.mjs +2 -2
- 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.mjs +2 -2
- package/fjs/ci/proof.f.mjs +9 -5
- package/fjs/cli/module.f.d.mts +4 -6
- package/fjs/cli/module.f.mjs +4 -8
- package/fjs/cli/proof.f.mjs +17 -16
- package/fjs/cli/types.d.ts +2 -3
- package/fjs/common/monoid/types.d.ts +1 -1
- package/fjs/crypto/hmac/module.f.mjs +2 -2
- package/fjs/crypto/sha2/module.f.mjs +3 -1
- package/fjs/crypto/sha2/proof.f.d.mts +1 -0
- package/fjs/crypto/sha2/proof.f.mjs +24 -0
- package/fjs/crypto/sha2/types.d.ts +11 -0
- package/fjs/crypto/sign/module.f.mjs +2 -2
- package/fjs/dev/module.f.d.mts +13 -4
- package/fjs/dev/module.f.mjs +56 -27
- package/fjs/dev/update/module.f.d.mts +9 -4
- package/fjs/dev/update/module.f.mjs +14 -10
- package/fjs/dev/update/proof.f.d.mts +1 -3
- package/fjs/dev/update/proof.f.mjs +10 -5
- 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 +13 -3
- package/fjs/djs/parser/module.f.mjs +117 -16
- package/fjs/djs/parser/proof.f.d.mts +5 -1
- package/fjs/djs/parser/proof.f.mjs +274 -12
- package/fjs/djs/parser/types.d.ts +7 -1
- package/fjs/djs/proof.f.d.mts +18 -2
- package/fjs/djs/proof.f.mjs +187 -12
- package/fjs/djs/serializer/module.f.d.mts +25 -8
- package/fjs/djs/serializer/module.f.mjs +61 -15
- 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 +99 -60
- package/fjs/djs/tokenizer/proof.f.d.mts +1 -1
- package/fjs/djs/tokenizer/proof.f.mjs +74 -67
- package/fjs/djs/transpiler/module.f.d.mts +12 -7
- package/fjs/djs/transpiler/module.f.mjs +82 -56
- package/fjs/djs/transpiler/types.d.ts +7 -3
- package/fjs/djs/types.d.ts +7 -1
- package/fjs/effects/list/module.f.d.mts +14 -11
- package/fjs/effects/list/module.f.mjs +13 -11
- package/fjs/effects/list/types.d.ts +27 -7
- package/fjs/effects/memory/module.f.d.mts +2 -1
- package/fjs/effects/memory/module.f.mjs +3 -4
- package/fjs/effects/memory/proof.f.mjs +13 -7
- package/fjs/effects/memory/types.d.ts +4 -3
- package/fjs/effects/mock/module.f.d.mts +20 -4
- package/fjs/effects/mock/module.f.mjs +37 -5
- package/fjs/effects/mock/types.d.ts +10 -1
- package/fjs/effects/module.d.mts +4 -2
- package/fjs/effects/module.f.d.mts +466 -280
- package/fjs/effects/module.f.mjs +537 -299
- package/fjs/effects/module.mjs +2 -1
- package/fjs/effects/node/memory/module.d.mts +4 -2
- package/fjs/effects/node/memory/module.mjs +6 -3
- package/fjs/effects/node/memory/proof.mjs +10 -4
- package/fjs/effects/node/module.d.mts +4 -3
- package/fjs/effects/node/module.f.d.mts +168 -33
- package/fjs/effects/node/module.f.mjs +256 -52
- package/fjs/effects/node/module.mjs +57 -34
- 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 +18 -4
- package/fjs/effects/node/virtual/module.f.mjs +110 -68
- package/fjs/effects/node/virtual/proof.f.d.mts +28 -2
- package/fjs/effects/node/virtual/proof.f.mjs +190 -9
- package/fjs/effects/proof.f.d.mts +69 -37
- package/fjs/effects/proof.f.mjs +410 -130
- package/fjs/effects/types.d.ts +161 -33
- package/fjs/emergent_testing/module.f.d.mts +19 -12
- package/fjs/emergent_testing/module.f.mjs +93 -33
- package/fjs/emergent_testing/proof.f.d.mts +21 -7
- package/fjs/emergent_testing/proof.f.mjs +166 -32
- package/fjs/emergent_testing/types.d.ts +22 -4
- package/fjs/fsm/module.f.d.mts +14 -4
- package/fjs/fsm/module.f.mjs +54 -37
- package/fjs/fsm/proof.f.d.mts +2 -0
- package/fjs/fsm/proof.f.mjs +83 -114
- package/fjs/js/keywords/module.f.d.mts +52 -0
- package/fjs/js/keywords/module.f.mjs +72 -0
- package/fjs/js/keywords/proof.f.d.mts +3 -0
- package/fjs/js/keywords/proof.f.mjs +13 -0
- package/fjs/js/tokenizer/module.f.d.mts +26 -6
- package/fjs/js/tokenizer/module.f.mjs +145 -159
- 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 +4 -4
- package/fjs/mcp/module.f.mjs +6 -6
- package/fjs/mcp/proof.f.d.mts +5 -3
- package/fjs/mcp/proof.f.mjs +112 -46
- package/fjs/media/html/module.f.mjs +1 -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 +114 -70
- 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 +33 -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.mjs +9 -9
- package/fjs/media/json/schema/module.f.mjs +3 -13
- package/fjs/media/json/schema/proof.f.d.mts +0 -1
- package/fjs/media/json/schema/proof.f.mjs +1 -2
- package/fjs/media/json/serializer/module.f.d.mts +32 -1
- package/fjs/media/json/serializer/module.f.mjs +64 -2
- package/fjs/media/json/tokenizer/module.f.mjs +7 -3
- 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/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 +78 -7
- package/fjs/media/revision/module.f.mjs +119 -12
- package/fjs/media/revision/proof.f.d.mts +10 -0
- package/fjs/media/revision/proof.f.mjs +88 -0
- package/fjs/media/revision/types.d.ts +34 -5
- package/fjs/media/type/module.f.d.mts +33 -15
- package/fjs/media/type/module.f.mjs +35 -29
- package/fjs/media/type/proof.f.d.mts +2 -1
- package/fjs/media/type/proof.f.mjs +30 -10
- package/fjs/module.f.mjs +29 -8
- 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/proof.f.d.mts +3 -3
- package/fjs/proof.f.mjs +36 -10
- package/fjs/protocol/json_rpc/module.f.d.mts +2 -2
- package/fjs/protocol/json_rpc/module.f.mjs +3 -3
- package/fjs/protocol/json_rpc/proof.f.mjs +4 -4
- 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 +13 -4
- package/fjs/protocol/mcp/proof.f.mjs +193 -61
- package/fjs/protocol/mcp/stdio/module.f.d.mts +15 -7
- package/fjs/protocol/mcp/stdio/module.f.mjs +38 -25
- package/fjs/protocol/mcp/stdio/proof.f.d.mts +4 -2
- package/fjs/protocol/mcp/stdio/proof.f.mjs +45 -12
- package/fjs/protocol/mcp/stdio/types.d.ts +1 -1
- package/fjs/protocol/mcp/types.d.ts +17 -6
- package/fjs/sul/id/module.f.d.mts +0 -1
- package/fjs/sul/id/module.f.mjs +2 -3
- package/fjs/sul/level/hash/module.f.mjs +2 -1
- package/fjs/sul/level/hash/proof.f.mjs +2 -2
- package/fjs/sul/module.f.mjs +18 -13
- package/fjs/text/code_point/module.f.d.mts +8 -0
- package/fjs/text/code_point/module.f.mjs +8 -1
- package/fjs/text/code_point/proof.f.d.mts +1 -0
- package/fjs/text/code_point/proof.f.mjs +11 -0
- package/fjs/text/sgr/module.f.d.mts +4 -6
- package/fjs/text/sgr/module.f.mjs +6 -7
- package/fjs/text/utf16/module.f.mjs +7 -2
- package/fjs/text/utf16/proof.f.mjs +3 -3
- package/fjs/text/utf8/module.f.mjs +4 -2
- package/fjs/types/array/module.f.mjs +14 -2
- package/fjs/types/bit_vec/module.f.d.mts +0 -2
- package/fjs/types/bit_vec/module.f.mjs +46 -32
- package/fjs/types/bit_vec/proof.f.mjs +2 -2
- package/fjs/types/btree/remove/module.f.mjs +1 -1
- package/fjs/types/btree/set/module.f.mjs +9 -11
- package/fjs/types/btree/set/proof.f.mjs +12 -0
- package/fjs/types/byte_set/module.f.d.mts +11 -4
- package/fjs/types/byte_set/module.f.mjs +14 -6
- package/fjs/types/byte_set/proof.f.mjs +7 -7
- package/fjs/types/function/compare/module.f.mjs +10 -3
- package/fjs/types/list/module.f.d.mts +1 -1
- package/fjs/types/list/module.f.mjs +1 -1
- package/fjs/types/nullable/module.f.d.mts +18 -4
- package/fjs/types/nullable/module.f.mjs +21 -4
- package/fjs/types/nullable/proof.f.d.mts +4 -0
- package/fjs/types/nullable/proof.f.mjs +15 -0
- package/fjs/types/object/module.f.d.mts +12 -2
- package/fjs/types/object/module.f.mjs +11 -1
- package/fjs/types/patricia_trie/module.f.mjs +26 -11
- package/fjs/types/result/module.f.d.mts +3 -3
- package/fjs/types/result/module.f.mjs +3 -3
- package/fjs/types/rtti/common/module.f.d.mts +25 -28
- package/fjs/types/rtti/common/module.f.mjs +36 -32
- package/fjs/types/rtti/common/proof.f.mjs +5 -5
- package/fjs/types/rtti/data/module.f.d.mts +14 -0
- package/fjs/types/rtti/data/module.f.mjs +25 -2
- package/fjs/types/rtti/data/proof.f.d.mts +1 -0
- package/fjs/types/rtti/data/proof.f.mjs +25 -2
- package/fjs/types/rtti/parse/module.f.d.mts +24 -14
- package/fjs/types/rtti/parse/module.f.mjs +37 -28
- package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
- package/fjs/types/rtti/parse/proof.f.mjs +33 -14
- package/fjs/types/rtti/proof.f.mjs +3 -1
- package/fjs/types/rtti/ts/module.f.mjs +13 -17
- package/fjs/types/rtti/ts/types.d.ts +14 -1
- package/fjs/types/sorted_set/module.f.d.mts +18 -0
- package/fjs/types/sorted_set/module.f.mjs +22 -0
- package/fjs/types/sorted_set/proof.f.d.mts +1 -0
- package/fjs/types/sorted_set/proof.f.mjs +16 -1
- package/fjs/types/uint8array/module.f.d.mts +1 -1
- package/fjs/types/uint8array/module.f.mjs +1 -1
- package/fjs/website/module.f.d.mts +3 -3
- package/fjs/website/module.f.mjs +4 -7
- package/fjs/website/proof.f.mjs +2 -1
- package/package.json +2 -2
- package/fjs/dev/package_json/module.f.d.mts +0 -39
- package/fjs/dev/package_json/module.f.mjs +0 -40
- package/fjs/dev/package_json/proof.f.d.mts +0 -6
- package/fjs/dev/package_json/proof.f.mjs +0 -32
- package/fjs/effects/eff/module.f.d.mts +0 -20
- package/fjs/effects/eff/module.f.mjs +0 -70
- package/fjs/effects/eff/proof.f.d.mts +0 -15
- package/fjs/effects/eff/proof.f.mjs +0 -69
- package/fjs/effects/eff/types.d.ts +0 -71
- package/fjs/types/rtti/validate/module.f.d.mts +0 -41
- package/fjs/types/rtti/validate/module.f.mjs +0 -171
- 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/cas/evo/types.d.ts
CHANGED
|
@@ -4,11 +4,32 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { Effect, NotImplemented, Operation } from '../../effects/types.ts';
|
|
8
8
|
import type { MemOp } from '../../effects/memory/types.ts';
|
|
9
|
-
import type { Result } from '../../types/result/types.ts';
|
|
10
9
|
import type { StringMap } from '../../types/object/types.ts';
|
|
11
|
-
import type {
|
|
10
|
+
import type { LockField } from '../../media/revision/types.ts';
|
|
11
|
+
/**
|
|
12
|
+
* A revision was rejected on its own terms: a parent that does not resolve, a
|
|
13
|
+
* hash that is not cBase32, a blob that holds something other than a
|
|
14
|
+
* `vnd.fjs.revision`, a store that would not take the write.
|
|
15
|
+
*
|
|
16
|
+
* A tagged tuple rather than a bare `string` so it joins {@link NotImplemented}
|
|
17
|
+
* and `IoError` as a discriminated union rather than a structural one — a
|
|
18
|
+
* renderer switches on the tag it already switches on, instead of testing
|
|
19
|
+
* `typeof`.
|
|
20
|
+
*/
|
|
21
|
+
export type EvoError = readonly ['evoError', string];
|
|
22
|
+
/**
|
|
23
|
+
* The error channel of the Evo API: the revision was rejected, or the runner
|
|
24
|
+
* could not dispatch an operation.
|
|
25
|
+
*
|
|
26
|
+
* Host IO failures do **not** appear here as `IoError`. A store that cannot be
|
|
27
|
+
* read or written is reported as an {@link EvoError} whose message says so,
|
|
28
|
+
* because from this API's outside a blob that cannot be delivered and a blob
|
|
29
|
+
* that is not a revision are the same answer — "no revision at that hash" —
|
|
30
|
+
* and the caller has the same recourse to both.
|
|
31
|
+
*/
|
|
32
|
+
export type EvoChannel = EvoError | NotImplemented;
|
|
12
33
|
/** A cBase32 content hash, as accepted/returned by `Cas<O>`. */
|
|
13
34
|
export type Hash = string;
|
|
14
35
|
/** The identity of a mutable object whose revisions are being evolved. */
|
|
@@ -38,6 +59,12 @@ export type Subject = string;
|
|
|
38
59
|
* - `generation` — input: **ignored**, {@link computeGeneration} derives the
|
|
39
60
|
* authoritative value from the parents; output: always present. It exists as
|
|
40
61
|
* an input field only so a read value round-trips into `add` as-is.
|
|
62
|
+
* - `lock` — the format's own field, so it is an inline map or the hash of a
|
|
63
|
+
* `vnd.fjs.lock` blob (`fjs/media/lock`) holding one. Both directions
|
|
64
|
+
* validate and canonicalize every hash it names — the reference itself, or
|
|
65
|
+
* each direct value at every depth of a map. Following a reference is not
|
|
66
|
+
* done here: evo stores and reads revisions, it does not resolve
|
|
67
|
+
* dependencies.
|
|
41
68
|
*
|
|
42
69
|
* Relaxing what the format requires is the point, not an oversight. The stored
|
|
43
70
|
* `vnd.fjs.revision` blob requires `subject`, `snapshot` and `generation`, and
|
|
@@ -58,7 +85,7 @@ export type RevisionData = {
|
|
|
58
85
|
readonly subject?: Subject | undefined;
|
|
59
86
|
readonly archived?: true | undefined;
|
|
60
87
|
readonly generation?: number | undefined;
|
|
61
|
-
readonly lock?:
|
|
88
|
+
readonly lock?: LockField | undefined;
|
|
62
89
|
};
|
|
63
90
|
/**
|
|
64
91
|
* Per-subject bookkeeping: every revision hash seen for the subject, every
|
|
@@ -94,11 +121,20 @@ export type Evo<O extends Operation> = {
|
|
|
94
121
|
* adding it later is a compatible extension of this parameter, while
|
|
95
122
|
* removing it would not be.
|
|
96
123
|
*/
|
|
97
|
-
readonly list: (archived?: true) => Effect<MemOp, readonly Subject[]>;
|
|
124
|
+
readonly list: (archived?: true) => Effect<MemOp, readonly Subject[], NotImplemented>;
|
|
98
125
|
/** Returns the current head hashes of `subject` (empty if unknown). */
|
|
99
|
-
readonly head: (subject: Subject) => Effect<MemOp, readonly Hash[]>;
|
|
100
|
-
/**
|
|
101
|
-
|
|
126
|
+
readonly head: (subject: Subject) => Effect<MemOp, readonly Hash[], NotImplemented>;
|
|
127
|
+
/**
|
|
128
|
+
* Adds a new head; see {@link addRevision}.
|
|
129
|
+
*
|
|
130
|
+
* A rejected revision and an undispatchable operation are both failures and
|
|
131
|
+
* share the channel, as {@link EvoChannel}'s two cases. They stay tellable
|
|
132
|
+
* apart — that is what the tags are for, and a caller that renders them
|
|
133
|
+
* differently switches on one — but they do not need separate *layers* to
|
|
134
|
+
* be told apart, and giving them separate layers costs every intermediate
|
|
135
|
+
* step a hand-written short-circuit that `step` would otherwise do.
|
|
136
|
+
*/
|
|
137
|
+
readonly add: (input: RevisionData) => Effect<O | MemOp, Hash, EvoChannel>;
|
|
102
138
|
/**
|
|
103
139
|
* The revision at `hash`, decoded, validated, and canonicalized; see
|
|
104
140
|
* {@link readRevision}. Served from the store today, so the `MemOp` in the
|
|
@@ -107,5 +143,5 @@ export type Evo<O extends Operation> = {
|
|
|
107
143
|
* operations read, which must not become a breaking change to this type
|
|
108
144
|
* when it happens.
|
|
109
145
|
*/
|
|
110
|
-
readonly revision: (hash: Hash) => Effect<O | MemOp,
|
|
146
|
+
readonly revision: (hash: Hash) => Effect<O | MemOp, RevisionData, EvoChannel>;
|
|
111
147
|
};
|
package/fjs/cas/module.f.d.mts
CHANGED
|
@@ -5,24 +5,19 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @import { Sha2, State as Sha2State } from '../crypto/sha2/types.ts'
|
|
7
7
|
* @import { Vec } from '../types/bit_vec/types.ts'
|
|
8
|
-
* @import {
|
|
9
|
-
* @import {
|
|
10
|
-
*
|
|
11
|
-
* Now,
|
|
12
|
-
* RandomInt,
|
|
13
|
-
* ReadBytes,
|
|
14
|
-
* Readdir,
|
|
15
|
-
* Rm,
|
|
16
|
-
* } from '../effects/node/types.ts'
|
|
8
|
+
* @import { Operation } from '../effects/types.ts'
|
|
9
|
+
* @import { Effect, NotImplemented } from '../effects/types.ts'
|
|
10
|
+
* @import { IoChannel, Now, RandomInt, ReadBytes, Readdir, Rm } from '../effects/node/types.ts'
|
|
17
11
|
* @import { List } from '../effects/list/types.ts'
|
|
18
12
|
* @import { Cas, FileCas, FileCasOperation } from './types.ts'
|
|
19
13
|
*/
|
|
20
14
|
import type { Sha2 } from '../crypto/sha2/types.ts';
|
|
21
15
|
import type { Vec } from '../types/bit_vec/types.ts';
|
|
22
|
-
import type {
|
|
23
|
-
import type {
|
|
16
|
+
import type { Operation } from '../effects/types.ts';
|
|
17
|
+
import type { Effect } from '../effects/types.ts';
|
|
18
|
+
import type { IoChannel, ReadBytes } from '../effects/node/types.ts';
|
|
24
19
|
import type { List } from '../effects/list/types.ts';
|
|
25
|
-
import type { Cas, FileCas
|
|
20
|
+
import type { Cas, FileCas } from './types.ts';
|
|
26
21
|
/** Converts a content key to its sharded relative CAS file path.
|
|
27
22
|
*
|
|
28
23
|
* @type {(key: Vec) => string}
|
|
@@ -31,17 +26,16 @@ export declare const toPath: (key: Vec) => string;
|
|
|
31
26
|
/**
|
|
32
27
|
* Drains a `Cas<O>.read` chunk stream into a single `Vec`. Used by any caller
|
|
33
28
|
* that needs the whole blob at once (an MCP `content: true` fetch, an Evo
|
|
34
|
-
* revision decode): the chunk stream is concatenated and
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* process.
|
|
29
|
+
* revision decode): the chunk stream is concatenated, and a stream that fails
|
|
30
|
+
* fails this. A single `Vec` cannot exceed `maxLength` bits; concatenating past
|
|
31
|
+
* it would overflow the runtime's `bigint` constraint, so that case fails here
|
|
32
|
+
* too rather than crashing the process.
|
|
39
33
|
*
|
|
40
34
|
* @template {Operation} O
|
|
41
|
-
* @param {List<O,
|
|
42
|
-
* @returns {Effect<O,
|
|
35
|
+
* @param {List<O, Vec, IoChannel>} stream
|
|
36
|
+
* @returns {Effect<O, Vec, IoChannel>}
|
|
43
37
|
*/
|
|
44
|
-
export declare const collectRead: <O extends Operation>(stream: List<O,
|
|
38
|
+
export declare const collectRead: <O extends Operation>(stream: List<O, Vec, IoChannel>) => Effect<O, Vec, IoChannel>;
|
|
45
39
|
/**
|
|
46
40
|
* Builds a content-addressable storage facade from a SHA-2 implementation.
|
|
47
41
|
*
|
|
@@ -50,19 +44,11 @@ export declare const collectRead: <O extends Operation>(stream: List<O, IoResult
|
|
|
50
44
|
export declare const fileCas: (sha2: Sha2) => (path: string) => FileCas;
|
|
51
45
|
/**
|
|
52
46
|
* Streams the file at `path` through `cas.write`, returning the content hash.
|
|
53
|
-
*
|
|
54
|
-
*
|
|
47
|
+
* Its one consumer is the CLI's `cas add`; the MCP `cas_add` tool takes inline
|
|
48
|
+
* content and calls `write` itself, so it does not come through here.
|
|
55
49
|
*
|
|
56
50
|
* @template {Operation} O
|
|
57
51
|
* @param {Cas<O>} cas
|
|
58
|
-
* @returns {(path: string) => Effect<O | ReadBytes,
|
|
52
|
+
* @returns {(path: string) => Effect<O | ReadBytes, Vec, IoChannel>}
|
|
59
53
|
*/
|
|
60
|
-
export declare const casAddFile: <O extends Operation>(cas: Cas<O>) => (path: string) => Effect<O | ReadBytes,
|
|
61
|
-
/**
|
|
62
|
-
* Upload pipeline: streams `fileName` from `~/cas_upload/` through `casAddFile`,
|
|
63
|
-
* then deletes the source on success. On failure the source is left in place so
|
|
64
|
-
* the upload can be retried; `write` already cleans up its own partial staging file.
|
|
65
|
-
*
|
|
66
|
-
* @type {(home: string) => (fileName: string) => Effect<FileCasOperation, IoResult<Vec>>}
|
|
67
|
-
*/
|
|
68
|
-
export declare const casUpload: (home: string) => (fileName: string) => Effect<FileCasOperation, IoResult<Vec>>;
|
|
54
|
+
export declare const casAddFile: <O extends Operation>(cas: Cas<O>) => (path: string) => Effect<O | ReadBytes, Vec, IoChannel>;
|
package/fjs/cas/module.f.mjs
CHANGED
|
@@ -5,27 +5,33 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @import { Sha2, State as Sha2State } from '../crypto/sha2/types.ts'
|
|
7
7
|
* @import { Vec } from '../types/bit_vec/types.ts'
|
|
8
|
-
* @import {
|
|
9
|
-
* @import {
|
|
10
|
-
*
|
|
11
|
-
* Now,
|
|
12
|
-
* RandomInt,
|
|
13
|
-
* ReadBytes,
|
|
14
|
-
* Readdir,
|
|
15
|
-
* Rm,
|
|
16
|
-
* } from '../effects/node/types.ts'
|
|
8
|
+
* @import { Operation } from '../effects/types.ts'
|
|
9
|
+
* @import { Effect, NotImplemented } from '../effects/types.ts'
|
|
10
|
+
* @import { IoChannel, Now, RandomInt, ReadBytes, Readdir, Rm } from '../effects/node/types.ts'
|
|
17
11
|
* @import { List } from '../effects/list/types.ts'
|
|
18
12
|
* @import { Cas, FileCas, FileCasOperation } from './types.ts'
|
|
19
13
|
*/
|
|
20
14
|
|
|
21
|
-
import { sha256 } from '../crypto/sha2/module.f.mjs'
|
|
22
15
|
import { join, normalize, parse } from '../path/module.f.mjs'
|
|
23
16
|
import { empty, length, maxLength, maxLengthBytes, msb, vec } from '../types/bit_vec/module.f.mjs'
|
|
24
17
|
import { cBase32ToVec, vecToCBase32 } from '../basen/cbase32/module.f.mjs'
|
|
25
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
catchStep,
|
|
20
|
+
foldStep,
|
|
21
|
+
forEachStep,
|
|
22
|
+
history,
|
|
23
|
+
historyStep,
|
|
24
|
+
mapStep as ioMapStep,
|
|
25
|
+
pureError,
|
|
26
|
+
pureOk,
|
|
27
|
+
resultMapStep,
|
|
28
|
+
resultStep,
|
|
29
|
+
step as ioStep,
|
|
30
|
+
} from '../effects/module.f.mjs'
|
|
26
31
|
import {
|
|
27
32
|
access,
|
|
28
33
|
createExclusive,
|
|
34
|
+
ioError,
|
|
29
35
|
isNotFound,
|
|
30
36
|
mkdir,
|
|
31
37
|
now,
|
|
@@ -38,7 +44,7 @@ import {
|
|
|
38
44
|
writeBytes,
|
|
39
45
|
} from '../effects/node/module.f.mjs'
|
|
40
46
|
import { toOption } from '../types/nullable/module.f.mjs'
|
|
41
|
-
import { error, ok
|
|
47
|
+
import { error, ok } from '../types/result/module.f.mjs'
|
|
42
48
|
import { splitAt } from '../types/string/module.f.mjs'
|
|
43
49
|
import { nonEmpty, empty as elEmpty } from '../effects/list/module.f.mjs'
|
|
44
50
|
|
|
@@ -60,28 +66,25 @@ export const toPath = key => {
|
|
|
60
66
|
/**
|
|
61
67
|
* Drains a `Cas<O>.read` chunk stream into a single `Vec`. Used by any caller
|
|
62
68
|
* that needs the whole blob at once (an MCP `content: true` fetch, an Evo
|
|
63
|
-
* revision decode): the chunk stream is concatenated and
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* process.
|
|
69
|
+
* revision decode): the chunk stream is concatenated, and a stream that fails
|
|
70
|
+
* fails this. A single `Vec` cannot exceed `maxLength` bits; concatenating past
|
|
71
|
+
* it would overflow the runtime's `bigint` constraint, so that case fails here
|
|
72
|
+
* too rather than crashing the process.
|
|
68
73
|
*
|
|
69
74
|
* @template {Operation} O
|
|
70
|
-
* @param {List<O,
|
|
71
|
-
* @returns {Effect<O,
|
|
75
|
+
* @param {List<O, Vec, IoChannel>} stream
|
|
76
|
+
* @returns {Effect<O, Vec, IoChannel>}
|
|
72
77
|
*/
|
|
73
78
|
export const collectRead = stream => {
|
|
74
|
-
/** @type {(acc: Vec) => (s: List<O,
|
|
79
|
+
/** @type {(acc: Vec) => (s: List<O, Vec, IoChannel>) => Effect<O, Vec, IoChannel>} */
|
|
75
80
|
const loop = acc => s =>
|
|
76
|
-
|
|
77
|
-
if (node === undefined) { return
|
|
81
|
+
ioStep(s, node => {
|
|
82
|
+
if (node === undefined) { return pureOk(acc) }
|
|
78
83
|
const { first, tail } = node
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (length(acc) + length(v) > maxLength) {
|
|
82
|
-
return pure(error(`cas blob exceeds maximum vector length of ${maxLength} bits`))
|
|
84
|
+
if (length(acc) + length(first) > maxLength) {
|
|
85
|
+
return pureError(ioError({ message: `cas blob exceeds maximum vector length of ${maxLength} bits` }))
|
|
83
86
|
}
|
|
84
|
-
return loop(msb.concat(acc)(
|
|
87
|
+
return loop(msb.concat(acc)(first))(tail)
|
|
85
88
|
})
|
|
86
89
|
return loop(empty)(stream)
|
|
87
90
|
}
|
|
@@ -126,9 +129,11 @@ const deadlineOf = name => Number(name.slice(0, name.indexOf('-')))
|
|
|
126
129
|
* still-live lease is left alone; the fencing rename keeps even a misjudged
|
|
127
130
|
* reclaim fail-safe (worst case: that upload restarts).
|
|
128
131
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
+
* The error channel is `never`, and that is the claim this function makes: it
|
|
133
|
+
* catches every failure it can produce, so an upload composing it cannot fail
|
|
134
|
+
* because of a sweep.
|
|
135
|
+
*
|
|
136
|
+
* @type {(stageDir: string) => Effect<Now | Readdir | Rm, void, never>}
|
|
132
137
|
*/
|
|
133
138
|
const gcStage = stageDir => {
|
|
134
139
|
// The `readdir` result and the `now()` timestamp are both needed by the last
|
|
@@ -137,16 +142,20 @@ const gcStage = stageDir => {
|
|
|
137
142
|
const listed = historyStep(
|
|
138
143
|
history(now()),
|
|
139
144
|
() => readdir(stageDir, {}))
|
|
140
|
-
|
|
145
|
+
const swept = ioStep(
|
|
141
146
|
listed,
|
|
142
|
-
([
|
|
143
|
-
|
|
144
|
-
const expired = v.flatMap(d =>
|
|
147
|
+
([dirents, t]) => {
|
|
148
|
+
const expired = dirents.flatMap(d =>
|
|
145
149
|
d.isFile && deadlineOf(d.name) < t ? [d.name] : [])
|
|
150
|
+
// Each `rm` recovers on its own, so one undeletable file does not
|
|
151
|
+
// stop the sweep at the file before it.
|
|
146
152
|
return forEachStep(
|
|
147
|
-
|
|
148
|
-
name =>
|
|
153
|
+
pureOk(expired),
|
|
154
|
+
name => catchStep(rm(join(stageDir, name)), () => pureOk(undefined)))
|
|
149
155
|
})
|
|
156
|
+
// A fresh store has no `_stage/` yet, and a sweep that cannot run must not
|
|
157
|
+
// fail the upload it is piggy-backed on.
|
|
158
|
+
return catchStep(swept, () => pureOk(undefined))
|
|
150
159
|
}
|
|
151
160
|
|
|
152
161
|
// Lock-free staging upload (plan/staging-lease.md): stream each chunk straight
|
|
@@ -162,73 +171,84 @@ const gcStage = stageDir => {
|
|
|
162
171
|
* @param {Sha2} sha2
|
|
163
172
|
* @param {string} path
|
|
164
173
|
* @param {string} stageDir
|
|
165
|
-
* @param {List<O1,
|
|
166
|
-
* @returns {Effect<O1 | FileCasOperation,
|
|
174
|
+
* @param {List<O1, Vec, IoChannel>} payload
|
|
175
|
+
* @returns {Effect<O1 | FileCasOperation, Vec, IoChannel>}
|
|
167
176
|
*/
|
|
168
177
|
const writeImpl = (sha2, path, stageDir, payload) => {
|
|
169
178
|
// Publish the finished staging file to its content-addressed shard. The three
|
|
170
179
|
// filesystem steps run best-effort with their results ignored; success is decided
|
|
171
180
|
// afterward by observing the target's size (see staging-lease.md "Publish ignores
|
|
172
181
|
// results and checks the end state").
|
|
173
|
-
/** @type {(state: Sha2State, offset: number, curPath: string) => Effect<FileCasOperation,
|
|
182
|
+
/** @type {(state: Sha2State, offset: number, curPath: string) => Effect<FileCasOperation, Vec, IoChannel>} */
|
|
174
183
|
const publish = (state, offset, curPath) => {
|
|
175
184
|
const hash = sha2.end(state)
|
|
176
185
|
const rel = toPath(hash)
|
|
177
186
|
const dst = join(path, rel)
|
|
178
187
|
const dstDir = join(path, ...parse(rel).slice(0, -1))
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
188
|
+
// `resultStep` throughout, and that is the "ignores results" above
|
|
189
|
+
// written in the type: each link runs whatever the previous one
|
|
190
|
+
// answered, and only the closing `stat` decides the outcome.
|
|
191
|
+
const created = resultStep(mkdir(dstDir, { recursive: true }), () => rename(curPath, dst))
|
|
192
|
+
const removed = resultStep(created, () => rm(curPath))
|
|
193
|
+
const stated = resultStep(removed, () => stat(dst))
|
|
194
|
+
return resultMapStep(
|
|
183
195
|
stated,
|
|
184
|
-
st => st[0] === 'ok' && st[1].size === offset ? ok(hash) : error('publish size mismatch'))
|
|
196
|
+
st => st[0] === 'ok' && st[1].size === offset ? ok(hash) : error(ioError({ message: 'publish size mismatch' })))
|
|
185
197
|
}
|
|
186
198
|
// Any streaming error fails closed: delete the partial file, return the error.
|
|
187
|
-
/** @type {(curPath: string, e:
|
|
199
|
+
/** @type {(curPath: string, e: IoChannel) => Effect<FileCasOperation, Vec, IoChannel>} */
|
|
188
200
|
const fail = (curPath, e) =>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
201
|
+
// The cleanup's own outcome is discarded on purpose: `e` is what the
|
|
202
|
+
// caller needs to hear, and a failed `rm` has no better answer.
|
|
203
|
+
resultMapStep(rm(curPath), () => error(e))
|
|
204
|
+
const rndEffect = ioStep(gcStage(stageDir), () => random256)
|
|
205
|
+
return ioStep(rndEffect, rnd => {
|
|
192
206
|
const rndStr = vecToCBase32(rnd)
|
|
193
|
-
/** @type {(state: Sha2State, offset: number, curPath: string) => (stream: List<O1,
|
|
207
|
+
/** @type {(state: Sha2State, offset: number, curPath: string) => (stream: List<O1, Vec, IoChannel>) => Effect<O1 | FileCasOperation, Vec, IoChannel>} */
|
|
194
208
|
const loop = (state, offset, curPath) =>
|
|
195
209
|
stream =>
|
|
196
|
-
step
|
|
210
|
+
// `resultStep`, not `step`: a stream that fails must still be
|
|
211
|
+
// cleaned up after, so this handles the failure rather than
|
|
212
|
+
// letting it propagate past the partial file.
|
|
213
|
+
resultStep(stream, r => {
|
|
214
|
+
if (r[0] === 'error') {
|
|
215
|
+
return fail(curPath, r[1])
|
|
216
|
+
}
|
|
217
|
+
const node = r[1]
|
|
197
218
|
if (node === undefined) {
|
|
198
219
|
return publish(state, offset, curPath)
|
|
199
220
|
}
|
|
200
|
-
const { first, tail } = node
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const chunk = first[1]
|
|
205
|
-
return step(writeBytes(curPath, offset, chunk), wb => {
|
|
221
|
+
const { first: chunk, tail } = node
|
|
222
|
+
// Both branches matter: a failed write is cleaned up after,
|
|
223
|
+
// rather than propagated with the partial file left behind.
|
|
224
|
+
return resultStep(writeBytes(curPath, offset, chunk), wb => {
|
|
206
225
|
if (wb[0] === 'error') { return fail(curPath, wb[1]) }
|
|
207
226
|
const newState = sha2.append(chunk)(state)
|
|
208
227
|
const newOffset = offset + Number(length(chunk) / 8n)
|
|
209
228
|
// Renew the lease: rename to a fresh deadline (keeps `delta` constant).
|
|
210
229
|
// The new path is still needed after the rename, to recurse with,
|
|
211
230
|
// so the rename captures it rather than closing over it.
|
|
212
|
-
const nextPath =
|
|
231
|
+
const nextPath = ioMapStep(
|
|
213
232
|
now(),
|
|
214
|
-
t =>
|
|
233
|
+
t => join(stageDir, stageName(t + leaseDelta, rndStr)))
|
|
215
234
|
const renamed = historyStep(
|
|
216
235
|
history(nextPath),
|
|
217
236
|
next => rename(curPath, next))
|
|
218
|
-
return
|
|
237
|
+
return resultStep(
|
|
219
238
|
renamed,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
239
|
+
r => {
|
|
240
|
+
if (r[0] === 'error') { return fail(curPath, r[1]) }
|
|
241
|
+
const [, next] = r[1]
|
|
242
|
+
return loop(newState, newOffset, next)(tail)
|
|
243
|
+
})
|
|
224
244
|
})
|
|
225
245
|
})
|
|
226
|
-
const started =
|
|
227
|
-
return
|
|
246
|
+
const started = ioStep(mkdir(stageDir, { recursive: true }), () => now())
|
|
247
|
+
return ioStep(started, t0 => {
|
|
228
248
|
const path0 = join(stageDir, stageName(t0 + leaseDelta, rndStr))
|
|
229
|
-
return
|
|
249
|
+
return ioStep(
|
|
230
250
|
createExclusive(path0),
|
|
231
|
-
|
|
251
|
+
() => loop(sha2.init, 0, path0)(payload))
|
|
232
252
|
})
|
|
233
253
|
})
|
|
234
254
|
}
|
|
@@ -245,42 +265,41 @@ export const fileCas = sha2 => path => {
|
|
|
245
265
|
return {
|
|
246
266
|
read: hash => {
|
|
247
267
|
const p = join(path, toPath(hash))
|
|
248
|
-
/** @type {(offset: number) => List<FileCasOperation,
|
|
268
|
+
/** @type {(offset: number) => List<FileCasOperation, Vec, IoChannel>} */
|
|
249
269
|
const loop = offset =>
|
|
250
|
-
step
|
|
270
|
+
// A missing shard or read error fails the stream, and `step`
|
|
271
|
+
// propagates it — the cell's own failure can never be mistaken
|
|
272
|
+
// for the `undefined` that ends one.
|
|
273
|
+
ioStep(
|
|
251
274
|
readBytes(p, offset, chunkBytes),
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
// End the stream only on an empty read; every non-empty read — including a
|
|
259
|
-
// final short (`< CHUNK_BYTES`) chunk — is emitted as an `ok` item.
|
|
260
|
-
return length(v) === 0n
|
|
261
|
-
? elEmpty()
|
|
262
|
-
: nonEmpty(ok(v), loop(offset + chunkBytes))
|
|
263
|
-
})
|
|
275
|
+
// End the stream only on an empty read; every non-empty read — including a
|
|
276
|
+
// final short (`< CHUNK_BYTES`) chunk — is emitted as a cell.
|
|
277
|
+
chunk => length(chunk) === 0n
|
|
278
|
+
? elEmpty()
|
|
279
|
+
: nonEmpty(chunk, loop(offset + chunkBytes)))
|
|
264
280
|
return loop(0)
|
|
265
281
|
},
|
|
266
282
|
write: payload => writeImpl(sha2, path, stageDir, payload),
|
|
267
283
|
list: () =>
|
|
268
284
|
// A fresh store has no `.cas` directory yet. Treat *only* that case as an
|
|
269
|
-
// empty store, mirroring how `read`
|
|
285
|
+
// empty store, mirroring how `read` fails a stream on a missing shard.
|
|
270
286
|
// A `.cas` that exists but cannot be read (permissions, corruption) is a
|
|
271
287
|
// genuine storage error and is surfaced, not masked as "no hashes".
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
288
|
+
//
|
|
289
|
+
// `resultStep` because both branches genuinely matter: the absent
|
|
290
|
+
// store is a success answering `ok([])`, and every other failure is
|
|
291
|
+
// the caller's to see. It used to `throw` here, which is what the
|
|
292
|
+
// paragraph above always meant by "surfaced" but could not say
|
|
293
|
+
// while the return type had no error channel to say it in.
|
|
294
|
+
resultStep(access(storePrefix), a =>
|
|
295
|
+
a[0] === 'error'
|
|
296
|
+
? isNotFound(a[1]) ? pureOk([]) : pureError(a[1])
|
|
297
|
+
: ioMapStep(
|
|
298
|
+
readdir(storePrefix, { recursive: true }),
|
|
299
|
+
r => r.flatMap(({ name, parentPath, isFile }) =>
|
|
300
|
+
toOption(isFile
|
|
301
|
+
? cBase32ToVec(normalize(parentPath).substring(normalizedStorePrefix.length).replaceAll('/', '') + name)
|
|
302
|
+
: null)))),
|
|
284
303
|
url: hash =>
|
|
285
304
|
join(path, toPath(hash))
|
|
286
305
|
}
|
|
@@ -288,44 +307,41 @@ export const fileCas = sha2 => path => {
|
|
|
288
307
|
|
|
289
308
|
/** 256-bit random `Vec` built from 8 sequential `randomInt` (32-bit) calls.
|
|
290
309
|
*
|
|
291
|
-
* @type {Effect<RandomInt, Vec>}
|
|
310
|
+
* @type {Effect<RandomInt, Vec, NotImplemented>}
|
|
292
311
|
*/
|
|
293
312
|
const random256 =
|
|
294
313
|
foldStep(
|
|
295
|
-
|
|
314
|
+
pureOk([0, 1, 2, 3, 4, 5, 6, 7]),
|
|
296
315
|
empty,
|
|
297
316
|
() => (/** @type {Vec} */ acc) =>
|
|
298
|
-
|
|
317
|
+
ioMapStep(randomInt(), r => msb.concat(acc)(vec(32n)(BigInt(r)))))
|
|
299
318
|
|
|
300
|
-
/** Streams any file at `filePath` in `<=128 KiB` chunks
|
|
319
|
+
/** Streams any file at `filePath` in `<=128 KiB` chunks.
|
|
301
320
|
*
|
|
302
|
-
*
|
|
321
|
+
* A failed read fails the stream. It used to be yielded as an error *item*
|
|
322
|
+
* followed by an explicit empty tail — a tail no consumer would ever pull.
|
|
323
|
+
*
|
|
324
|
+
* @type {(filePath: string) => List<ReadBytes, Vec, IoChannel>}
|
|
303
325
|
*/
|
|
304
326
|
const streamFile = filePath => {
|
|
305
|
-
/** @type {(offset: number) => List<ReadBytes,
|
|
327
|
+
/** @type {(offset: number) => List<ReadBytes, Vec, IoChannel>} */
|
|
306
328
|
const loop = offset =>
|
|
307
|
-
|
|
329
|
+
ioStep(
|
|
308
330
|
readBytes(filePath, offset, chunkBytes),
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
const chunk = result[1]
|
|
314
|
-
return length(chunk) === 0n
|
|
315
|
-
? elEmpty()
|
|
316
|
-
: nonEmpty(ok(chunk), loop(offset + chunkBytes))
|
|
317
|
-
})
|
|
331
|
+
chunk => length(chunk) === 0n
|
|
332
|
+
? elEmpty()
|
|
333
|
+
: nonEmpty(chunk, loop(offset + chunkBytes)))
|
|
318
334
|
return loop(0)
|
|
319
335
|
}
|
|
320
336
|
|
|
321
337
|
/**
|
|
322
338
|
* Streams the file at `path` through `cas.write`, returning the content hash.
|
|
323
|
-
*
|
|
324
|
-
*
|
|
339
|
+
* Its one consumer is the CLI's `cas add`; the MCP `cas_add` tool takes inline
|
|
340
|
+
* content and calls `write` itself, so it does not come through here.
|
|
325
341
|
*
|
|
326
342
|
* @template {Operation} O
|
|
327
343
|
* @param {Cas<O>} cas
|
|
328
|
-
* @returns {(path: string) => Effect<O | ReadBytes,
|
|
344
|
+
* @returns {(path: string) => Effect<O | ReadBytes, Vec, IoChannel>}
|
|
329
345
|
*/
|
|
330
346
|
export const casAddFile = cas => path =>
|
|
331
347
|
// streamFile produces only ReadBytes effects. TypeScript can't prove ListEffect<ReadBytes,T>
|
|
@@ -333,18 +349,3 @@ export const casAddFile = cas => path =>
|
|
|
333
349
|
// caller passes a Cas<O> where ReadBytes ⊆ O (e.g. FileCasOperation).
|
|
334
350
|
cas.write(streamFile(path))
|
|
335
351
|
|
|
336
|
-
/**
|
|
337
|
-
* Upload pipeline: streams `fileName` from `~/cas_upload/` through `casAddFile`,
|
|
338
|
-
* then deletes the source on success. On failure the source is left in place so
|
|
339
|
-
* the upload can be retried; `write` already cleans up its own partial staging file.
|
|
340
|
-
*
|
|
341
|
-
* @type {(home: string) => (fileName: string) => Effect<FileCasOperation, IoResult<Vec>>}
|
|
342
|
-
*/
|
|
343
|
-
export const casUpload = home => fileName => {
|
|
344
|
-
const src = join(home, 'cas_upload', fileName)
|
|
345
|
-
const c = fileCas(sha256)(home)
|
|
346
|
-
return step(
|
|
347
|
-
casAddFile(c)(src),
|
|
348
|
-
okStep(/** @type {(v: Vec) => Effect<Rm, IoResult<Vec>>} */ (v =>
|
|
349
|
-
mapStep(rm(src), () => ok(v)))))
|
|
350
|
-
}
|
package/fjs/cas/proof.f.d.mts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @import { Vec } from '../types/bit_vec/types.ts'
|
|
3
3
|
* @import { FileCasOperation } from './types.ts'
|
|
4
|
+
* @import { IoChannel, IoResult, Mkdir, ReadFile, WriteFile } from '../effects/node/types.ts'
|
|
4
5
|
* @import { Effect } from '../effects/types.ts'
|
|
5
|
-
* @import {
|
|
6
|
-
* @import { Ok } from '../types/result/types.ts'
|
|
6
|
+
* @import { Ok, Result } from '../types/result/types.ts'
|
|
7
7
|
* @import { List } from '../effects/list/types.ts'
|
|
8
8
|
*/
|
|
9
9
|
import type { FileCasOperation } from './types.ts';
|
|
10
|
-
import type { ReadFile, WriteFile
|
|
11
|
-
export type _TestOp = FileCasOperation | WriteFile | ReadFile |
|
|
10
|
+
import type { Mkdir, ReadFile, WriteFile } from '../effects/node/types.ts';
|
|
11
|
+
export type _TestOp = FileCasOperation | WriteFile | ReadFile | Mkdir;
|
|
12
12
|
export declare const proof: {
|
|
13
13
|
addBigFile: () => void;
|
|
14
14
|
addAndGetBigFile: () => void;
|
|
@@ -16,14 +16,17 @@ export declare const proof: {
|
|
|
16
16
|
casReadMissingShard: () => void;
|
|
17
17
|
casWriteMultiChunk: () => void;
|
|
18
18
|
casWriteDedup: () => void;
|
|
19
|
-
|
|
19
|
+
casWriteStreamFailureAborts: () => void;
|
|
20
20
|
casWriteReadExceedsMaxLength: () => void;
|
|
21
21
|
casWriteGcReclaimsExpired: () => void;
|
|
22
|
+
casWriteGcRmFailureDoesNotFailTheUpload: () => void;
|
|
22
23
|
casWriteGcSkipsLiveLease: () => void;
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
casWriteBytesErrorAborts: () => void;
|
|
25
|
+
casWriteLeaseRenewalRenameErrorAborts: () => void;
|
|
26
|
+
casWritePublishSizeMismatchErrors: () => void;
|
|
27
|
+
casWritePublishStatErrorErrorsEvenWithMatchingSize: () => void;
|
|
25
28
|
collectReadDrainsChunks: () => void;
|
|
26
|
-
|
|
29
|
+
collectReadPropagatesStreamFailure: () => void;
|
|
27
30
|
collectReadOverflow: () => void;
|
|
28
31
|
casListPropagatesNonNotFoundAccessError: () => void;
|
|
29
32
|
};
|