functionalscript 0.45.0 → 0.46.1
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/rtti/validate/module.f.d.mts +81 -19
- package/fjs/types/rtti/validate/module.f.mjs +107 -61
- package/fjs/types/rtti/validate/proof.f.d.mts +13 -10
- package/fjs/types/rtti/validate/proof.f.mjs +186 -197
- 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/types.d.ts +0 -6
|
@@ -4,18 +4,20 @@
|
|
|
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
|
|
|
13
|
-
import { todo } from '../../../asserts/module.f.mjs'
|
|
14
|
+
import { assert, todo } from '../../../asserts/module.f.mjs'
|
|
14
15
|
import { isProperPrefix, join, parse } from '../../../path/module.f.mjs'
|
|
15
16
|
import { utf8ToString } from '../../../text/module.f.mjs'
|
|
16
17
|
import { empty, length, maxLengthBytes, msb, vec } from '../../../types/bit_vec/module.f.mjs'
|
|
17
18
|
import { error, ok } from '../../../types/result/module.f.mjs'
|
|
18
|
-
import {
|
|
19
|
+
import { ioError, nodeCommands } from '../module.f.mjs'
|
|
20
|
+
import { partialRun } from '../../mock/module.f.mjs'
|
|
19
21
|
import { asBase, asNominal } from '../../memory/module.f.mjs'
|
|
20
22
|
|
|
21
23
|
/** @type {State} */
|
|
@@ -44,10 +46,13 @@ const operation = op => {
|
|
|
44
46
|
}
|
|
45
47
|
const [first, ...rest] = path
|
|
46
48
|
const subDir = dir[first]
|
|
47
|
-
|
|
49
|
+
// `instanceof Array`, not `Array.isArray`: the latter narrows to `any[]`,
|
|
50
|
+
// which `readonly Vec[]` is not assignable to, so its negative branch never
|
|
51
|
+
// removes a `readonly` array from `_Entity`. Only `instanceof` narrows here.
|
|
52
|
+
if (typeof subDir !== 'object' || subDir instanceof Array) {
|
|
48
53
|
return op(dir, path)
|
|
49
54
|
}
|
|
50
|
-
const [newSubDir, r] = f(
|
|
55
|
+
const [newSubDir, r] = f(subDir, rest)
|
|
51
56
|
return [{ ...dir, [first]: newSubDir }, r]
|
|
52
57
|
}
|
|
53
58
|
return path => state => {
|
|
@@ -65,12 +70,22 @@ const readOperation = op => operation((dir, path) => [dir, op(dir, path)])
|
|
|
65
70
|
|
|
66
71
|
const okVoid = ok(undefined)
|
|
67
72
|
|
|
73
|
+
/**
|
|
74
|
+
* A virtual host failure. The virtual runner reports the same normalized
|
|
75
|
+
* {@link IoError} the Node runner does, so a program cannot tell the two apart
|
|
76
|
+
* by the shape of what it catches — which is what makes a proof against the
|
|
77
|
+
* virtual filesystem evidence about the real one.
|
|
78
|
+
*
|
|
79
|
+
* @type {(message: string) => Error<IoError>}
|
|
80
|
+
*/
|
|
81
|
+
const fail = message => error(ioError({ message }))
|
|
82
|
+
|
|
68
83
|
/** @type {(recursive: boolean) => (dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<void>]} */
|
|
69
84
|
const mkdirOp = recursive => (dir, path) => {
|
|
70
85
|
let d = {}
|
|
71
86
|
let i = path.length
|
|
72
87
|
if (i > 1 && !recursive) {
|
|
73
|
-
return [dir,
|
|
88
|
+
return [dir, fail('non-recursive')]
|
|
74
89
|
}
|
|
75
90
|
while (i > 0) {
|
|
76
91
|
i -= 1
|
|
@@ -84,7 +99,7 @@ const mkdirOp = recursive => (dir, path) => {
|
|
|
84
99
|
const mkdir = recursive => operation(mkdirOp(recursive))
|
|
85
100
|
|
|
86
101
|
/** Absent-path error mirroring Node's `ENOENT`, so `isNotFound` recognizes it. */
|
|
87
|
-
const enoent = error({ code: 'ENOENT' })
|
|
102
|
+
const enoent = error(ioError({ code: 'ENOENT', message: 'no such file or directory' }))
|
|
88
103
|
|
|
89
104
|
/** @type {(path: string) => (state: State) => readonly [State, IoResult<Vec>]} */
|
|
90
105
|
const readFile = readOperation((dir, path) => {
|
|
@@ -92,15 +107,18 @@ const readFile = readOperation((dir, path) => {
|
|
|
92
107
|
const file = dir[path[0]]
|
|
93
108
|
if (typeof file === 'function') { throw new Error(`'${path[0]}' is a JsModule; readFile not supported`) }
|
|
94
109
|
if (file === undefined) { return enoent }
|
|
95
|
-
|
|
96
|
-
|
|
110
|
+
// `operation`'s wrapper descends into every plain-object (`Dir`) entry
|
|
111
|
+
// before this op ever runs, and the `JsModule` case already threw above,
|
|
112
|
+
// so `file` here is always a `Vec[]` — never a bare `Dir`.
|
|
113
|
+
assert(Array.isArray(file), `'${path[0]}' is not a file`)
|
|
114
|
+
const chunks = file
|
|
97
115
|
const capBits = maxLengthBytes * 8n
|
|
98
116
|
let result = empty
|
|
99
117
|
for (const chunk of chunks) {
|
|
100
118
|
const chunkLen = length(chunk)
|
|
101
119
|
if (chunkLen === 0n) { continue }
|
|
102
120
|
if (length(result) + chunkLen > capBits) {
|
|
103
|
-
return
|
|
121
|
+
return fail(`File size exceeds maximum allowed size of ${maxLengthBytes} bytes`)
|
|
104
122
|
}
|
|
105
123
|
result = msb.concat(result)(chunk)
|
|
106
124
|
}
|
|
@@ -109,13 +127,13 @@ const readFile = readOperation((dir, path) => {
|
|
|
109
127
|
|
|
110
128
|
/** @type {(path: string) => (state: State) => readonly [State, IoResult<Module>]} */
|
|
111
129
|
const import_ = readOperation((dir, path) => {
|
|
112
|
-
if (path.length !== 1) { return
|
|
130
|
+
if (path.length !== 1) { return fail('no such file') }
|
|
113
131
|
const entry = dir[path[0]]
|
|
114
|
-
if (typeof entry !== 'function') { return
|
|
132
|
+
if (typeof entry !== 'function') { return fail(`'${path[0]}' is not a JsModule`) }
|
|
115
133
|
return ok(entry())
|
|
116
134
|
})
|
|
117
135
|
|
|
118
|
-
const writeFileError =
|
|
136
|
+
const writeFileError = fail('invalid file')
|
|
119
137
|
|
|
120
138
|
/** @type {(payload: Vec) => (dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<void>]} */
|
|
121
139
|
const writeFileOp = payload => (dir, path) => {
|
|
@@ -130,7 +148,7 @@ const writeFileOp = payload => (dir, path) => {
|
|
|
130
148
|
/** @type {(payload: Vec) => (path: string) => (state: State) => readonly [State, IoResult<void>]} */
|
|
131
149
|
const writeFile = payload => operation(writeFileOp(payload))
|
|
132
150
|
|
|
133
|
-
const invalidPath =
|
|
151
|
+
const invalidPath = fail('invalid path')
|
|
134
152
|
|
|
135
153
|
const { entries } = Object
|
|
136
154
|
|
|
@@ -143,10 +161,10 @@ const readdir = (base, recursive) => readOperation((dir, path) => {
|
|
|
143
161
|
let result = []
|
|
144
162
|
for (const [name, content] of entries(d)) {
|
|
145
163
|
if (content === undefined) { continue }
|
|
146
|
-
const isFile =
|
|
164
|
+
const isFile = content instanceof Array || typeof content !== 'object'
|
|
147
165
|
result = [...result, { name, parentPath, isFile }]
|
|
148
166
|
if (!isFile && recursive) {
|
|
149
|
-
result = [...result, ...f(join(parentPath, name),
|
|
167
|
+
result = [...result, ...f(join(parentPath, name), content)]
|
|
150
168
|
}
|
|
151
169
|
}
|
|
152
170
|
return result
|
|
@@ -163,11 +181,15 @@ const access = readOperation((dir, path) => {
|
|
|
163
181
|
|
|
164
182
|
/** @type {(dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<void>]} */
|
|
165
183
|
const rmOp = (dir, path) => {
|
|
166
|
-
if (path.length !== 1) { return [dir,
|
|
184
|
+
if (path.length !== 1) { return [dir, fail('invalid path')] }
|
|
167
185
|
const [name] = path
|
|
168
186
|
const entry = dir[name]
|
|
169
|
-
if (entry === undefined) { return [dir,
|
|
170
|
-
|
|
187
|
+
if (entry === undefined) { return [dir, fail('no such file')] }
|
|
188
|
+
// No "is a directory" guard here: `operation`'s wrapper descends into
|
|
189
|
+
// every plain-object (`Dir`) entry before this op ever runs, so `entry`
|
|
190
|
+
// is always a `Vec[]` or a `JsModule` — never a bare `Dir` — and rm can
|
|
191
|
+
// always proceed. (`rm` on a genuinely non-empty directory instead hits
|
|
192
|
+
// `path.length !== 1` above, once the wrapper has descended into it.)
|
|
171
193
|
const { [name]: _, ...rest } = dir
|
|
172
194
|
return [rest, okVoid]
|
|
173
195
|
}
|
|
@@ -177,7 +199,7 @@ const rm = operation(rmOp)
|
|
|
177
199
|
|
|
178
200
|
/** @type {(dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<_Entity>]} */
|
|
179
201
|
const extractEntity = (dir, path) => {
|
|
180
|
-
if (path.length === 0) { return [dir,
|
|
202
|
+
if (path.length === 0) { return [dir, fail('cannot extract root')] }
|
|
181
203
|
if (path.length === 1) {
|
|
182
204
|
const [name] = path
|
|
183
205
|
const entry = dir[name]
|
|
@@ -187,15 +209,21 @@ const extractEntity = (dir, path) => {
|
|
|
187
209
|
}
|
|
188
210
|
const [first, ...rest] = path
|
|
189
211
|
const sub = dir[first]
|
|
190
|
-
if (sub === undefined ||
|
|
191
|
-
const [newSub, result] = extractEntity(
|
|
212
|
+
if (sub === undefined || sub instanceof Array || typeof sub === 'function') { return [dir, enoent] }
|
|
213
|
+
const [newSub, result] = extractEntity(sub, rest)
|
|
192
214
|
if (result[0] === 'error') { return [dir, result] }
|
|
193
215
|
return [{ ...dir, [first]: newSub }, result]
|
|
194
216
|
}
|
|
195
217
|
|
|
196
218
|
/** @type {(dir: Dir, path: readonly string[], entity: _Entity) => readonly [Dir, IoResult<void>]} */
|
|
197
219
|
const insertEntityAt = (dir, path, entity) => {
|
|
198
|
-
|
|
220
|
+
// `insertEntityAt`'s only external caller, `rename`, always rejects an
|
|
221
|
+
// empty `dst` earlier — `isProperPrefix([], srcParsed)` is true whenever
|
|
222
|
+
// `srcParsed` is non-empty, so renaming onto root is already caught as
|
|
223
|
+
// "onto an ancestor" before this function runs. The recursive self-calls
|
|
224
|
+
// below never pass an empty path either (they only recurse when
|
|
225
|
+
// `path.length > 1`, with a non-empty remainder).
|
|
226
|
+
assert(path.length > 0, 'cannot insert at root')
|
|
199
227
|
if (path.length === 1) {
|
|
200
228
|
const [name] = path
|
|
201
229
|
const existing = dir[name]
|
|
@@ -203,16 +231,16 @@ const insertEntityAt = (dir, path, entity) => {
|
|
|
203
231
|
const entityIsDir = !Array.isArray(entity) && typeof entity === 'object'
|
|
204
232
|
const existingIsDir = !Array.isArray(existing) && typeof existing === 'object'
|
|
205
233
|
if (entityIsDir && !existingIsDir) {
|
|
206
|
-
return [dir,
|
|
234
|
+
return [dir, fail(`cannot overwrite file '${name}' with a directory`)]
|
|
207
235
|
}
|
|
208
236
|
if (!entityIsDir && existingIsDir) {
|
|
209
|
-
return [dir,
|
|
237
|
+
return [dir, fail(`'${name}' is a directory`)]
|
|
210
238
|
}
|
|
211
239
|
if (entityIsDir && existingIsDir) {
|
|
212
|
-
const existingDir =
|
|
240
|
+
const existingDir = existing
|
|
213
241
|
const hasContent = Object.values(existingDir).some(v => v !== undefined)
|
|
214
242
|
if (hasContent) {
|
|
215
|
-
return [dir,
|
|
243
|
+
return [dir, fail(`cannot overwrite non-empty directory '${name}'`)]
|
|
216
244
|
}
|
|
217
245
|
}
|
|
218
246
|
}
|
|
@@ -221,8 +249,8 @@ const insertEntityAt = (dir, path, entity) => {
|
|
|
221
249
|
const [first, ...rest] = path
|
|
222
250
|
const sub = dir[first]
|
|
223
251
|
if (sub === undefined) { return [dir, enoent] }
|
|
224
|
-
if (
|
|
225
|
-
const [newSub, result] = insertEntityAt(
|
|
252
|
+
if (sub instanceof Array || typeof sub === 'function') { return [dir, fail('not a directory')] }
|
|
253
|
+
const [newSub, result] = insertEntityAt(sub, rest, entity)
|
|
226
254
|
if (result[0] === 'error') { return [dir, result] }
|
|
227
255
|
return [{ ...dir, [first]: newSub }, result]
|
|
228
256
|
}
|
|
@@ -237,7 +265,7 @@ const rename = (src, dst) => state => {
|
|
|
237
265
|
// now that source exists, reject if dst is strictly inside src's subtree (rename into own descendant)
|
|
238
266
|
// or if src is strictly inside dst's subtree (rename onto own ancestor)
|
|
239
267
|
if (isProperPrefix(srcParsed, dstParsed) || isProperPrefix(dstParsed, srcParsed)) {
|
|
240
|
-
return [state,
|
|
268
|
+
return [state, fail('cannot rename a directory into its own subtree or onto an ancestor')]
|
|
241
269
|
}
|
|
242
270
|
const [dstRoot, dstResult] = insertEntityAt(srcRoot, dstParsed, srcResult[1])
|
|
243
271
|
if (dstResult[0] === 'error') { return [state, dstResult] }
|
|
@@ -250,13 +278,16 @@ const readBytesOp = (path, offset, size) => readOperation((dir, p) => {
|
|
|
250
278
|
const file = dir[p[0]]
|
|
251
279
|
if (typeof file === 'function') { throw new Error(`'${p[0]}' is a JsModule; readBytes not supported`) }
|
|
252
280
|
if (file === undefined) { return enoent }
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (
|
|
258
|
-
if (
|
|
259
|
-
|
|
281
|
+
// `operation`'s wrapper descends into every plain-object (`Dir`) entry
|
|
282
|
+
// before this op ever runs, and the `JsModule` case already threw above,
|
|
283
|
+
// so `file` here is always a `Vec[]` — never a bare `Dir`.
|
|
284
|
+
assert(Array.isArray(file), `'${p[0]}' is not a file`)
|
|
285
|
+
if (!Number.isInteger(offset)) { return fail(`Offset ${offset} is not an integer`) }
|
|
286
|
+
if (!Number.isInteger(size)) { return fail(`Chunk size ${size} is not an integer`) }
|
|
287
|
+
if (offset < 0) { return fail(`Offset ${offset} is negative`) }
|
|
288
|
+
if (size < 0) { return fail(`Chunk size ${size} is negative`) }
|
|
289
|
+
if (BigInt(size) > maxLengthBytes) { return fail(`Chunk size ${size} exceeds maximum allowed size of ${maxLengthBytes} bytes`) }
|
|
290
|
+
const chunks = file
|
|
260
291
|
let toSkip = BigInt(offset) * 8n
|
|
261
292
|
let toRead = BigInt(size) * 8n
|
|
262
293
|
let result = empty
|
|
@@ -283,7 +314,7 @@ const fileSizeBytes = chunks =>
|
|
|
283
314
|
chunks.reduce((acc, c) => acc + Number(length(c) / 8n), 0)
|
|
284
315
|
|
|
285
316
|
/** Absent-path error for an already-existing exclusive create, mirroring `EEXIST`. */
|
|
286
|
-
const eexist = error({ code: 'EEXIST' })
|
|
317
|
+
const eexist = error(ioError({ code: 'EEXIST', message: 'file already exists' }))
|
|
287
318
|
|
|
288
319
|
/** @type {(dir: Dir, path: readonly string[]) => readonly [Dir, IoResult<void>]} */
|
|
289
320
|
const createExclusiveOp = (dir, path) => {
|
|
@@ -308,11 +339,11 @@ const writeBytesRawOp = (offset, data) => (dir, p) => {
|
|
|
308
339
|
const [name] = p
|
|
309
340
|
const file = dir[name]
|
|
310
341
|
if (file === undefined) { return [dir, enoent] } // writeBytes never creates
|
|
311
|
-
if (!Array.isArray(file)) { return [dir,
|
|
312
|
-
if (!Number.isInteger(offset) || offset < 0) { return [dir,
|
|
313
|
-
const chunks =
|
|
342
|
+
if (!Array.isArray(file)) { return [dir, fail(`'${name}' is not a file`)] }
|
|
343
|
+
if (!Number.isInteger(offset) || offset < 0) { return [dir, fail(`Offset ${offset} is invalid`)] }
|
|
344
|
+
const chunks = file
|
|
314
345
|
if (offset !== fileSizeBytes(chunks)) {
|
|
315
|
-
return [dir,
|
|
346
|
+
return [dir, fail(`writeBytes offset ${offset} must equal the file size (append-only)`)]
|
|
316
347
|
}
|
|
317
348
|
return [{ ...dir, [name]: [...chunks, data] }, okVoid]
|
|
318
349
|
}
|
|
@@ -325,43 +356,46 @@ const statOp = readOperation((dir, path) => {
|
|
|
325
356
|
if (path.length !== 1) { return enoent }
|
|
326
357
|
const file = dir[path[0]]
|
|
327
358
|
if (file === undefined) { return enoent }
|
|
328
|
-
if (!Array.isArray(file)) { return
|
|
329
|
-
return ok({ size: fileSizeBytes(
|
|
359
|
+
if (!Array.isArray(file)) { return fail(`'${path[0]}' is not a file`) }
|
|
360
|
+
return ok({ size: fileSizeBytes(file) })
|
|
330
361
|
})
|
|
331
362
|
|
|
332
|
-
/** @type {
|
|
363
|
+
/** @type {PartialMemOperationMap<NodeOp, State>} */
|
|
333
364
|
const map = {
|
|
334
365
|
all: (...a) => state => {
|
|
335
|
-
|
|
366
|
+
// Each entry is the effect's whole `Result`: `all`'s own envelope says
|
|
367
|
+
// only whether the runner could dispatch it, so the inner answers pass
|
|
368
|
+
// through untouched for `allOk` to collapse.
|
|
369
|
+
/** @type {readonly Result<unknown, unknown>[]} */
|
|
336
370
|
let e = []
|
|
337
371
|
for (const i of a) {
|
|
338
372
|
const [ns, ei] = virtual(state)(i)
|
|
339
373
|
state = ns
|
|
340
374
|
e = [...e, ei]
|
|
341
375
|
}
|
|
342
|
-
return [state, e]
|
|
376
|
+
return [state, ok(e)]
|
|
343
377
|
},
|
|
344
378
|
memCreate: value => state => {
|
|
345
379
|
const id = `mem${state.memoryNext}`
|
|
346
|
-
const key =
|
|
380
|
+
const key = asNominal(id)
|
|
347
381
|
return [{
|
|
348
382
|
...state,
|
|
349
383
|
memoryNext: state.memoryNext + 1,
|
|
350
384
|
memoryValues: { ...state.memoryValues, [id]: value },
|
|
351
|
-
}, key]
|
|
385
|
+
}, ok(key)]
|
|
352
386
|
},
|
|
353
387
|
memRead: key => state =>
|
|
354
|
-
[state, state.memoryValues[asBase(key)]],
|
|
388
|
+
[state, ok(state.memoryValues[asBase(key)])],
|
|
355
389
|
memWrite: (key, value) => state => {
|
|
356
390
|
const id = asBase(key)
|
|
357
391
|
return [{
|
|
358
392
|
...state,
|
|
359
393
|
memoryValues: { ...state.memoryValues, [id]: value },
|
|
360
|
-
},
|
|
394
|
+
}, okVoid]
|
|
361
395
|
},
|
|
362
396
|
fetch: url => state => {
|
|
363
397
|
const result = state.internet[url]
|
|
364
|
-
return result === undefined ? [state,
|
|
398
|
+
return result === undefined ? [state, fail('not found')] : [state, ok(result)]
|
|
365
399
|
},
|
|
366
400
|
mkdir: (path, p) => mkdir(p !== undefined)(path),
|
|
367
401
|
readFile,
|
|
@@ -375,12 +409,8 @@ const map = {
|
|
|
375
409
|
createExclusive,
|
|
376
410
|
writeBytes: writeBytesOp,
|
|
377
411
|
stat: statOp,
|
|
378
|
-
randomInt: () => state => [{ ...state, randomNext: state.randomNext + 1 }, state.randomNext],
|
|
379
|
-
|
|
380
|
-
createServer: todo,
|
|
381
|
-
listen: todo,
|
|
382
|
-
forever: todo,
|
|
383
|
-
now: () => state => [state, state.epochNs],
|
|
412
|
+
randomInt: () => state => [{ ...state, randomNext: state.randomNext + 1 }, ok(state.randomNext)],
|
|
413
|
+
now: () => state => [state, ok(state.epochNs)],
|
|
384
414
|
// Virtual sandbox is a pass-through: the fixture's test function is
|
|
385
415
|
// expected to return a `SandboxResult` directly (encoding pass/fail and a
|
|
386
416
|
// chosen duration), so the handler invokes it without try/catch or clock
|
|
@@ -388,23 +418,35 @@ const map = {
|
|
|
388
418
|
// result instead of the runner measuring real execution. A genuine
|
|
389
419
|
// exception in a fixture propagates loudly as a bug in the fixture.
|
|
390
420
|
// See: issues/156-tf-virtual-tests.md
|
|
391
|
-
sandbox: f => state => [state, /** @type {SandboxResult<unknown>} */ (f())],
|
|
392
|
-
await: p => state => [state, [p]],
|
|
393
|
-
test: todo,
|
|
421
|
+
sandbox: f => state => [state, ok(/** @type {SandboxResult<unknown>} */ (f()))],
|
|
422
|
+
await: p => state => [state, ok([p])],
|
|
394
423
|
write: (stream, data) => state => {
|
|
395
424
|
const s = utf8ToString(data)
|
|
396
|
-
return [{ ...state, [stream]: `${state[stream]}${s}` },
|
|
425
|
+
return [{ ...state, [stream]: `${state[stream]}${s}` }, okVoid]
|
|
397
426
|
},
|
|
398
427
|
read: () => state => {
|
|
399
428
|
const [first, ...rest] = state.stdin
|
|
400
429
|
return state.stdin.length === 0
|
|
401
|
-
? [state, null]
|
|
402
|
-
: [{ ...state, stdin: rest }, first]
|
|
430
|
+
? [state, ok(null)]
|
|
431
|
+
: [{ ...state, stdin: rest }, ok(first)]
|
|
403
432
|
},
|
|
404
433
|
}
|
|
405
434
|
|
|
406
|
-
/**
|
|
407
|
-
|
|
435
|
+
/**
|
|
436
|
+
* The virtual runner.
|
|
437
|
+
*
|
|
438
|
+
* **It implements part of `NodeOp`, and says so.** `exec`, `createServer`,
|
|
439
|
+
* `listen`, `forever` and `test` have no meaning against an in-memory
|
|
440
|
+
* filesystem, and they used to be present as `todo` handlers — entries that
|
|
441
|
+
* existed only to satisfy a total operation map and threw when reached. They
|
|
442
|
+
* are simply absent now, so a program that asks for one gets
|
|
443
|
+
* `error(notImplemented)` back through its own continuation and decides what an
|
|
444
|
+
* incompatible runner means for it, which is what `NotImplemented` was
|
|
445
|
+
* introduced for. A command that is not a `NodeOp` at all still panics.
|
|
446
|
+
*
|
|
447
|
+
* @type {RunInstance<NodeOp, State>}
|
|
448
|
+
*/
|
|
449
|
+
export const virtual = partialRun(nodeCommands)(map)
|
|
408
450
|
|
|
409
451
|
const testContext = { test: todo }
|
|
410
452
|
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @import { Dir
|
|
2
|
+
* @import { Dir } from './types.ts'
|
|
3
|
+
* @import { NodeOp } from '../types.ts'
|
|
4
|
+
* @import { Effect } from '../../types.ts'
|
|
5
|
+
* @import { IoChannel } from '../types.ts'
|
|
3
6
|
*/
|
|
4
7
|
export declare const proof: {
|
|
8
|
+
unimplemented: {
|
|
9
|
+
declaredButAbsent: () => void;
|
|
10
|
+
programChoosesAFallback: () => void;
|
|
11
|
+
throw: {
|
|
12
|
+
undeclaredCommand: () => void;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
5
15
|
rm: {
|
|
6
16
|
success: () => void;
|
|
7
17
|
notFound: () => void;
|
|
8
|
-
|
|
18
|
+
onDirectory: () => void;
|
|
9
19
|
};
|
|
10
20
|
writeFileOnDirectory: () => void;
|
|
11
21
|
writeFileOverJsModule: () => void;
|
|
@@ -18,7 +28,23 @@ export declare const proof: {
|
|
|
18
28
|
importNonModule: () => void;
|
|
19
29
|
throw: {
|
|
20
30
|
readFileOnJsModule: () => void;
|
|
31
|
+
readBytesOnJsModule: () => void;
|
|
21
32
|
};
|
|
33
|
+
readFileSkipsEmptyChunk: () => void;
|
|
34
|
+
readdirSkipsUndefinedEntry: () => void;
|
|
35
|
+
renameEmptySrc: () => void;
|
|
36
|
+
renameSrcThroughFile: () => void;
|
|
37
|
+
renameSrcThreeLevelsMissing: () => void;
|
|
38
|
+
renameDstMissingIntermediate: () => void;
|
|
39
|
+
renameDstThroughFile: () => void;
|
|
40
|
+
renameDstNestedError: () => void;
|
|
41
|
+
createExclusiveNestedMissing: () => void;
|
|
42
|
+
writeBytesNestedMissing: () => void;
|
|
43
|
+
writeBytesMissingFile: () => void;
|
|
44
|
+
writeBytesOnJsModule: () => void;
|
|
45
|
+
writeBytesNegativeOffset: () => void;
|
|
46
|
+
statNestedMissing: () => void;
|
|
47
|
+
statMissingFile: () => void;
|
|
22
48
|
renameSamePath: () => void;
|
|
23
49
|
renameIntoOwnSubtree: () => void;
|
|
24
50
|
renameOntoOwnAncestor: () => void;
|