functionalscript 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fs/asn.1/module.f.js +18 -22
- package/fs/asn.1/proof.f.d.ts +5 -0
- package/fs/asn.1/proof.f.js +38 -2
- package/fs/base64/module.f.d.ts +9 -0
- package/fs/base64/module.f.js +60 -0
- package/fs/base64/proof.f.d.ts +11 -0
- package/fs/base64/proof.f.js +117 -0
- package/fs/base_n/module.f.d.ts +44 -0
- package/fs/base_n/module.f.js +54 -0
- package/fs/base_n/proof.f.d.ts +10 -0
- package/fs/base_n/proof.f.js +86 -0
- package/fs/bnf/data/module.f.d.ts +4 -9
- package/fs/bnf/data/module.f.js +4 -3
- package/fs/bnf/module.f.d.ts +3 -4
- package/fs/bnf/module.f.js +3 -2
- package/fs/bnf/proof.f.d.ts +9 -0
- package/fs/bnf/proof.f.js +98 -1
- package/fs/cas/mcp/module.f.d.ts +40 -0
- package/fs/cas/mcp/module.f.js +223 -0
- package/fs/cas/mcp/proof.f.d.ts +30 -0
- package/fs/cas/mcp/proof.f.js +310 -0
- package/fs/cas/module.f.d.ts +9 -3
- package/fs/cas/module.f.js +104 -69
- package/fs/cas/proof.f.d.ts +2 -0
- package/fs/cas/proof.f.js +30 -1
- package/fs/cbase32/module.f.d.ts +1 -1
- package/fs/cbase32/module.f.js +19 -40
- package/fs/cbase32/proof.f.d.ts +1 -0
- package/fs/cbase32/proof.f.js +15 -0
- package/fs/ci/config/module.f.d.ts +6 -6
- package/fs/ci/config/module.f.js +6 -6
- package/fs/ci/deno/module.f.js +2 -2
- package/fs/ci/module.f.d.ts +5 -0
- package/fs/ci/module.f.js +2 -3
- package/fs/ci/proof.f.d.ts +1 -0
- package/fs/ci/proof.f.js +9 -3
- package/fs/cli/module.f.d.ts +3 -1
- package/fs/cli/module.f.js +22 -12
- package/fs/cli/proof.f.d.ts +5 -0
- package/fs/cli/proof.f.js +76 -0
- package/fs/crypto/hmac/proof.f.d.ts +1 -0
- package/fs/crypto/hmac/proof.f.js +8 -0
- package/fs/crypto/secp/proof.f.d.ts +1 -0
- package/fs/crypto/secp/proof.f.js +10 -0
- package/fs/crypto/sign/module.f.js +1 -4
- package/fs/dev/module.f.d.ts +5 -3
- package/fs/dev/module.f.js +13 -2
- package/fs/dev/package_json/proof.f.d.ts +1 -0
- package/fs/dev/package_json/proof.f.js +5 -0
- package/fs/dev/proof.f.d.ts +1 -0
- package/fs/dev/proof.f.js +12 -1
- package/fs/djs/ast/module.f.d.ts +1 -1
- package/fs/djs/ast/proof.f.d.ts +5 -0
- package/fs/djs/ast/proof.f.js +17 -0
- package/fs/djs/module.f.d.ts +1 -1
- package/fs/djs/module.f.js +2 -3
- package/fs/djs/serializer/module.f.js +24 -11
- package/fs/djs/transpiler/module.f.js +3 -4
- package/fs/effects/memory/module.f.d.ts +37 -0
- package/fs/effects/memory/module.f.js +24 -0
- package/fs/effects/memory/proof.f.d.ts +6 -0
- package/fs/effects/memory/proof.f.js +64 -0
- package/fs/effects/mock/module.f.d.ts +7 -2
- package/fs/effects/mock/module.f.js +12 -8
- package/fs/effects/module.d.ts +1 -1
- package/fs/effects/module.f.d.ts +41 -0
- package/fs/effects/module.f.js +25 -0
- package/fs/effects/module.js +6 -7
- package/fs/effects/node/memory/module.d.ts +20 -0
- package/fs/effects/node/memory/module.js +44 -0
- package/fs/effects/node/memory/proof.d.ts +10 -0
- package/fs/effects/node/memory/proof.js +35 -0
- package/fs/effects/node/module.d.ts +1 -1
- package/fs/effects/node/module.f.d.ts +54 -8
- package/fs/effects/node/module.f.js +55 -4
- package/fs/effects/node/module.js +166 -135
- package/fs/effects/node/proof.f.d.ts +10 -0
- package/fs/effects/node/proof.f.js +88 -21
- package/fs/effects/node/virtual/module.f.d.ts +6 -0
- package/fs/effects/node/virtual/module.f.js +48 -16
- package/fs/effects/node/virtual/proof.f.d.ts +17 -0
- package/fs/effects/node/virtual/proof.f.js +79 -0
- package/fs/effects/proof.f.d.ts +5 -0
- package/fs/effects/proof.f.js +54 -34
- package/fs/emergent_testing/module.f.d.ts +6 -1
- package/fs/emergent_testing/module.f.js +8 -3
- package/fs/emergent_testing/proof.f.d.ts +11 -0
- package/fs/emergent_testing/proof.f.js +52 -16
- package/fs/fjs/module.f.js +2 -2
- package/fs/fjs/proof.f.d.ts +8 -0
- package/fs/fjs/proof.f.js +39 -0
- package/fs/fsc/module.f.d.ts +5 -0
- package/fs/fsc/module.f.js +17 -27
- package/fs/fsc/proof.f.d.ts +8 -0
- package/fs/fsc/proof.f.js +48 -2
- package/fs/fsm/module.f.d.ts +2 -3
- package/fs/fsm/module.f.js +1 -1
- package/fs/html/module.f.d.ts +2 -3
- package/fs/html/module.f.js +7 -6
- package/fs/html/proof.f.d.ts +1 -0
- package/fs/html/proof.f.js +7 -0
- package/fs/js/tokenizer/module.f.js +1 -1
- package/fs/js/tokenizer/proof.f.js +7 -0
- package/fs/json/module.f.d.ts +9 -2
- package/fs/json/module.f.js +9 -7
- package/fs/json/parser/module.f.js +4 -6
- package/fs/json/parser/proof.f.js +19 -10
- package/fs/json/proof.f.d.ts +2 -1
- package/fs/json/proof.f.js +33 -6
- package/fs/json/rpc/module.f.d.ts +5 -0
- package/fs/json/rpc/module.f.js +16 -11
- package/fs/json/schema/module.f.d.ts +15 -14
- package/fs/json/schema/proof.f.d.ts +3 -0
- package/fs/json/schema/proof.f.js +16 -1
- package/fs/mcp/module.f.d.ts +103 -5
- package/fs/mcp/module.f.js +139 -2
- package/fs/mcp/proof.f.d.ts +34 -0
- package/fs/mcp/proof.f.js +207 -0
- package/fs/mcp/stdio/module.f.d.ts +39 -0
- package/fs/mcp/stdio/module.f.js +54 -0
- package/fs/mcp/stdio/proof.f.d.ts +10 -0
- package/fs/mcp/stdio/proof.f.js +92 -0
- package/fs/mime/module.f.d.ts +39 -0
- package/fs/mime/module.f.js +78 -0
- package/fs/mime/proof.f.d.ts +15 -0
- package/fs/mime/proof.f.js +30 -0
- package/fs/path/module.f.d.ts +8 -0
- package/fs/path/module.f.js +10 -3
- package/fs/path/proof.f.d.ts +1 -0
- package/fs/path/proof.f.js +40 -2
- package/fs/text/sgr/proof.f.js +30 -2
- package/fs/text/utf8/module.f.d.ts +7 -0
- package/fs/text/utf8/module.f.js +85 -31
- package/fs/text/utf8/proof.f.d.ts +1 -0
- package/fs/text/utf8/proof.f.js +60 -1
- package/fs/types/bigfloat/module.f.d.ts +3 -0
- package/fs/types/bigfloat/module.f.js +30 -31
- package/fs/types/bigint/module.f.d.ts +24 -2
- package/fs/types/bigint/module.f.js +30 -2
- package/fs/types/bigint/proof.f.d.ts +7 -0
- package/fs/types/bigint/proof.f.js +47 -16
- package/fs/types/bit_vec/module.f.d.ts +18 -0
- package/fs/types/bit_vec/module.f.js +18 -0
- package/fs/types/bit_vec/proof.f.d.ts +1 -0
- package/fs/types/bit_vec/proof.f.js +13 -1
- package/fs/types/function/operator/proof.f.d.ts +2 -0
- package/fs/types/function/operator/proof.f.js +20 -2
- package/fs/types/list/module.f.js +1 -4
- package/fs/types/list/proof.f.d.ts +1 -1
- package/fs/types/list/proof.f.js +14 -6
- package/fs/types/map/proof.f.d.ts +1 -0
- package/fs/types/map/proof.f.js +10 -1
- package/fs/types/nibble_set/module.f.d.ts +13 -0
- package/fs/types/nibble_set/module.f.js +16 -0
- package/fs/types/object/module.f.d.ts +12 -8
- package/fs/types/object/module.f.js +9 -2
- package/fs/types/object/proof.f.d.ts +14 -0
- package/fs/types/object/proof.f.js +4 -0
- package/fs/types/ordered_map/proof.f.d.ts +1 -0
- package/fs/types/ordered_map/proof.f.js +39 -79
- package/fs/types/phantom/module.f.d.ts +22 -0
- package/fs/types/phantom/module.f.js +11 -0
- package/fs/types/prime_field/proof.f.d.ts +7 -0
- package/fs/types/prime_field/proof.f.js +63 -122
- package/fs/types/rtti/common/module.f.d.ts +4 -4
- package/fs/types/rtti/common/module.f.js +1 -1
- package/fs/types/rtti/module.f.d.ts +2 -3
- package/fs/types/rtti/parse/module.f.js +0 -1
- package/fs/types/rtti/ts/module.f.d.ts +26 -32
- package/fs/types/rtti/ts/module.f.js +2 -2
- package/fs/types/rtti/ts/proof.f.js +3 -3
- package/fs/types/rtti/validate/module.f.js +0 -1
- package/fs/types/sorted_list/module.f.d.ts +6 -0
- package/fs/types/sorted_list/module.f.js +11 -0
- package/fs/types/sorted_set/module.f.js +2 -8
- package/fs/types/ts/module.f.d.ts +1 -1
- package/fs/types/ts/module.f.js +3 -1
- package/fs/types/ts/proof.f.js +3 -2
- package/fs/website/proof.f.d.ts +1 -0
- package/fs/website/proof.f.js +8 -1
- package/package.json +3 -3
|
@@ -4,17 +4,21 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { todo } from "../../../asserts/module.f.js";
|
|
7
|
-
import { parse } from "../../../path/module.f.js";
|
|
7
|
+
import { join, parse } from "../../../path/module.f.js";
|
|
8
8
|
import { utf8ToString } from "../../../text/module.f.js";
|
|
9
9
|
import { isVec } from "../../../types/bit_vec/module.f.js";
|
|
10
10
|
import { error, ok } from "../../../types/result/module.f.js";
|
|
11
11
|
import { run } from "../../mock/module.f.js";
|
|
12
|
+
import { asBase, asNominal } from "../../memory/module.f.js";
|
|
12
13
|
export const emptyState = {
|
|
13
14
|
stdout: '',
|
|
14
15
|
stderr: '',
|
|
16
|
+
stdin: [],
|
|
15
17
|
root: {},
|
|
16
18
|
internet: {},
|
|
17
19
|
epochNs: 0,
|
|
20
|
+
memoryNext: 0,
|
|
21
|
+
memoryValues: {},
|
|
18
22
|
};
|
|
19
23
|
const operation = (op) => {
|
|
20
24
|
const f = (dir, path) => {
|
|
@@ -29,7 +33,7 @@ const operation = (op) => {
|
|
|
29
33
|
const [newSubDir, r] = f(subDir, rest);
|
|
30
34
|
return [{ ...dir, [first]: newSubDir }, r];
|
|
31
35
|
};
|
|
32
|
-
return (
|
|
36
|
+
return (path) => (state) => {
|
|
33
37
|
const [root, result] = f(state.root, parse(path));
|
|
34
38
|
return [{ ...state, root }, result];
|
|
35
39
|
};
|
|
@@ -49,15 +53,20 @@ const mkdir = (recursive) => operation((dir, path) => {
|
|
|
49
53
|
dir = { ...dir, ...d };
|
|
50
54
|
return [dir, okVoid];
|
|
51
55
|
});
|
|
52
|
-
|
|
56
|
+
/** Absent-path error mirroring Node's `ENOENT`, so `isNotFound` recognizes it. */
|
|
57
|
+
const enoent = error({ code: 'ENOENT' });
|
|
53
58
|
const readFile = readOperation((dir, path) => {
|
|
54
59
|
if (path.length !== 1) {
|
|
55
|
-
return
|
|
60
|
+
return enoent;
|
|
56
61
|
}
|
|
57
62
|
const file = dir[path[0]];
|
|
58
63
|
if (typeof file === 'function') {
|
|
59
64
|
throw new Error(`'${path[0]}' is a JsModule; readFile not supported`);
|
|
60
65
|
}
|
|
66
|
+
if (file === undefined) {
|
|
67
|
+
return enoent;
|
|
68
|
+
}
|
|
69
|
+
// exists but is a directory — a real error, not a missing path
|
|
61
70
|
if (!isVec(file)) {
|
|
62
71
|
return error(`'${path[0]}' is not a file`);
|
|
63
72
|
}
|
|
@@ -102,7 +111,7 @@ const readdir = (base, recursive) => readOperation((dir, path) => {
|
|
|
102
111
|
const isFile = typeof content !== 'object';
|
|
103
112
|
result = [...result, { name, parentPath, isFile }];
|
|
104
113
|
if (!isFile && recursive) {
|
|
105
|
-
result = [...result, ...f(
|
|
114
|
+
result = [...result, ...f(join(parentPath, name), content)];
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
117
|
return result;
|
|
@@ -114,9 +123,9 @@ const access = readOperation((dir, path) => {
|
|
|
114
123
|
return okVoid;
|
|
115
124
|
}
|
|
116
125
|
if (path.length !== 1) {
|
|
117
|
-
return
|
|
126
|
+
return enoent;
|
|
118
127
|
}
|
|
119
|
-
return dir[path[0]] !== undefined ? okVoid :
|
|
128
|
+
return dir[path[0]] !== undefined ? okVoid : enoent;
|
|
120
129
|
});
|
|
121
130
|
const rm = operation((dir, path) => {
|
|
122
131
|
if (path.length !== 1) {
|
|
@@ -134,7 +143,7 @@ const rm = operation((dir, path) => {
|
|
|
134
143
|
return [rest, okVoid];
|
|
135
144
|
});
|
|
136
145
|
const map = {
|
|
137
|
-
all: (
|
|
146
|
+
all: (...a) => state => {
|
|
138
147
|
let e = [];
|
|
139
148
|
for (const i of a) {
|
|
140
149
|
const [ns, ei] = virtual(state)(i);
|
|
@@ -143,14 +152,31 @@ const map = {
|
|
|
143
152
|
}
|
|
144
153
|
return [state, e];
|
|
145
154
|
},
|
|
146
|
-
|
|
155
|
+
memCreate: value => state => {
|
|
156
|
+
const id = `mem${state.memoryNext}`;
|
|
157
|
+
const key = asNominal(id);
|
|
158
|
+
return [{
|
|
159
|
+
...state,
|
|
160
|
+
memoryNext: state.memoryNext + 1,
|
|
161
|
+
memoryValues: { ...state.memoryValues, [id]: value },
|
|
162
|
+
}, key];
|
|
163
|
+
},
|
|
164
|
+
memRead: key => state => [state, state.memoryValues[asBase(key)]],
|
|
165
|
+
memWrite: (key, value) => state => {
|
|
166
|
+
const id = asBase(key);
|
|
167
|
+
return [{
|
|
168
|
+
...state,
|
|
169
|
+
memoryValues: { ...state.memoryValues, [id]: value },
|
|
170
|
+
}, undefined];
|
|
171
|
+
},
|
|
172
|
+
fetch: url => state => {
|
|
147
173
|
const result = state.internet[url];
|
|
148
174
|
return result === undefined ? [state, error('not found')] : [state, ok(result)];
|
|
149
175
|
},
|
|
150
|
-
mkdir: (
|
|
176
|
+
mkdir: (path, p) => mkdir(p !== undefined)(path),
|
|
151
177
|
readFile,
|
|
152
|
-
readdir: (
|
|
153
|
-
writeFile: (
|
|
178
|
+
readdir: (path, { recursive }) => readdir(path, recursive === true)(path),
|
|
179
|
+
writeFile: (path, payload) => writeFile(payload)(path),
|
|
154
180
|
access,
|
|
155
181
|
import: import_,
|
|
156
182
|
rm,
|
|
@@ -158,7 +184,7 @@ const map = {
|
|
|
158
184
|
createServer: todo,
|
|
159
185
|
listen: todo,
|
|
160
186
|
forever: todo,
|
|
161
|
-
now: (
|
|
187
|
+
now: () => state => [state, state.epochNs],
|
|
162
188
|
// Virtual sandbox is a pass-through: the fixture's test function is
|
|
163
189
|
// expected to return a `SandboxResult` directly (encoding pass/fail and a
|
|
164
190
|
// chosen duration), so the handler invokes it without try/catch or clock
|
|
@@ -166,12 +192,18 @@ const map = {
|
|
|
166
192
|
// result instead of the runner measuring real execution. A genuine
|
|
167
193
|
// exception in a fixture propagates loudly as a bug in the fixture.
|
|
168
194
|
// See: issues/156-tf-virtual-tests.md
|
|
169
|
-
sandbox:
|
|
170
|
-
await:
|
|
195
|
+
sandbox: f => state => [state, f()],
|
|
196
|
+
await: p => state => [state, [p]],
|
|
171
197
|
test: todo,
|
|
172
|
-
write: (
|
|
198
|
+
write: (stream, data) => state => {
|
|
173
199
|
const s = utf8ToString(data);
|
|
174
200
|
return [{ ...state, [stream]: `${state[stream]}${s}` }, undefined];
|
|
175
201
|
},
|
|
202
|
+
read: () => state => {
|
|
203
|
+
const [first, ...rest] = state.stdin;
|
|
204
|
+
return state.stdin.length === 0
|
|
205
|
+
? [state, null]
|
|
206
|
+
: [{ ...state, stdin: rest }, first];
|
|
207
|
+
},
|
|
176
208
|
};
|
|
177
209
|
export const virtual = run(map);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const proof: {
|
|
2
|
+
rm: {
|
|
3
|
+
success: () => void;
|
|
4
|
+
notFound: () => void;
|
|
5
|
+
isDirectory: () => void;
|
|
6
|
+
};
|
|
7
|
+
writeFileOnDirectory: () => void;
|
|
8
|
+
readdirRecursive: () => void;
|
|
9
|
+
readFileIntoDir: () => void;
|
|
10
|
+
awaitNonPromise: () => void;
|
|
11
|
+
fetchNotFound: () => void;
|
|
12
|
+
importNestedPath: () => void;
|
|
13
|
+
importNonModule: () => void;
|
|
14
|
+
throw: {
|
|
15
|
+
readFileOnJsModule: () => void;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { assertEq } from "../../../asserts/module.f.js";
|
|
2
|
+
import { awaitIfPromise, fetch, rm, writeFile, readFile, readdir, import_ } from "../module.f.js";
|
|
3
|
+
import { vec8 } from "../../../types/bit_vec/module.f.js";
|
|
4
|
+
import { emptyState, virtual } from "./module.f.js";
|
|
5
|
+
export const proof = {
|
|
6
|
+
rm: {
|
|
7
|
+
success: () => {
|
|
8
|
+
const root = { 'a.txt': vec8(0x42n) };
|
|
9
|
+
const [, result] = virtual({ ...emptyState, root })(rm('a.txt'));
|
|
10
|
+
assertEq(result[0], 'ok');
|
|
11
|
+
},
|
|
12
|
+
notFound: () => {
|
|
13
|
+
const [, result] = virtual(emptyState)(rm('notexist.txt'));
|
|
14
|
+
assertEq(result[0], 'error');
|
|
15
|
+
},
|
|
16
|
+
isDirectory: () => {
|
|
17
|
+
const inner = {};
|
|
18
|
+
const root = { 'mydir': inner };
|
|
19
|
+
const [, result] = virtual({ ...emptyState, root })(rm('mydir'));
|
|
20
|
+
assertEq(result[0], 'error');
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
writeFileOnDirectory: () => {
|
|
24
|
+
const inner = {};
|
|
25
|
+
const root = { 'mydir': inner };
|
|
26
|
+
const [, result] = virtual({ ...emptyState, root })(writeFile('mydir', vec8(0x42n)));
|
|
27
|
+
assertEq(result[0], 'error');
|
|
28
|
+
},
|
|
29
|
+
readdirRecursive: () => {
|
|
30
|
+
const file = vec8(0x42n);
|
|
31
|
+
const sub = { 'file.txt': file };
|
|
32
|
+
const outer = { 'sub': sub };
|
|
33
|
+
const root = { 'mydir': outer };
|
|
34
|
+
const [, result] = virtual({ ...emptyState, root })(readdir('mydir', { recursive: true }));
|
|
35
|
+
assertEq(result[0], 'ok');
|
|
36
|
+
if (result[0] !== 'ok') {
|
|
37
|
+
throw result;
|
|
38
|
+
}
|
|
39
|
+
assertEq(result[1].length, 2);
|
|
40
|
+
},
|
|
41
|
+
readFileIntoDir: () => {
|
|
42
|
+
// 'a/b' where both 'a' and 'b' are directories
|
|
43
|
+
// hits path.length === 0 in operation's f and path.length !== 1 in readFile op
|
|
44
|
+
const inner = {};
|
|
45
|
+
const outer = { 'b': inner };
|
|
46
|
+
const root = { 'a': outer };
|
|
47
|
+
const [, result] = virtual({ ...emptyState, root })(readFile('a/b'));
|
|
48
|
+
assertEq(result[0], 'error');
|
|
49
|
+
},
|
|
50
|
+
awaitNonPromise: () => {
|
|
51
|
+
// a non-promise value passes through the virtual `await` handler as-is
|
|
52
|
+
const [, result] = virtual(emptyState)(awaitIfPromise(42));
|
|
53
|
+
assertEq(result, 42);
|
|
54
|
+
},
|
|
55
|
+
fetchNotFound: () => {
|
|
56
|
+
// covers the `result === undefined` branch of the `fetch` handler
|
|
57
|
+
const [, result] = virtual(emptyState)(fetch('https://example.com/missing'));
|
|
58
|
+
assertEq(result[0], 'error');
|
|
59
|
+
},
|
|
60
|
+
importNestedPath: () => {
|
|
61
|
+
// import_ on a path whose parent does not exist covers the
|
|
62
|
+
// path.length !== 1 branch of the import_ op
|
|
63
|
+
const [, result] = virtual(emptyState)(import_('a/b'));
|
|
64
|
+
assertEq(result[0], 'error');
|
|
65
|
+
},
|
|
66
|
+
importNonModule: () => {
|
|
67
|
+
// import_ on a Vec (not a JsModule) covers typeof entry !== 'function' branch
|
|
68
|
+
const root = { 'module.f.ts': vec8(0x42n) };
|
|
69
|
+
const [, result] = virtual({ ...emptyState, root })(import_('module.f.ts'));
|
|
70
|
+
assertEq(result[0], 'error');
|
|
71
|
+
},
|
|
72
|
+
throw: {
|
|
73
|
+
readFileOnJsModule: () => {
|
|
74
|
+
// readFile on a JsModule path covers typeof file === 'function' branch
|
|
75
|
+
const root = { 'a.f.ts': (() => ({})) };
|
|
76
|
+
virtual({ ...emptyState, root })(readFile('a.f.ts'));
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
};
|
package/fs/effects/proof.f.d.ts
CHANGED
package/fs/effects/proof.f.js
CHANGED
|
@@ -1,56 +1,76 @@
|
|
|
1
|
-
import { foldStep, forEachStep, pure } from "./module.f.js";
|
|
1
|
+
import { decode, do_, foldStep, forEachStep, match, pure } from "./module.f.js";
|
|
2
|
+
const assertPure = (e, expected) => {
|
|
3
|
+
const d = decode(e);
|
|
4
|
+
if (!d.done) {
|
|
5
|
+
throw e.value;
|
|
6
|
+
}
|
|
7
|
+
if (d.result !== expected) {
|
|
8
|
+
throw d.result;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const next = match({ add: (a, b) => a + b });
|
|
2
12
|
export const proof = {
|
|
3
13
|
foldStep: {
|
|
4
14
|
empty: () => {
|
|
5
15
|
const e = foldStep((x) => (s) => pure(s + x))(10)([]);
|
|
6
|
-
|
|
7
|
-
if (value.length !== 1) {
|
|
8
|
-
throw value;
|
|
9
|
-
}
|
|
10
|
-
if (value[0] !== 10) {
|
|
11
|
-
throw value[0];
|
|
12
|
-
}
|
|
16
|
+
assertPure(e, 10);
|
|
13
17
|
},
|
|
14
18
|
threadsState: () => {
|
|
15
19
|
const e = foldStep((x) => (s) => pure(s + x))(0)([1, 2, 3, 4]);
|
|
16
|
-
|
|
17
|
-
if (value.length !== 1) {
|
|
18
|
-
throw value;
|
|
19
|
-
}
|
|
20
|
-
if (value[0] !== 10) {
|
|
21
|
-
throw value[0];
|
|
22
|
-
}
|
|
20
|
+
assertPure(e, 10);
|
|
23
21
|
},
|
|
24
22
|
order: () => {
|
|
25
23
|
const e = foldStep((x) => (s) => pure(s + x))('')(['a', 'b', 'c']);
|
|
26
|
-
|
|
27
|
-
if (value.length !== 1) {
|
|
28
|
-
throw value;
|
|
29
|
-
}
|
|
30
|
-
if (value[0] !== 'abc') {
|
|
31
|
-
throw value[0];
|
|
32
|
-
}
|
|
24
|
+
assertPure(e, 'abc');
|
|
33
25
|
},
|
|
34
26
|
},
|
|
35
27
|
forEachStep: {
|
|
36
28
|
empty: () => {
|
|
37
29
|
const e = forEachStep(() => pure(undefined))([]);
|
|
38
|
-
|
|
39
|
-
if (value.length !== 1) {
|
|
40
|
-
throw value;
|
|
41
|
-
}
|
|
42
|
-
if (value[0] !== undefined) {
|
|
43
|
-
throw value[0];
|
|
44
|
-
}
|
|
30
|
+
assertPure(e, undefined);
|
|
45
31
|
},
|
|
46
32
|
runs: () => {
|
|
47
33
|
const e = forEachStep(() => pure(undefined))([1, 2, 3]);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
34
|
+
assertPure(e, undefined);
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
decode: () => {
|
|
38
|
+
const d = decode(do_('add')(2, 3));
|
|
39
|
+
if (d.done) {
|
|
40
|
+
throw d;
|
|
41
|
+
}
|
|
42
|
+
if (d.command !== 'add') {
|
|
43
|
+
throw d.command;
|
|
44
|
+
}
|
|
45
|
+
if (d.payload[0] !== 2 || d.payload[1] !== 3) {
|
|
46
|
+
throw d.payload;
|
|
47
|
+
}
|
|
48
|
+
assertPure(d.continuation(5), 5);
|
|
49
|
+
},
|
|
50
|
+
match: {
|
|
51
|
+
done: () => {
|
|
52
|
+
const r = next(pure(7));
|
|
53
|
+
if (r[0] !== 'done') {
|
|
54
|
+
throw r;
|
|
55
|
+
}
|
|
56
|
+
if (r[1] !== 7) {
|
|
57
|
+
throw r[1];
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
cont: () => {
|
|
61
|
+
const r = next(do_('add')(2, 3));
|
|
62
|
+
if (r[0] !== 'cont') {
|
|
63
|
+
throw r;
|
|
64
|
+
}
|
|
65
|
+
if (r[1] !== 5) {
|
|
66
|
+
throw r[1];
|
|
67
|
+
}
|
|
68
|
+
const r2 = next(r[2](r[1]));
|
|
69
|
+
if (r2[0] !== 'done') {
|
|
70
|
+
throw r2;
|
|
51
71
|
}
|
|
52
|
-
if (
|
|
53
|
-
throw
|
|
72
|
+
if (r2[1] !== 5) {
|
|
73
|
+
throw r2[1];
|
|
54
74
|
}
|
|
55
75
|
},
|
|
56
76
|
},
|
|
@@ -24,7 +24,12 @@ export type TestSet = TestEntry | readonly (readonly [string, unknown])[];
|
|
|
24
24
|
* Converts an arbitrary JS value into a `TestSet`.
|
|
25
25
|
*
|
|
26
26
|
* - Zero-argument functions become a `TestEntry`; the `throws` flag is set if
|
|
27
|
-
* `throws` is already `true`
|
|
27
|
+
* `throws` is already `true` (i.e. a `throw` key appears in the ancestor path).
|
|
28
|
+
* The canonical way to declare a throw-test is structural: nest it under a
|
|
29
|
+
* `throw` property key. The secondary `fn.name === 'throw'` check is a legacy
|
|
30
|
+
* path that only fires when a function is extracted from a `throw` key and
|
|
31
|
+
* placed under a different key; that pattern is engine-dependent (unreliable on
|
|
32
|
+
* Bun) and is not a supported authoring style.
|
|
28
33
|
* - Non-null objects become an array of `[key, value]` pairs to recurse into.
|
|
29
34
|
* - All other values (including functions with parameters) produce an empty array.
|
|
30
35
|
*/
|
|
@@ -15,6 +15,7 @@ import { all, awaitIfPromise, sandbox, test } from "../effects/node/module.f.js"
|
|
|
15
15
|
import { pure } from "../effects/module.f.js";
|
|
16
16
|
import { loadModuleMap, shouldLoad } from "../dev/module.f.js";
|
|
17
17
|
import { invert } from "../types/result/module.f.js";
|
|
18
|
+
import { definedEntries } from "../types/object/module.f.js";
|
|
18
19
|
const addPass = (delta) => (ts) => ({ ...ts, time: ts.time + delta, pass: ts.pass + 1 });
|
|
19
20
|
const addFail = (delta) => (ts) => ({ ...ts, time: ts.time + delta, fail: ts.fail + 1 });
|
|
20
21
|
const timeFormat = (a) => {
|
|
@@ -30,7 +31,12 @@ const timeFormat = (a) => {
|
|
|
30
31
|
* Converts an arbitrary JS value into a `TestSet`.
|
|
31
32
|
*
|
|
32
33
|
* - Zero-argument functions become a `TestEntry`; the `throws` flag is set if
|
|
33
|
-
* `throws` is already `true`
|
|
34
|
+
* `throws` is already `true` (i.e. a `throw` key appears in the ancestor path).
|
|
35
|
+
* The canonical way to declare a throw-test is structural: nest it under a
|
|
36
|
+
* `throw` property key. The secondary `fn.name === 'throw'` check is a legacy
|
|
37
|
+
* path that only fires when a function is extracted from a `throw` key and
|
|
38
|
+
* placed under a different key; that pattern is engine-dependent (unreliable on
|
|
39
|
+
* Bun) and is not a supported authoring style.
|
|
34
40
|
* - Non-null objects become an array of `[key, value]` pairs to recurse into.
|
|
35
41
|
* - All other values (including functions with parameters) produce an empty array.
|
|
36
42
|
*/
|
|
@@ -129,8 +135,7 @@ const runModule = ({ result, test }) => (k, v) => (ts) => {
|
|
|
129
135
|
return walk([], false, v)
|
|
130
136
|
.step(delta => pure(mergeState(ts, delta)));
|
|
131
137
|
};
|
|
132
|
-
const
|
|
133
|
-
const proofEntries = (moduleMap) => entries(moduleMap)
|
|
138
|
+
const proofEntries = (moduleMap) => definedEntries(moduleMap)
|
|
134
139
|
.flatMap(([k, v]) => v.proof !== undefined ? [[k, v.proof]] : []);
|
|
135
140
|
/**
|
|
136
141
|
* Runs all test modules in `moduleMap` whose names pass `isTest`, accumulates
|
|
@@ -10,6 +10,7 @@ export declare const multipleFiles: () => void;
|
|
|
10
10
|
export declare const throwByFunctionName: () => void;
|
|
11
11
|
export declare const namedExports: () => void;
|
|
12
12
|
export declare const defaultReporterOutput: () => void;
|
|
13
|
+
export declare const defaultReporterOutputLargeDuration: () => void;
|
|
13
14
|
export declare const defaultReporterFailOutput: () => void;
|
|
14
15
|
export declare const githubReporterOutput: () => void;
|
|
15
16
|
export declare const registerSuffixes: () => void;
|
|
@@ -21,6 +22,10 @@ export declare const helpers: {
|
|
|
21
22
|
fmtPath: () => void;
|
|
22
23
|
fmtTerm: () => void;
|
|
23
24
|
ghEscape: () => void;
|
|
25
|
+
parseTestSet: {
|
|
26
|
+
nullReturnsEmpty: () => void;
|
|
27
|
+
functionWithParamsReturnsEmpty: () => void;
|
|
28
|
+
};
|
|
24
29
|
};
|
|
25
30
|
export declare const proof: {
|
|
26
31
|
flat: () => void;
|
|
@@ -35,9 +40,11 @@ export declare const proof: {
|
|
|
35
40
|
throwByFunctionName: () => void;
|
|
36
41
|
namedExports: () => void;
|
|
37
42
|
defaultReporterOutput: () => void;
|
|
43
|
+
defaultReporterOutputLargeDuration: () => void;
|
|
38
44
|
defaultReporterFailOutput: () => void;
|
|
39
45
|
githubReporterOutput: () => void;
|
|
40
46
|
registerSuffixes: () => void;
|
|
47
|
+
defaultReporterExpectedToThrow: () => void;
|
|
41
48
|
helpers: {
|
|
42
49
|
isInteger: () => void;
|
|
43
50
|
isIdentifier: () => void;
|
|
@@ -46,5 +53,9 @@ export declare const proof: {
|
|
|
46
53
|
fmtPath: () => void;
|
|
47
54
|
fmtTerm: () => void;
|
|
48
55
|
ghEscape: () => void;
|
|
56
|
+
parseTestSet: {
|
|
57
|
+
nullReturnsEmpty: () => void;
|
|
58
|
+
functionWithParamsReturnsEmpty: () => void;
|
|
59
|
+
};
|
|
49
60
|
};
|
|
50
61
|
};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { pure } from "../effects/module.f.js";
|
|
2
|
+
import { log } from "../effects/node/module.f.js";
|
|
2
3
|
import { emptyState } from "../effects/node/virtual/module.f.js";
|
|
3
4
|
import { virtual } from "../effects/node/virtual/module.f.js";
|
|
4
5
|
import { assert, assertEq, todo } from "../asserts/module.f.js";
|
|
5
|
-
import { testAll, defaultReporter, fmtPath, fmtTerm, fmtImport, ghEscape, isInteger, isIdentifier, registerModule, defaultTest, } from "./module.f.js";
|
|
6
|
+
import { testAll, defaultReporter, fmtPath, fmtTerm, fmtImport, ghEscape, isInteger, isIdentifier, registerModule, parseTestSet, defaultTest, } from "./module.f.js";
|
|
6
7
|
import { run as mockRun } from "../effects/mock/module.f.js";
|
|
7
8
|
import { shouldLoad } from "../dev/module.f.js";
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return [reporter, () => events];
|
|
16
|
-
};
|
|
9
|
+
const writeEvent = (event) => log(JSON.stringify(event));
|
|
10
|
+
const parseEvents = (stdout) => stdout === '' ? [] : stdout.trimEnd().split('\n').map(line => JSON.parse(line));
|
|
11
|
+
const makeReporter = () => ({
|
|
12
|
+
result: (file, path, r, _throws) => writeEvent(['result', file, [...path], r]),
|
|
13
|
+
summary: (pass, fail, time) => writeEvent(['summary', pass, fail, time]),
|
|
14
|
+
test: defaultTest,
|
|
15
|
+
});
|
|
17
16
|
const noopTestContext = { test: todo };
|
|
18
17
|
const options = (initCwd, github = false) => ({
|
|
19
18
|
args: [],
|
|
20
19
|
env: { INIT_CWD: initCwd, ...(github ? { GITHUB_ACTIONS: 'true' } : {}) },
|
|
20
|
+
home: '.',
|
|
21
21
|
std: { stdout: { isTTY: false }, stderr: { isTTY: false } },
|
|
22
22
|
testContext: noopTestContext,
|
|
23
23
|
bunTestContext: noopTestContext,
|
|
@@ -28,10 +28,10 @@ const ok0 = () => ({ result: ['ok', undefined], duration: 0 });
|
|
|
28
28
|
const fail0 = () => ({ result: ['error', 'oops'], duration: 0 });
|
|
29
29
|
const ok1 = () => ({ result: ['ok', undefined], duration: 1 });
|
|
30
30
|
const run = (dir, initCwd = '.') => {
|
|
31
|
-
const
|
|
31
|
+
const reporter = makeReporter();
|
|
32
32
|
const state = { ...emptyState, root: dir };
|
|
33
|
-
const [, exitCode] = virtual(state)(testAll(reporter)(options(initCwd)));
|
|
34
|
-
return [
|
|
33
|
+
const [finalState, exitCode] = virtual(state)(testAll(reporter)(options(initCwd)));
|
|
34
|
+
return [parseEvents(finalState.stdout), exitCode];
|
|
35
35
|
};
|
|
36
36
|
// Runs the real `defaultReporter` and returns its captured stdout/stderr so the
|
|
37
37
|
// terminal and GitHub output formats can be asserted directly.
|
|
@@ -199,6 +199,16 @@ export const defaultReporterOutput = () => {
|
|
|
199
199
|
+ 'Number of tests: pass: 1, fail: 0, total: 1\n'
|
|
200
200
|
+ 'Time: 0.0000 ms\n');
|
|
201
201
|
};
|
|
202
|
+
// timeFormat with duration >= 1ms covers the `yl <= 0` branch (no leading zeros needed)
|
|
203
|
+
export const defaultReporterOutputLargeDuration = () => {
|
|
204
|
+
const [stdout, , exit] = runMain({
|
|
205
|
+
'a.proof.f.ts': () => ({ proof: { x: ok1 } }),
|
|
206
|
+
});
|
|
207
|
+
assertEq(exit, 0);
|
|
208
|
+
assertEq(stdout, 'import("./a.proof.f.ts").proof.x(): ok, 1.0000 ms\n'
|
|
209
|
+
+ 'Number of tests: pass: 1, fail: 0, total: 1\n'
|
|
210
|
+
+ 'Time: 1.0000 ms\n');
|
|
211
|
+
};
|
|
202
212
|
// a failure on the non-GitHub reporter writes the error to stderr, not stdout
|
|
203
213
|
export const defaultReporterFailOutput = () => {
|
|
204
214
|
const [, stderr, exit] = runMain({
|
|
@@ -222,8 +232,8 @@ export const registerSuffixes = () => {
|
|
|
222
232
|
let runner;
|
|
223
233
|
const noopCtx = { test: (_n, _o, _f) => Promise.resolve() };
|
|
224
234
|
const makeRunner = () => mockRun({
|
|
225
|
-
test: (
|
|
226
|
-
all: (
|
|
235
|
+
test: (_ctx, name, _xf, _fn) => (s) => [[...s, name], undefined],
|
|
236
|
+
all: (...effects) => (s) => {
|
|
227
237
|
let st = s;
|
|
228
238
|
const rs = [];
|
|
229
239
|
for (const e of effects) {
|
|
@@ -233,7 +243,7 @@ export const registerSuffixes = () => {
|
|
|
233
243
|
}
|
|
234
244
|
return [st, rs];
|
|
235
245
|
},
|
|
236
|
-
await: (s
|
|
246
|
+
await: p => (s) => [s, [p]],
|
|
237
247
|
});
|
|
238
248
|
runner = makeRunner();
|
|
239
249
|
const proof = {
|
|
@@ -309,6 +319,8 @@ export const helpers = {
|
|
|
309
319
|
assertEq(fmtTerm(['math', 'add']), '| | add');
|
|
310
320
|
assertEq(fmtTerm(['a', '0']), '| | 0');
|
|
311
321
|
assertEq(fmtTerm(['x', 'hello world']), '| | "hello world"');
|
|
322
|
+
// null marks a function-call boundary; fmtTerm filters it out
|
|
323
|
+
assertEq(fmtTerm(['outer', null, 'inner']), '| | inner');
|
|
312
324
|
},
|
|
313
325
|
ghEscape: () => {
|
|
314
326
|
assertEq(ghEscape('a%b'), 'a%25b');
|
|
@@ -317,6 +329,28 @@ export const helpers = {
|
|
|
317
329
|
assertEq(ghEscape('a\r\nb'), 'a%0D%0Ab');
|
|
318
330
|
assertEq(ghEscape('a%b:c,d'), 'a%25b%3Ac%2Cd');
|
|
319
331
|
},
|
|
332
|
+
parseTestSet: {
|
|
333
|
+
nullReturnsEmpty: () => {
|
|
334
|
+
const result = parseTestSet(false, null);
|
|
335
|
+
assertEq(Array.isArray(result), true);
|
|
336
|
+
assertEq(result.length, 0);
|
|
337
|
+
},
|
|
338
|
+
functionWithParamsReturnsEmpty: () => {
|
|
339
|
+
const result = parseTestSet(false, (_x) => _x);
|
|
340
|
+
assertEq(Array.isArray(result), true);
|
|
341
|
+
assertEq(result.length, 0);
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
// a passing throw-test emits '# EXPECTED TO THROW' in its output line
|
|
346
|
+
const defaultReporterExpectedToThrow = () => {
|
|
347
|
+
// fail0 returns a SandboxResult indicating an error; in a throw context
|
|
348
|
+
// defaultTest inverts it to ok, so defaultReporter.result sees s==='ok' and throws===true
|
|
349
|
+
const [stdout, , exit] = runMain({
|
|
350
|
+
'a.proof.f.ts': () => ({ proof: { throw: { x: fail0 } } }),
|
|
351
|
+
});
|
|
352
|
+
assertEq(exit, 0);
|
|
353
|
+
assert(stdout.includes('# EXPECTED TO THROW'), stdout);
|
|
320
354
|
};
|
|
321
355
|
export const proof = {
|
|
322
356
|
flat,
|
|
@@ -331,8 +365,10 @@ export const proof = {
|
|
|
331
365
|
throwByFunctionName,
|
|
332
366
|
namedExports,
|
|
333
367
|
defaultReporterOutput,
|
|
368
|
+
defaultReporterOutputLargeDuration,
|
|
334
369
|
defaultReporterFailOutput,
|
|
335
370
|
githubReporterOutput,
|
|
336
371
|
registerSuffixes,
|
|
372
|
+
defaultReporterExpectedToThrow,
|
|
337
373
|
helpers
|
|
338
374
|
};
|
package/fs/fjs/module.f.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { compile } from "../djs/module.f.js";
|
|
7
7
|
import { main as testMain } from "../emergent_testing/module.f.js";
|
|
8
|
-
import {
|
|
8
|
+
import { commands as casCommands } from "../cas/module.f.js";
|
|
9
9
|
import { main as ciMain } from "../ci/module.f.js";
|
|
10
10
|
import { import_ } from "../effects/node/module.f.js";
|
|
11
11
|
import { dispatch } from "../cli/module.f.js";
|
|
@@ -23,7 +23,7 @@ const commands = [
|
|
|
23
23
|
{
|
|
24
24
|
names: ['cas', 's'],
|
|
25
25
|
description: 'Content-addressable storage operations',
|
|
26
|
-
handler:
|
|
26
|
+
handler: casCommands,
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
names: ['ci', 'i'],
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { assert, assertEq } from "../asserts/module.f.js";
|
|
2
|
+
import { pure } from "../effects/module.f.js";
|
|
3
|
+
import { emptyState, virtual } from "../effects/node/virtual/module.f.js";
|
|
4
|
+
import { main } from "./module.f.js";
|
|
5
|
+
const makeOptions = (args) => ({
|
|
6
|
+
args,
|
|
7
|
+
env: {},
|
|
8
|
+
home: '.',
|
|
9
|
+
std: { stdout: { isTTY: false }, stderr: { isTTY: false } },
|
|
10
|
+
testContext: { test: async () => { } },
|
|
11
|
+
bunTestContext: { test: async () => { } },
|
|
12
|
+
playwrightTestContext: { test: async () => { } },
|
|
13
|
+
engine: 'node',
|
|
14
|
+
});
|
|
15
|
+
const run = (root) => (args) => virtual({ ...emptyState, root })(main(makeOptions(args)));
|
|
16
|
+
const appMain = ({ args }) => pure(args.length);
|
|
17
|
+
export const proof = {
|
|
18
|
+
help: () => {
|
|
19
|
+
const [state, code] = run({})(['help']);
|
|
20
|
+
assertEq(code, 0);
|
|
21
|
+
assert(state.stdout.includes('compile'), 'expected command list in stdout');
|
|
22
|
+
},
|
|
23
|
+
compileRequiresArgs: () => {
|
|
24
|
+
const [state, code] = run({})(['compile']);
|
|
25
|
+
assertEq(code, 1);
|
|
26
|
+
assert(state.stderr.length !== 0, 'expected error in stderr');
|
|
27
|
+
},
|
|
28
|
+
runModule: () => {
|
|
29
|
+
const root = { 'app.f.ts': () => ({ main: appMain }) };
|
|
30
|
+
const [, code] = run(root)(['run', 'app.f.ts', 'x', 'y']);
|
|
31
|
+
// `run` strips the command and file name, so `main` sees two arguments
|
|
32
|
+
assertEq(code, 2);
|
|
33
|
+
},
|
|
34
|
+
throw: {
|
|
35
|
+
runImportError: () => {
|
|
36
|
+
run({})(['run', 'missing.f.ts']);
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
package/fs/fsc/module.f.d.ts
CHANGED
|
@@ -2,4 +2,9 @@ type Result = readonly [readonly string[], ToResult];
|
|
|
2
2
|
type ToResult = (codePoint: number) => Result;
|
|
3
3
|
export declare const terminal = -1;
|
|
4
4
|
export declare const init: ToResult;
|
|
5
|
+
export declare const proof: {
|
|
6
|
+
throw: {
|
|
7
|
+
unionConflict: () => void;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
5
10
|
export {};
|