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
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @import { Vec } from "../../types/bit_vec/types.ts"
|
|
3
|
-
* @import { IoResult, ReadFile } from "./types.ts"
|
|
4
|
-
* @import {
|
|
3
|
+
* @import { IoChannel, IoError, IoResult, ReadFile } from "./types.ts"
|
|
4
|
+
* @import { Result } from "../../types/result/types.ts"
|
|
5
5
|
* @import { List } from "../list/types.ts"
|
|
6
6
|
* @import { OperationMap } from "../types.ts"
|
|
7
7
|
*/
|
|
8
8
|
export declare const proof: {
|
|
9
|
+
toIoError: {
|
|
10
|
+
error: () => void;
|
|
11
|
+
withCode: () => void;
|
|
12
|
+
string: () => void;
|
|
13
|
+
null: () => void;
|
|
14
|
+
nonStringCode: () => void;
|
|
15
|
+
noCode: () => void;
|
|
16
|
+
};
|
|
17
|
+
isNotFound: {
|
|
18
|
+
enoent: () => void;
|
|
19
|
+
otherCode: () => void;
|
|
20
|
+
notImplemented: () => void;
|
|
21
|
+
};
|
|
22
|
+
errorMessage: {
|
|
23
|
+
io: () => void;
|
|
24
|
+
notImplemented: () => void;
|
|
25
|
+
};
|
|
26
|
+
errorSummary: {
|
|
27
|
+
io: () => void;
|
|
28
|
+
ioWithoutCode: () => void;
|
|
29
|
+
notImplemented: () => void;
|
|
30
|
+
};
|
|
31
|
+
exitStep: {
|
|
32
|
+
ok: () => void;
|
|
33
|
+
error: () => void;
|
|
34
|
+
};
|
|
9
35
|
externalTestContext: () => void;
|
|
10
36
|
map: () => void;
|
|
11
37
|
fetch: () => void;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @import { Vec } from "../../types/bit_vec/types.ts"
|
|
3
|
-
* @import { IoResult, ReadFile } from "./types.ts"
|
|
4
|
-
* @import {
|
|
3
|
+
* @import { IoChannel, IoError, IoResult, ReadFile } from "./types.ts"
|
|
4
|
+
* @import { Result } from "../../types/result/types.ts"
|
|
5
5
|
* @import { List } from "../list/types.ts"
|
|
6
6
|
* @import { OperationMap } from "../types.ts"
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { empty, isVec, uint, vec, vec8 } from "../../types/bit_vec/module.f.mjs"
|
|
10
10
|
import { utf8, utf8ToString } from "../../text/module.f.mjs"
|
|
11
|
-
import { match
|
|
12
|
-
import {
|
|
11
|
+
import { match } from "../module.f.mjs"
|
|
12
|
+
import { mapStep, step as ioStep } from "../module.f.mjs"
|
|
13
|
+
import { both, errorMessage, errorSummary, exitStep, fetch, ioError, isNotFound, mkdir, now, readdir, readFile, readUtf8File, rm, sandbox, toIoError, writeFile, writeUtf8File, rename, readBytes, randomInt, writeFromStream, usesInlineTestContext, versionLessThan } from "./module.f.mjs"
|
|
13
14
|
import { create as memCreate, read as memRead, write as memWrite } from "../memory/module.f.mjs"
|
|
14
15
|
import { empty as listEmpty, nonEmpty as listNonEmpty } from "../list/module.f.mjs"
|
|
15
16
|
import { emptyState, virtual } from "./virtual/module.f.mjs"
|
|
@@ -29,7 +30,115 @@ const readHelloMap = {
|
|
|
29
30
|
|
|
30
31
|
const readHello = match(readHelloMap)
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Asserts that a channel error is a host failure carrying `message`. Every
|
|
35
|
+
* runner reports through the same normalized {@link IoError}, so a proof
|
|
36
|
+
* against the virtual filesystem names the message rather than the shape.
|
|
37
|
+
* @type {(e: IoChannel, message: string) => void}
|
|
38
|
+
*/
|
|
39
|
+
const assertIoMessage = (e, message) => {
|
|
40
|
+
assert(e[0] === 'ioError', e)
|
|
41
|
+
assertEq(e[1].message, message)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Asserts that an operation succeeded with `expected`.
|
|
45
|
+
* @type {<T, E>(r: Result<T, E>, expected: T) => void}
|
|
46
|
+
*/
|
|
47
|
+
const assertOk = (r, expected) => {
|
|
48
|
+
assert(r[0] === 'ok', r)
|
|
49
|
+
assertEq(r[1], expected)
|
|
50
|
+
}
|
|
51
|
+
|
|
32
52
|
export const proof = {
|
|
53
|
+
// The one boundary where a runner's `catch` becomes effect data: whatever
|
|
54
|
+
// was thrown is reduced to a code (when the host attached a string one)
|
|
55
|
+
// and a message.
|
|
56
|
+
toIoError: {
|
|
57
|
+
error: () => {
|
|
58
|
+
assertIoMessage(toIoError(new Error('boom')), 'boom')
|
|
59
|
+
},
|
|
60
|
+
withCode: () => {
|
|
61
|
+
const e = toIoError(Object.assign(new Error('missing'), { code: 'ENOENT' }))
|
|
62
|
+
assert(e[0] === 'ioError', e)
|
|
63
|
+
assertEq(e[1].code, 'ENOENT', e)
|
|
64
|
+
assertEq(e[1].message, 'missing', e)
|
|
65
|
+
},
|
|
66
|
+
// A thrown non-`Error` still normalizes: the value's string form is the
|
|
67
|
+
// message, and there is no code to carry.
|
|
68
|
+
string: () => {
|
|
69
|
+
const e = toIoError('plain')
|
|
70
|
+
assert(e[0] === 'ioError', e)
|
|
71
|
+
assertEq(e[1].code, undefined, e)
|
|
72
|
+
assertEq(e[1].message, 'plain', e)
|
|
73
|
+
},
|
|
74
|
+
null: () => {
|
|
75
|
+
assertIoMessage(toIoError(null), 'null')
|
|
76
|
+
},
|
|
77
|
+
// An object whose `code` is not a string is not an OS error code, so it
|
|
78
|
+
// is dropped rather than carried as one.
|
|
79
|
+
nonStringCode: () => {
|
|
80
|
+
const e = toIoError({ code: 42 })
|
|
81
|
+
assert(e[0] === 'ioError', e)
|
|
82
|
+
assertEq(e[1].code, undefined, e)
|
|
83
|
+
},
|
|
84
|
+
noCode: () => {
|
|
85
|
+
const e = toIoError({})
|
|
86
|
+
assert(e[0] === 'ioError', e)
|
|
87
|
+
assertEq(e[1].code, undefined, e)
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
isNotFound: {
|
|
91
|
+
enoent: () => {
|
|
92
|
+
assert(isNotFound(ioError({ code: 'ENOENT', message: 'no such file or directory' })))
|
|
93
|
+
},
|
|
94
|
+
otherCode: () => {
|
|
95
|
+
assert(!isNotFound(ioError({ code: 'EACCES', message: 'permission denied' })))
|
|
96
|
+
},
|
|
97
|
+
// A runner that cannot perform the operation has not looked for the
|
|
98
|
+
// path at all, so a missing handler is never "not found".
|
|
99
|
+
notImplemented: () => {
|
|
100
|
+
assert(!isNotFound(['notImplemented', 'readFile']))
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
errorMessage: {
|
|
104
|
+
io: () => {
|
|
105
|
+
assertEq(errorMessage(ioError({ message: 'disk full' })), 'disk full')
|
|
106
|
+
},
|
|
107
|
+
notImplemented: () => {
|
|
108
|
+
assertEq(errorMessage(['notImplemented', 'readFile']), 'operation not implemented: readFile')
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
errorSummary: {
|
|
112
|
+
// The distinction that matters: `errorMessage` hands back the host's
|
|
113
|
+
// words, which is where the path lives; `errorSummary` never does.
|
|
114
|
+
io: () => {
|
|
115
|
+
assertEq(errorSummary(ioError({ code: 'ENOENT', message: "no such file or directory, scandir '/home/u/.cas'" })), 'io error: ENOENT')
|
|
116
|
+
},
|
|
117
|
+
ioWithoutCode: () => {
|
|
118
|
+
assertEq(errorSummary(ioError({ message: "cannot read '/home/u/.cas'" })), 'io error')
|
|
119
|
+
},
|
|
120
|
+
notImplemented: () => {
|
|
121
|
+
assertEq(errorSummary(['notImplemented', 'readdir']), 'operation not implemented: readdir')
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
exitStep: {
|
|
125
|
+
// The exit-code policy a `NodeProgram` ends with: success is `0`...
|
|
126
|
+
ok: () => {
|
|
127
|
+
const [state, code] = virtual(emptyState)(exitStep(writeFile('hello', vec8(0x2An))))
|
|
128
|
+
assertEq(code[0], 'ok')
|
|
129
|
+
assertEq(code[1], 0)
|
|
130
|
+
assertEq(state.stderr, '')
|
|
131
|
+
},
|
|
132
|
+
// ...and a failure is reported on `stderr` and exits `1`.
|
|
133
|
+
// ...and the code is `[1]` either way, which is what lets a runner
|
|
134
|
+
// read it without asking which branch it came from.
|
|
135
|
+
error: () => {
|
|
136
|
+
const [state, code] = virtual(emptyState)(exitStep(readFile('missing')))
|
|
137
|
+
assertEq(code[0], 'error')
|
|
138
|
+
assertEq(code[1], 1)
|
|
139
|
+
assertEq(state.stderr, 'no such file or directory\n')
|
|
140
|
+
},
|
|
141
|
+
},
|
|
33
142
|
externalTestContext: () => {
|
|
34
143
|
assert(usesInlineTestContext('node', 'v22.20.0'))
|
|
35
144
|
assert(usesInlineTestContext('node', '25.99.99'))
|
|
@@ -44,18 +153,15 @@ export const proof = {
|
|
|
44
153
|
assert(!versionLessThan('26.1.1', '26.1.1'))
|
|
45
154
|
},
|
|
46
155
|
map: () => {
|
|
47
|
-
const e =
|
|
48
|
-
readFile('hello'),
|
|
49
|
-
([k, v]) => {
|
|
50
|
-
assert(k !== 'error', v)
|
|
51
|
-
return pure(uint(v) * 2n)
|
|
52
|
-
})
|
|
156
|
+
const e = mapStep(readFile('hello'), v => uint(v) * 2n)
|
|
53
157
|
//
|
|
54
158
|
let r = readHello(e)
|
|
55
159
|
while (r[0] === 'cont') {
|
|
56
160
|
r = readHello(r[2](r[1]))
|
|
57
161
|
}
|
|
58
|
-
|
|
162
|
+
// `done` carries the whole `Result`: an interpreter never separates the
|
|
163
|
+
// channels, so the projection's value is inside the `ok`.
|
|
164
|
+
assertEq(r[1][1], 0x2An)
|
|
59
165
|
},
|
|
60
166
|
fetch: () => {
|
|
61
167
|
const [_, [t, result]] = virtual({
|
|
@@ -81,8 +187,11 @@ export const proof = {
|
|
|
81
187
|
)
|
|
82
188
|
assert(t === 'ok', result)
|
|
83
189
|
const tmp = state.root.tmp
|
|
84
|
-
|
|
85
|
-
|
|
190
|
+
// `instanceof Array`, not `Array.isArray`: only the former's negative
|
|
191
|
+
// branch removes a `readonly` array from a union, so only it narrows
|
|
192
|
+
// `_Entity` to `Dir`.
|
|
193
|
+
assert(!(typeof tmp !== 'object' || tmp instanceof Array), state.root)
|
|
194
|
+
const cache = tmp.cache
|
|
86
195
|
assert(!(typeof cache !== 'object' || Array.isArray(cache)), tmp)
|
|
87
196
|
},
|
|
88
197
|
nonRec: () => {
|
|
@@ -122,7 +231,8 @@ export const proof = {
|
|
|
122
231
|
nestedPath: () => {
|
|
123
232
|
const [_, [t, result]] = virtual(emptyState)(readFile('tmp/cache'))
|
|
124
233
|
assert(t === 'error', result)
|
|
125
|
-
|
|
234
|
+
assert(result[0] === 'ioError', result)
|
|
235
|
+
assertEq(result[1].code, 'ENOENT', result)
|
|
126
236
|
},
|
|
127
237
|
withinLimit: () => {
|
|
128
238
|
// Test with a small file well within the 131,072 byte limit
|
|
@@ -197,7 +307,7 @@ export const proof = {
|
|
|
197
307
|
noSuchDir: () => {
|
|
198
308
|
const [_, [t, result]] = virtual(emptyState)(readdir('tmp', { recursive: true }))
|
|
199
309
|
assert(t === 'error', result)
|
|
200
|
-
|
|
310
|
+
assertIoMessage(result, 'invalid path')
|
|
201
311
|
},
|
|
202
312
|
},
|
|
203
313
|
writeFile: {
|
|
@@ -229,7 +339,7 @@ export const proof = {
|
|
|
229
339
|
writeFile('tmp/cache', vec8(0x2An))
|
|
230
340
|
)
|
|
231
341
|
assert(t === 'error', result)
|
|
232
|
-
|
|
342
|
+
assertIoMessage(result, 'invalid file')
|
|
233
343
|
assertEq(state.root.tmp, undefined, state.root)
|
|
234
344
|
},
|
|
235
345
|
directory: () => {
|
|
@@ -242,7 +352,7 @@ export const proof = {
|
|
|
242
352
|
writeFile('tmp', vec8(0x2An))
|
|
243
353
|
)
|
|
244
354
|
assert(t === 'error', result)
|
|
245
|
-
|
|
355
|
+
assertIoMessage(result, 'invalid file')
|
|
246
356
|
const tmp = state.root.tmp
|
|
247
357
|
assert(!(tmp === undefined || Array.isArray(tmp)), tmp)
|
|
248
358
|
},
|
|
@@ -272,13 +382,13 @@ export const proof = {
|
|
|
272
382
|
})(rm('tmp/cache'))
|
|
273
383
|
assert(t === 'ok', result)
|
|
274
384
|
const tmp = state.root.tmp
|
|
275
|
-
assert(!(typeof tmp !== 'object' || Array
|
|
276
|
-
assertEq(
|
|
385
|
+
assert(!(typeof tmp !== 'object' || tmp instanceof Array), state.root)
|
|
386
|
+
assertEq(tmp.cache, undefined, tmp)
|
|
277
387
|
},
|
|
278
388
|
noSuchFile: () => {
|
|
279
389
|
const [_, [t, result]] = virtual(emptyState)(rm('hello'))
|
|
280
390
|
assert(t === 'error', result)
|
|
281
|
-
|
|
391
|
+
assertIoMessage(result, 'no such file')
|
|
282
392
|
},
|
|
283
393
|
isDirectory: () => {
|
|
284
394
|
const [state, [t, result]] = virtual({
|
|
@@ -286,18 +396,20 @@ export const proof = {
|
|
|
286
396
|
root: { tmp: {} },
|
|
287
397
|
})(rm('tmp'))
|
|
288
398
|
assert(t === 'error', result)
|
|
289
|
-
|
|
399
|
+
assertIoMessage(result, 'invalid path')
|
|
290
400
|
assert(state.root.tmp !== undefined, state.root)
|
|
291
401
|
},
|
|
292
402
|
},
|
|
293
403
|
both: () => {
|
|
294
|
-
const [_,
|
|
404
|
+
const [_, both2] = virtual({
|
|
295
405
|
...emptyState,
|
|
296
406
|
root: {
|
|
297
407
|
a: [vec8(0x2An)],
|
|
298
408
|
b: [vec8(0x15n)],
|
|
299
409
|
},
|
|
300
410
|
})(both(readFile('a'))(readFile('b')))
|
|
411
|
+
assert(both2[0] === 'ok', both2)
|
|
412
|
+
const results = both2[1]
|
|
301
413
|
assert(results[0][0] === 'ok', results[0])
|
|
302
414
|
assert(results[1][0] === 'ok', results[1])
|
|
303
415
|
assertEq(uint(results[0][1]), 0x2An, results[0][1])
|
|
@@ -305,41 +417,48 @@ export const proof = {
|
|
|
305
417
|
},
|
|
306
418
|
now: () => {
|
|
307
419
|
const [_, result] = virtual({ ...emptyState, epochNs: 1_000_000 })(now())
|
|
308
|
-
|
|
420
|
+
assertOk(result, 1_000_000)
|
|
309
421
|
},
|
|
310
422
|
sandbox: {
|
|
311
423
|
// Virtual `sandbox` is now a pass-through: the function is expected
|
|
312
424
|
// to return a `SandboxResult` directly. Fixtures dictate the result
|
|
313
425
|
// (and `duration`) instead of the runner measuring.
|
|
314
426
|
ok: () => {
|
|
315
|
-
const [_,
|
|
316
|
-
sandbox(() =>
|
|
427
|
+
const [_, sandboxed] = virtual(emptyState)(
|
|
428
|
+
sandbox(() => ({ result: ['ok', 42], duration: 0 })))
|
|
429
|
+
// Two `Result`s, one inside the other on purpose: the outer one is
|
|
430
|
+
// the operation's own status, the inner one is the sandboxed
|
|
431
|
+
// function's outcome — returned data, not effect status.
|
|
432
|
+
assert(sandboxed[0] === 'ok', sandboxed)
|
|
433
|
+
const { result, duration } = sandboxed[1]
|
|
317
434
|
assert(result[0] === 'ok', result)
|
|
318
435
|
assertEq(result[1], 42)
|
|
319
436
|
assertEq(duration, 0)
|
|
320
437
|
},
|
|
321
438
|
error: () => {
|
|
322
439
|
const err = new Error('fail')
|
|
323
|
-
const [_,
|
|
324
|
-
sandbox(() =>
|
|
440
|
+
const [_, sandboxed] = virtual(emptyState)(
|
|
441
|
+
sandbox(() => ({ result: ['error', err], duration: 0 })))
|
|
442
|
+
assert(sandboxed[0] === 'ok', sandboxed)
|
|
443
|
+
const { result } = sandboxed[1]
|
|
325
444
|
assert(result[0] === 'error', result)
|
|
326
445
|
assertEq(result[1], err)
|
|
327
446
|
},
|
|
328
447
|
},
|
|
329
448
|
memory: {
|
|
330
449
|
createAndRead: () => {
|
|
331
|
-
const effect =
|
|
450
|
+
const effect = ioStep(memCreate(42), key => memRead(key))
|
|
332
451
|
const [_, value] = virtual(emptyState)(effect)
|
|
333
|
-
|
|
452
|
+
assertOk(value, 42)
|
|
334
453
|
},
|
|
335
454
|
createAndWrite: () => {
|
|
336
|
-
const effect =
|
|
455
|
+
const effect = ioStep(
|
|
337
456
|
memCreate(1),
|
|
338
|
-
key =>
|
|
457
|
+
key => ioStep(
|
|
339
458
|
memWrite(key, 99),
|
|
340
459
|
() => memRead(key)))
|
|
341
460
|
const [_, value] = virtual(emptyState)(effect)
|
|
342
|
-
|
|
461
|
+
assertOk(value, 99)
|
|
343
462
|
},
|
|
344
463
|
},
|
|
345
464
|
rename: {
|
|
@@ -351,7 +470,7 @@ export const proof = {
|
|
|
351
470
|
assert(t === 'ok', result)
|
|
352
471
|
assertEq(state.root.src, undefined, state.root)
|
|
353
472
|
assert(Array.isArray(state.root.dst), state.root)
|
|
354
|
-
assertEq(uint(
|
|
473
|
+
assertEq(uint(state.root.dst[0]), 0x2An, state.root)
|
|
355
474
|
},
|
|
356
475
|
nestedRename: () => {
|
|
357
476
|
const [state, [t, result]] = virtual({
|
|
@@ -360,8 +479,8 @@ export const proof = {
|
|
|
360
479
|
})(rename('tmp/src', 'tmp/dst'))
|
|
361
480
|
assert(t === 'ok', result)
|
|
362
481
|
const tmp = state.root.tmp
|
|
363
|
-
assert(!(typeof tmp !== 'object' || Array
|
|
364
|
-
assertEq(
|
|
482
|
+
assert(!(typeof tmp !== 'object' || tmp instanceof Array), state.root)
|
|
483
|
+
assertEq(tmp.src, undefined, tmp)
|
|
365
484
|
},
|
|
366
485
|
dirOverFile: () => {
|
|
367
486
|
const [state, [t, result]] = virtual({
|
|
@@ -408,18 +527,18 @@ export const proof = {
|
|
|
408
527
|
randomInt: {
|
|
409
528
|
increments: () => {
|
|
410
529
|
const [state1, r1] = virtual(emptyState)(randomInt())
|
|
411
|
-
|
|
530
|
+
assertOk(r1, 0)
|
|
412
531
|
const [state2, r2] = virtual(state1)(randomInt())
|
|
413
|
-
|
|
532
|
+
assertOk(r2, 1)
|
|
414
533
|
const [_, r3] = virtual(state2)(randomInt())
|
|
415
|
-
|
|
534
|
+
assertOk(r3, 2)
|
|
416
535
|
},
|
|
417
536
|
},
|
|
418
537
|
writeFromStream: {
|
|
419
538
|
createExclusiveFails: () => {
|
|
420
539
|
// The destination already exists, so `createExclusive` fails (EEXIST) and
|
|
421
540
|
// the error propagates without ever touching `writeBytes`.
|
|
422
|
-
/** @type {List<never,
|
|
541
|
+
/** @type {List<never, Vec, IoChannel>} */
|
|
423
542
|
const chunks = listEmpty()
|
|
424
543
|
const [state, [t, result]] = virtual({
|
|
425
544
|
...emptyState,
|
|
@@ -432,13 +551,13 @@ export const proof = {
|
|
|
432
551
|
invalidBufferSize: () => {
|
|
433
552
|
// A chunk whose bit length isn't a multiple of 8 trips the
|
|
434
553
|
// byte-alignment guard before `writeBytes` is ever called.
|
|
435
|
-
/** @type {List<never,
|
|
436
|
-
const chunks = listNonEmpty(
|
|
554
|
+
/** @type {List<never, Vec, IoChannel>} */
|
|
555
|
+
const chunks = listNonEmpty(vec(4n)(0b1010n), listEmpty())
|
|
437
556
|
const [_, [t, result]] = virtual(emptyState)(
|
|
438
557
|
writeFromStream('hello', chunks)
|
|
439
558
|
)
|
|
440
559
|
assert(t === 'error', result)
|
|
441
|
-
|
|
560
|
+
assertIoMessage(result, 'invalid buffer size')
|
|
442
561
|
},
|
|
443
562
|
},
|
|
444
563
|
}
|
|
@@ -9,10 +9,70 @@ import type { MemOp } from '../memory/types.ts';
|
|
|
9
9
|
import type { Nominal } from '../../types/nominal/types.ts';
|
|
10
10
|
import type { Result } from '../../types/result/types.ts';
|
|
11
11
|
import type { StringMap } from '../../types/object/types.ts';
|
|
12
|
-
import type { Effect, Operation, ToAsyncOperationMap } from '../types.ts';
|
|
12
|
+
import type { Effect, NotImplemented, Operation, ToAsyncOperationMap } from '../types.ts';
|
|
13
13
|
import type { List } from '../list/types.ts';
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* A host failure, normalized: whatever the runtime threw reduced to a
|
|
16
|
+
* serializable record. `code` is the OS error code when the host supplied one
|
|
17
|
+
* (`'ENOENT'`, `'EEXIST'`), absent otherwise.
|
|
18
|
+
*
|
|
19
|
+
* It is a tagged tuple for the same reason {@link NotImplemented} is — the two
|
|
20
|
+
* share an error channel, and the tag is what tells them apart. That
|
|
21
|
+
* distinction is the whole reason this type exists: with a bare `unknown`
|
|
22
|
+
* error, `NotImplemented | unknown` collapses to `unknown` and a program can no
|
|
23
|
+
* longer tell "this runner cannot do it" from "the host tried and failed".
|
|
24
|
+
*
|
|
25
|
+
* Normalizing also keeps the channel serializable. A thrown `Error` carries a
|
|
26
|
+
* stack, a `cause`, and arbitrary own properties; none of it survives a wire
|
|
27
|
+
* hop, and a runner in another process could not reproduce it.
|
|
28
|
+
*/
|
|
29
|
+
export type IoError = readonly ['ioError', IoErrorInfo];
|
|
30
|
+
export type IoErrorInfo = {
|
|
31
|
+
readonly code?: string;
|
|
32
|
+
readonly message: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The result of an operation with no failures of its own: it either produces
|
|
36
|
+
* its value or reports that the runner does not implement it.
|
|
37
|
+
*
|
|
38
|
+
* Every operation's return type is a `Result`, including the ones that cannot
|
|
39
|
+
* fail on their own terms — an operation left on a raw contract would be a hole
|
|
40
|
+
* in the error channel, and a runner may omit a handler for any of them.
|
|
41
|
+
*/
|
|
42
|
+
export type OpResult<T> = Result<T, NotImplemented>;
|
|
43
|
+
/**
|
|
44
|
+
* The error channel of anything that performs host IO: a normalized host
|
|
45
|
+
* failure, or the report that the runner does not implement the operation.
|
|
46
|
+
*
|
|
47
|
+
* It is one name rather than a union spelled at each site, and that is a
|
|
48
|
+
* migration property rather than brevity. An effect that does no IO *yet* is
|
|
49
|
+
* one added `readFile` away from doing some, and if each signature names its
|
|
50
|
+
* own errors, that one change walks up every enclosing signature — the failure
|
|
51
|
+
* mode that sank `throws` clauses elsewhere, where engineers eventually
|
|
52
|
+
* declared everything throwing rather than maintain the cascade. Declaring the
|
|
53
|
+
* standard channel once is that concession made deliberately: an IO-touching
|
|
54
|
+
* effect says it fails *the way node IO fails*, and gaining a new way to do so
|
|
55
|
+
* changes nothing above it.
|
|
56
|
+
*
|
|
57
|
+
* It is not a licence to widen. An operation with failures of its own extends
|
|
58
|
+
* the channel (`IoChannel | ParseError`), and a computation whose errors are
|
|
59
|
+
* genuinely narrower should say so — this is the default for IO, not a ceiling.
|
|
60
|
+
*/
|
|
61
|
+
export type IoChannel = NotImplemented | IoError;
|
|
62
|
+
/**
|
|
63
|
+
* The result of an operation that performs host IO: its value, a normalized
|
|
64
|
+
* host failure, or the missing-handler report.
|
|
65
|
+
*/
|
|
66
|
+
export type IoResult<T> = Result<T, IoChannel>;
|
|
67
|
+
/**
|
|
68
|
+
* Runs its effects concurrently and answers each one's whole `Result`.
|
|
69
|
+
*
|
|
70
|
+
* The nesting is deliberate and belongs to the runner: this envelope says
|
|
71
|
+
* whether `all` itself could be dispatched, and each inner `Result` is what
|
|
72
|
+
* that effect answered. `allOk` (`./module.f.mjs`) is the collapse a fallible
|
|
73
|
+
* chain wants.
|
|
74
|
+
*/
|
|
75
|
+
export type All = ['all', <T, E>(...effects: Effect<never, T, E>[]) => OpResult<readonly Result<T, E>[]>];
|
|
16
76
|
export type Fetch = ['fetch', (url: string) => IoResult<Vec>];
|
|
17
77
|
export type MakeDirectoryOptions = {
|
|
18
78
|
readonly recursive: true;
|
|
@@ -42,7 +102,7 @@ export type WriteFile = readonly ['writeFile', (path: string, data: Vec) => IoRe
|
|
|
42
102
|
export type Rm = readonly ['rm', (path: string) => IoResult<void>];
|
|
43
103
|
export type Rename = readonly ['rename', (src: string, dst: string) => IoResult<void>];
|
|
44
104
|
export type ReadBytes = readonly ['readBytes', (path: string, offset: number, size: number) => IoResult<Vec>];
|
|
45
|
-
export type RandomInt = readonly ['randomInt', () => number];
|
|
105
|
+
export type RandomInt = readonly ['randomInt', () => OpResult<number>];
|
|
46
106
|
export type ExecResult = {
|
|
47
107
|
readonly stdout: string;
|
|
48
108
|
readonly stderr: string;
|
|
@@ -66,7 +126,7 @@ export type CreateExclusive = readonly ['createExclusive', (path: string) => IoR
|
|
|
66
126
|
*/
|
|
67
127
|
export type WriteBytes = readonly ['writeBytes', (path: string, offset: number, data: Vec) => IoResult<void>];
|
|
68
128
|
/** @internal */
|
|
69
|
-
export type _WriteLoop = <O extends Operation>(offset: number, e: List<O,
|
|
129
|
+
export type _WriteLoop = <O extends Operation>(offset: number, e: List<O, Vec, IoChannel>) => Effect<O | WriteBytes, void, IoChannel>;
|
|
70
130
|
/** File metadata returned by `stat`. Only `size` (in bytes) for now. */
|
|
71
131
|
export type FileStat = {
|
|
72
132
|
readonly size: number;
|
|
@@ -86,11 +146,16 @@ export type ServerResponse = {
|
|
|
86
146
|
readonly headers: Headers;
|
|
87
147
|
readonly body: Vec;
|
|
88
148
|
};
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
149
|
+
/**
|
|
150
|
+
* An HTTP request handler. The channel is `never` because the response frame
|
|
151
|
+
* *is* where a failure goes — a listener that cannot answer still has a status
|
|
152
|
+
* code to answer with, so absorbing is the contract rather than an omission.
|
|
153
|
+
*/
|
|
154
|
+
export type RequestListener<O extends Operation> = (_: IncomingMessage) => Effect<O, ServerResponse, never>;
|
|
155
|
+
export type CreateServer = ['createServer', (listener: RequestListener<Operation>) => OpResult<Server>];
|
|
156
|
+
export type Listen = ['listen', (server: Server, port: number) => OpResult<void>];
|
|
92
157
|
export type Http = CreateServer | Listen;
|
|
93
|
-
export type Forever = ['forever', () => never];
|
|
158
|
+
export type Forever = ['forever', () => OpResult<never>];
|
|
94
159
|
export type Module = StringMap<unknown>;
|
|
95
160
|
export type Import = ['import', (path: string) => IoResult<Module>];
|
|
96
161
|
/** Named output streams accepted by the `Write` effect. */
|
|
@@ -100,7 +165,7 @@ export type WriteConsoles = 'stdout' | 'stderr';
|
|
|
100
165
|
* a `Vec`. The Node runner maps each stream name to the appropriate fd and
|
|
101
166
|
* delegates to the OS via `stream.write()` with backpressure handling.
|
|
102
167
|
*/
|
|
103
|
-
export type Write = readonly ['write', (stream: WriteConsoles, data: Vec) => void];
|
|
168
|
+
export type Write = readonly ['write', (stream: WriteConsoles, data: Vec) => OpResult<void>];
|
|
104
169
|
export type Console = (s: string) => Effect<Write, void>;
|
|
105
170
|
/** Named input streams accepted by the `Read` effect. */
|
|
106
171
|
export type ReadConsoles = 'stdin';
|
|
@@ -112,10 +177,10 @@ export type ReadConsoles = 'stdin';
|
|
|
112
177
|
* rather than the interpreter. Back-pressure is naturally sequential — the next
|
|
113
178
|
* `read` is only issued once the previous byte is consumed.
|
|
114
179
|
*/
|
|
115
|
-
export type Read = readonly ['read', (stream: ReadConsoles) => number | null];
|
|
180
|
+
export type Read = readonly ['read', (stream: ReadConsoles) => OpResult<number | null>];
|
|
116
181
|
/** @internal */
|
|
117
182
|
export type _UtfList = EffectList<number>;
|
|
118
|
-
export type Now = readonly ['now', () => number];
|
|
183
|
+
export type Now = readonly ['now', () => OpResult<number>];
|
|
119
184
|
/**
|
|
120
185
|
* The outcome of a `Sandbox` operation.
|
|
121
186
|
*
|
|
@@ -132,7 +197,7 @@ export type SandboxResult<T> = {
|
|
|
132
197
|
*/
|
|
133
198
|
readonly duration: number;
|
|
134
199
|
};
|
|
135
|
-
export type Sandbox = readonly ['sandbox', <T>(f: () => T) => SandboxResult<T
|
|
200
|
+
export type Sandbox = readonly ['sandbox', <T>(f: () => T) => OpResult<SandboxResult<T>>];
|
|
136
201
|
/**
|
|
137
202
|
* Resolves the return value of a test function inside the effect runner.
|
|
138
203
|
* If `p` is a real `Promise`, it is awaited and rejections propagate as
|
|
@@ -140,7 +205,7 @@ export type Sandbox = readonly ['sandbox', <T>(f: () => T) => SandboxResult<T>];
|
|
|
140
205
|
* (objects with a `.then` method that are not `instanceof Promise`) are
|
|
141
206
|
* treated as ordinary values — not awaited. See `fjs/dev/tf/README.md`.
|
|
142
207
|
*/
|
|
143
|
-
export type Await = readonly ['await', (p: unknown) => readonly [unknown]];
|
|
208
|
+
export type Await = readonly ['await', (p: unknown) => OpResult<readonly [unknown]>];
|
|
144
209
|
/**
|
|
145
210
|
* Signature of a framework test-registration function (e.g. `nodeTest.test`,
|
|
146
211
|
* `bunTest.test`). Returns `Promise<void>` so async sub-tests can be awaited.
|
|
@@ -157,10 +222,18 @@ export type TestFn = (name: string, options: {
|
|
|
157
222
|
export type TestContext = {
|
|
158
223
|
readonly test: TestFn;
|
|
159
224
|
};
|
|
160
|
-
/**
|
|
161
|
-
|
|
225
|
+
/**
|
|
226
|
+
* Operation that registers a named test with the active `TestContext`.
|
|
227
|
+
*
|
|
228
|
+
* The callback's `never` is the honest reading of what an external framework
|
|
229
|
+
* accepts. Node `--test`, Bun and Deno take a body that either returns or
|
|
230
|
+
* throws; there is no channel to answer a failure through, so the body absorbs
|
|
231
|
+
* its own — which `emergent_testing` does, by panicking, since a throw is the
|
|
232
|
+
* one failure signal those frameworks understand.
|
|
233
|
+
*/
|
|
234
|
+
export type Test = readonly ['test', (ctx: TestContext, name: string, expectFailure: boolean, test: (t: TestContext) => Effect<Test | All | Await, void, never>) => OpResult<void>];
|
|
162
235
|
export type NodeOp = Access | All | Await | Fetch | Fs | Http | Forever | Import | MemOp | Now | RandomInt | Read | Sandbox | Write | Test;
|
|
163
|
-
export type NodeEffect<T> = Effect<NodeOp, T>;
|
|
236
|
+
export type NodeEffect<T, E = IoChannel> = Effect<NodeOp, T, E>;
|
|
164
237
|
export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
|
|
165
238
|
/**
|
|
166
239
|
* The environment variables.
|
|
@@ -199,5 +272,20 @@ export type NodeProgramOptions = {
|
|
|
199
272
|
readonly nodeVersion?: string;
|
|
200
273
|
readonly inlineTestContext: boolean;
|
|
201
274
|
};
|
|
202
|
-
|
|
275
|
+
/**
|
|
276
|
+
* A program: run it, and it answers an exit code.
|
|
277
|
+
*
|
|
278
|
+
* The code lives in a `Result` rather than in a bare `number`, and the two
|
|
279
|
+
* branches say which kind of code it is — `ok(0)` for success, `error(n)` for
|
|
280
|
+
* failure. A bare `number` could not: nothing could short-circuit on it, so a
|
|
281
|
+
* chain that ran one program and then another had to re-test the code by hand,
|
|
282
|
+
* and `step(…, () => pure(0))` was a way to report a failed program as a clean
|
|
283
|
+
* exit that the type system had no opinion about.
|
|
284
|
+
*
|
|
285
|
+
* **`T` is the literal `0`**, so a success carries no information beyond
|
|
286
|
+
* having succeeded, and `r[1]` is the exit code in *either* branch. A runner
|
|
287
|
+
* reads the code without asking which branch it came from; a caller that cares
|
|
288
|
+
* whether the program failed asks `r[0]`.
|
|
289
|
+
*/
|
|
290
|
+
export type Program<O extends Operation> = (options: NodeProgramOptions) => Effect<O, 0, number>;
|
|
203
291
|
export type NodeProgram = Program<NodeOp>;
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*
|
|
6
6
|
* @import { Vec } from '../../../types/bit_vec/types.ts'
|
|
7
|
-
* @import {
|
|
8
|
-
* @import {
|
|
9
|
-
* @import {
|
|
7
|
+
* @import { PartialMemOperationMap, RunInstance } from '../../mock/types.ts'
|
|
8
|
+
* @import { Dirent, FileStat, IoError, IoResult, Module, NodeOp, NodeProgramOptions, SandboxResult } from '../types.ts'
|
|
9
|
+
* @import { Result } from '../../../types/result/types.ts'
|
|
10
|
+
* @import { Error } from '../../../types/result/types.ts'
|
|
10
11
|
* @import { Dir, State, _Entity } from './types.ts'
|
|
11
12
|
*/
|
|
12
13
|
import type { RunInstance } from '../../mock/types.ts';
|
|
@@ -14,7 +15,20 @@ import type { NodeOp, NodeProgramOptions } from '../types.ts';
|
|
|
14
15
|
import type { State } from './types.ts';
|
|
15
16
|
/** @type {State} */
|
|
16
17
|
export declare const emptyState: State;
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* The virtual runner.
|
|
20
|
+
*
|
|
21
|
+
* **It implements part of `NodeOp`, and says so.** `exec`, `createServer`,
|
|
22
|
+
* `listen`, `forever` and `test` have no meaning against an in-memory
|
|
23
|
+
* filesystem, and they used to be present as `todo` handlers — entries that
|
|
24
|
+
* existed only to satisfy a total operation map and threw when reached. They
|
|
25
|
+
* are simply absent now, so a program that asks for one gets
|
|
26
|
+
* `error(notImplemented)` back through its own continuation and decides what an
|
|
27
|
+
* incompatible runner means for it, which is what `NotImplemented` was
|
|
28
|
+
* introduced for. A command that is not a `NodeOp` at all still panics.
|
|
29
|
+
*
|
|
30
|
+
* @type {RunInstance<NodeOp, State>}
|
|
31
|
+
*/
|
|
18
32
|
export declare const virtual: RunInstance<NodeOp, State>;
|
|
19
33
|
/**
|
|
20
34
|
* Safe, inert defaults for every {@link NodeProgramOptions} field, intended for
|