functionalscript 0.17.0 → 0.18.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/ci/common/module.f.d.ts +4 -5
- package/fs/ci/common/module.f.js +4 -4
- package/fs/ci/config/module.f.d.ts +4 -4
- package/fs/ci/config/module.f.js +4 -4
- package/fs/ci/test.f.js +2 -4
- package/fs/dev/module.f.d.ts +3 -3
- package/fs/dev/module.f.js +12 -9
- package/fs/dev/tf/module.d.ts +1 -0
- package/fs/dev/tf/module.f.d.ts +70 -6
- package/fs/dev/tf/module.f.js +135 -87
- package/fs/dev/tf/module.js +66 -17
- package/fs/dev/tf/test.f.d.ts +21 -20
- package/fs/dev/tf/test.f.js +249 -31
- package/fs/djs/module.f.d.ts +2 -2
- package/fs/djs/module.f.js +8 -5
- package/fs/djs/test.f.js +5 -6
- package/fs/djs/tokenizer-new/test.f.js +126 -78
- package/fs/djs/transpiler/module.f.js +2 -2
- package/fs/djs/transpiler/test.f.js +11 -12
- package/fs/fjs/module.f.d.ts +2 -7
- package/fs/fjs/module.f.js +16 -22
- package/fs/fjs/module.js +2 -2
- package/fs/io/module.d.ts +3 -3
- package/fs/io/module.f.d.ts +5 -2
- package/fs/io/module.f.js +14 -3
- package/fs/io/module.js +38 -17
- package/fs/path/module.f.d.ts +6 -0
- package/fs/path/module.f.js +6 -0
- package/fs/path/test.f.d.ts +3 -5
- package/fs/path/test.f.js +67 -49
- package/fs/text/sgr/module.f.d.ts +9 -1
- package/fs/text/sgr/module.f.js +16 -5
- package/fs/types/effects/node/module.f.d.ts +17 -17
- package/fs/types/effects/node/module.f.js +20 -2
- package/fs/types/effects/node/test.f.js +8 -5
- package/fs/types/effects/node/virtual/module.f.d.ts +11 -2
- package/fs/types/effects/node/virtual/module.f.js +30 -17
- package/fs/types/function/compare/module.f.d.ts +12 -0
- package/fs/types/function/compare/module.f.js +33 -0
- package/fs/types/function/operator/test.f.d.ts +10 -0
- package/fs/types/function/operator/test.f.js +81 -0
- package/fs/types/range_map/module.f.js +3 -18
- package/fs/types/result/module.f.d.ts +4 -0
- package/fs/types/result/module.f.js +4 -0
- package/fs/types/result/test.f.d.ts +2 -4
- package/fs/types/result/test.f.js +24 -16
- package/fs/types/rtti/common/module.f.d.ts +10 -1
- package/fs/types/rtti/common/module.f.js +7 -2
- package/fs/types/rtti/parse/module.f.js +35 -46
- package/fs/types/rtti/validate/module.f.js +9 -12
- package/fs/types/sorted_list/module.f.d.ts +1 -2
- package/fs/types/sorted_list/module.f.js +8 -21
- package/fs/types/sorted_set/module.f.d.ts +1 -3
- package/fs/types/ts/test.f.d.ts +18 -0
- package/fs/types/ts/test.f.js +111 -0
- package/fs/types/uint8array/module.f.js +7 -1
- package/fs/types/uint8array/test.f.d.ts +1 -0
- package/fs/types/uint8array/test.f.js +5 -1
- package/package.json +1 -1
package/fs/io/module.js
CHANGED
|
@@ -10,12 +10,46 @@ import http from 'node:http';
|
|
|
10
10
|
import childProcess from 'node:child_process';
|
|
11
11
|
import fs from 'node:fs';
|
|
12
12
|
import process from 'node:process';
|
|
13
|
-
import { fromIo, run } from "./module.f.js";
|
|
14
13
|
import { concat } from "../path/module.f.js";
|
|
14
|
+
import { once } from 'node:events';
|
|
15
|
+
import { fromIo, runProgram } from "./module.f.js";
|
|
15
16
|
import { error, ok } from "../types/result/module.f.js";
|
|
17
|
+
import { fromVec } from "../types/uint8array/module.f.js";
|
|
16
18
|
const prefix = 'file:///';
|
|
17
19
|
const { now } = Date;
|
|
20
|
+
/** Maps `WriteConsoles` names to the corresponding Node.js writable streams. */
|
|
21
|
+
const streams = {
|
|
22
|
+
stdout: process.stdout,
|
|
23
|
+
stderr: process.stderr,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Writes `data` to `stream` respecting Node.js backpressure.
|
|
27
|
+
*
|
|
28
|
+
* `stream.write()` returns `false` when the internal buffer is full; the data
|
|
29
|
+
* is already buffered at that point (no retry needed) but the caller must not
|
|
30
|
+
* issue more writes until the `'drain'` event fires. Waiting here throttles the
|
|
31
|
+
* producer to the speed of the OS consumer, preventing unbounded memory growth
|
|
32
|
+
* when many large messages arrive faster than they can be flushed.
|
|
33
|
+
*
|
|
34
|
+
* When the buffer is not full `write()` returns `true` and we return
|
|
35
|
+
* immediately, so large computations with occasional prints never stall.
|
|
36
|
+
*
|
|
37
|
+
* @see {@link https://nodejs.org/api/stream.html#writablewritechunk}
|
|
38
|
+
*/
|
|
39
|
+
const writeAll = async (stream, data) => {
|
|
40
|
+
if (!stream.write(data)) {
|
|
41
|
+
await once(stream, 'drain');
|
|
42
|
+
}
|
|
43
|
+
};
|
|
18
44
|
export const asyncImport = (v) => import(__rewriteRelativeImportExtension(v));
|
|
45
|
+
export const tryCatch = f => {
|
|
46
|
+
try {
|
|
47
|
+
return ok(f());
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
return error(e);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
19
53
|
export const io = {
|
|
20
54
|
console,
|
|
21
55
|
fs,
|
|
@@ -27,14 +61,7 @@ export const io = {
|
|
|
27
61
|
},
|
|
28
62
|
performance,
|
|
29
63
|
fetch,
|
|
30
|
-
tryCatch
|
|
31
|
-
try {
|
|
32
|
-
return ok(f());
|
|
33
|
-
}
|
|
34
|
-
catch (e) {
|
|
35
|
-
return error(e);
|
|
36
|
-
}
|
|
37
|
-
},
|
|
64
|
+
tryCatch,
|
|
38
65
|
asyncTryCatch: async (f) => {
|
|
39
66
|
try {
|
|
40
67
|
return ok(await f());
|
|
@@ -61,13 +88,7 @@ export const io = {
|
|
|
61
88
|
}
|
|
62
89
|
return { result, duration: after - before };
|
|
63
90
|
},
|
|
91
|
+
write: (stream, data) => writeAll(streams[stream], fromVec(data)),
|
|
64
92
|
};
|
|
65
|
-
|
|
66
|
-
export const ioRun = (io) => {
|
|
67
|
-
const r = fromIo(io);
|
|
68
|
-
const { argv, env } = io.process;
|
|
69
|
-
const options = { args: argv.slice(2), env };
|
|
70
|
-
return p => r(p(options));
|
|
71
|
-
};
|
|
72
|
-
const effectRun = ioRun(io);
|
|
93
|
+
const effectRun = runProgram(io)(io.process.argv.slice(2));
|
|
73
94
|
export default effectRun;
|
package/fs/path/module.f.d.ts
CHANGED
|
@@ -20,3 +20,9 @@ export declare const normalize: Unary<string, string>;
|
|
|
20
20
|
* Concatenates two path fragments and returns a normalized path.
|
|
21
21
|
*/
|
|
22
22
|
export declare const concat: Reduce<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns `path` relative to `base` with a `./` prefix, or `path` unchanged
|
|
25
|
+
* if it does not start with `base` or `base` is empty.
|
|
26
|
+
* E.g. `relativize('/repo', '/repo/fs/a.ts')` → `'./fs/a.ts'`.
|
|
27
|
+
*/
|
|
28
|
+
export declare const relativize: (base: string, path: string) => string;
|
package/fs/path/module.f.js
CHANGED
|
@@ -46,3 +46,9 @@ export const concat = a => b => {
|
|
|
46
46
|
const s = stringConcat([a, '/', b]);
|
|
47
47
|
return normalize(s);
|
|
48
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* Returns `path` relative to `base` with a `./` prefix, or `path` unchanged
|
|
51
|
+
* if it does not start with `base` or `base` is empty.
|
|
52
|
+
* E.g. `relativize('/repo', '/repo/fs/a.ts')` → `'./fs/a.ts'`.
|
|
53
|
+
*/
|
|
54
|
+
export const relativize = (base, path) => base !== '' && path.startsWith(base) ? `.${path.slice(base.length)}` : path;
|
package/fs/path/test.f.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
export default _default;
|
|
1
|
+
export declare const normalizeTest: (() => void)[];
|
|
2
|
+
export declare const concatTest: (() => void)[];
|
|
3
|
+
export declare const relativizeTest: (() => void)[];
|
package/fs/path/test.f.js
CHANGED
|
@@ -1,49 +1,67 @@
|
|
|
1
|
-
import { concat, normalize } from "./module.f.js";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
()
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
()
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
import { concat, normalize, relativize } from "./module.f.js";
|
|
2
|
+
export const normalizeTest = [
|
|
3
|
+
() => {
|
|
4
|
+
const norm = normalize("dir/file.json");
|
|
5
|
+
if (norm !== "dir/file.json") {
|
|
6
|
+
throw norm;
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
() => {
|
|
10
|
+
const norm = normalize("dir//file.json");
|
|
11
|
+
if (norm !== "dir/file.json") {
|
|
12
|
+
throw norm;
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
() => {
|
|
16
|
+
const norm = normalize("../../dir/file.json");
|
|
17
|
+
if (norm !== "../../dir/file.json") {
|
|
18
|
+
throw norm;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
() => {
|
|
22
|
+
const norm = normalize("../../dir/../file.json");
|
|
23
|
+
if (norm !== "../../file.json") {
|
|
24
|
+
throw norm;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
export const concatTest = [
|
|
29
|
+
() => {
|
|
30
|
+
const c = concat("a")("b");
|
|
31
|
+
if (c !== "a/b") {
|
|
32
|
+
throw c;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
() => {
|
|
36
|
+
const c = concat("a///b/")("c");
|
|
37
|
+
if (c !== "a/b/c") {
|
|
38
|
+
throw c;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
() => {
|
|
42
|
+
const c = concat("a/../b/..")("c");
|
|
43
|
+
if (c !== "c") {
|
|
44
|
+
throw c;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
export const relativizeTest = [
|
|
49
|
+
() => {
|
|
50
|
+
const r = relativize('/repo', '/repo/fs/a.ts');
|
|
51
|
+
if (r !== './fs/a.ts') {
|
|
52
|
+
throw r;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
() => {
|
|
56
|
+
const r = relativize('/repo', '/other/a.ts');
|
|
57
|
+
if (r !== '/other/a.ts') {
|
|
58
|
+
throw r;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
() => {
|
|
62
|
+
const r = relativize('', './fs/a.ts');
|
|
63
|
+
if (r !== './fs/a.ts') {
|
|
64
|
+
throw r;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
];
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import type { Io, Writable } from "../../io/module.f.ts";
|
|
8
|
+
import { type Write, type WriteConsoles, type NodeProgramOptions } from '../../types/effects/node/module.f.ts';
|
|
9
|
+
import { type Effect } from '../../types/effects/module.f.ts';
|
|
8
10
|
export declare const backspace: string;
|
|
9
11
|
type End = 'm';
|
|
10
12
|
type Csi = (code: number | string) => string;
|
|
@@ -44,7 +46,7 @@ export type WriteText = (text: string) => WriteText;
|
|
|
44
46
|
export declare const createConsoleText: (stdout: Stdout) => WriteText;
|
|
45
47
|
export type CsiConsole = (s: string) => void;
|
|
46
48
|
/**
|
|
47
|
-
* Creates a TTY-aware console function.
|
|
49
|
+
* Creates a TTY-aware console function. Appends `\n` to each string before writing.
|
|
48
50
|
*
|
|
49
51
|
* For TTY destinations, ANSI SGR sequences are preserved.
|
|
50
52
|
* For non-TTY destinations, ANSI SGR sequences are stripped.
|
|
@@ -57,4 +59,10 @@ export declare const console: ({ fs: { writeSync } }: Io) => (w: Writable) => Cs
|
|
|
57
59
|
export declare const stdio: (io: Io) => CsiConsole;
|
|
58
60
|
/** Writes to process stderr using a TTY-aware CSI console. */
|
|
59
61
|
export declare const stderr: (io: Io) => CsiConsole;
|
|
62
|
+
/**
|
|
63
|
+
* Effect-based TTY-aware write. Strips ANSI SGR sequences when the target
|
|
64
|
+
* stream is not a TTY, then encodes to UTF-8 and emits a `Write` effect.
|
|
65
|
+
* Does NOT append `\n` — callers are responsible for line termination.
|
|
66
|
+
*/
|
|
67
|
+
export declare const csiWrite: ({ std }: NodeProgramOptions) => (stream: WriteConsoles) => (s: string) => Effect<Write, void>;
|
|
60
68
|
export {};
|
package/fs/text/sgr/module.f.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
|
+
import { write } from "../../types/effects/node/module.f.js";
|
|
8
|
+
import {} from "../../types/effects/module.f.js";
|
|
9
|
+
import { utf8 } from "../module.f.js";
|
|
7
10
|
export const backspace = '\x08';
|
|
8
11
|
const begin = '\x1b[';
|
|
9
12
|
/**
|
|
@@ -46,8 +49,9 @@ export const createConsoleText = (stdout) => {
|
|
|
46
49
|
};
|
|
47
50
|
return f('');
|
|
48
51
|
};
|
|
52
|
+
const str = (isTTY) => (s) => isTTY ? s : s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
49
53
|
/**
|
|
50
|
-
* Creates a TTY-aware console function.
|
|
54
|
+
* Creates a TTY-aware console function. Appends `\n` to each string before writing.
|
|
51
55
|
*
|
|
52
56
|
* For TTY destinations, ANSI SGR sequences are preserved.
|
|
53
57
|
* For non-TTY destinations, ANSI SGR sequences are stripped.
|
|
@@ -56,12 +60,19 @@ export const createConsoleText = (stdout) => {
|
|
|
56
60
|
* @returns A function that targets a writable stream.
|
|
57
61
|
*/
|
|
58
62
|
export const console = ({ fs: { writeSync } }) => (w) => {
|
|
59
|
-
const
|
|
60
|
-
return
|
|
61
|
-
? (s) => writeSync(w.fd, s + '\n')
|
|
62
|
-
: (s) => writeSync(w.fd, s.replace(/\x1b\[[0-9;]*m/g, '') + '\n');
|
|
63
|
+
const toStr = str(w.isTTY);
|
|
64
|
+
return (s) => writeSync(w.fd, toStr(s) + '\n');
|
|
63
65
|
};
|
|
64
66
|
/** Writes to process stdout using a TTY-aware CSI console. */
|
|
65
67
|
export const stdio = (io) => console(io)(io.process.stdout);
|
|
66
68
|
/** Writes to process stderr using a TTY-aware CSI console. */
|
|
67
69
|
export const stderr = (io) => console(io)(io.process.stderr);
|
|
70
|
+
/**
|
|
71
|
+
* Effect-based TTY-aware write. Strips ANSI SGR sequences when the target
|
|
72
|
+
* stream is not a TTY, then encodes to UTF-8 and emits a `Write` effect.
|
|
73
|
+
* Does NOT append `\n` — callers are responsible for line termination.
|
|
74
|
+
*/
|
|
75
|
+
export const csiWrite = ({ std }) => (stream) => {
|
|
76
|
+
const toStr = str(std[stream].isTTY);
|
|
77
|
+
return (s) => write(stream, utf8(toStr(s)));
|
|
78
|
+
};
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Node.js effect operations: filesystem (`mkdir`, `readFile`, `readdir`,
|
|
3
|
-
* `writeFile`, `rm`, `access`), networking (`fetch`, `createServer`, `listen`),
|
|
4
|
-
* subprocess `exec`, console (`log`, `error`), `import_`, `now`, `sandbox`, `forever`,
|
|
5
|
-
* and `all`/`both` parallelism; defines the `NodeOp`/`NodeProgram` types used
|
|
6
|
-
* by the Node runner.
|
|
7
|
-
*
|
|
8
|
-
* @module
|
|
9
|
-
*/
|
|
10
1
|
import type { Vec } from '../../bit_vec/module.f.ts';
|
|
11
2
|
import type { Nominal } from '../../nominal/module.f.ts';
|
|
12
3
|
import type { Result } from '../../result/module.f.ts';
|
|
@@ -58,11 +49,6 @@ export declare const exec: Func<Exec>;
|
|
|
58
49
|
export type Access = readonly ['access', (path: string) => IoResult<void>];
|
|
59
50
|
export declare const access: Func<Access>;
|
|
60
51
|
export type Fs = Mkdir | ReadFile | Readdir | WriteFile | Rm | Exec | Access;
|
|
61
|
-
export type Error = ['error', (message: string) => void];
|
|
62
|
-
export declare const error: Func<Error>;
|
|
63
|
-
export type Log = ['log', (message: string) => void];
|
|
64
|
-
export declare const log: Func<Log>;
|
|
65
|
-
export type Console = Log | Error;
|
|
66
52
|
export type Server = Nominal<'server', `160855c4f69310fece3273c1853ac32de43dee1eb41bf59d821917f8eebe9272`, unknown>;
|
|
67
53
|
export type Headers = {
|
|
68
54
|
readonly [k in string]: string;
|
|
@@ -87,10 +73,18 @@ export type Http = CreateServer | Listen;
|
|
|
87
73
|
export type Forever = ['forever', () => never];
|
|
88
74
|
export declare const forever: Func<Forever>;
|
|
89
75
|
export type Module = {
|
|
90
|
-
readonly
|
|
76
|
+
readonly [k in string]: unknown;
|
|
91
77
|
};
|
|
92
78
|
export type Import = ['import', (path: string) => IoResult<Module>];
|
|
93
79
|
export declare const import_: Func<Import>;
|
|
80
|
+
export type WriteConsoles = 'stdout' | 'stderr';
|
|
81
|
+
export type Write = readonly ['write', (stream: WriteConsoles, data: Vec) => void];
|
|
82
|
+
export declare const write: Func<Write>;
|
|
83
|
+
export type Console = (s: string) => Effect<Write, void>;
|
|
84
|
+
/** Writes a line to `stdout`. Replaces the retired `Log` effect. */
|
|
85
|
+
export declare const log: Console;
|
|
86
|
+
/** Writes a line to `stderr`. Replaces the retired `Error` effect. */
|
|
87
|
+
export declare const error: Console;
|
|
94
88
|
export type Now = readonly ['now', () => number];
|
|
95
89
|
export declare const now: Func<Now>;
|
|
96
90
|
export type SandboxResult<T> = {
|
|
@@ -122,7 +116,7 @@ export type Sandbox = readonly ['sandbox', <T>(f: () => T) => SandboxResult<T>];
|
|
|
122
116
|
* @see {@link SandboxResult}
|
|
123
117
|
*/
|
|
124
118
|
export declare const sandbox: Func<Sandbox>;
|
|
125
|
-
export type NodeOp = All | Fetch |
|
|
119
|
+
export type NodeOp = All | Fetch | Fs | Http | Forever | Import | Now | Sandbox | Write;
|
|
126
120
|
export type NodeEffect<T> = Effect<NodeOp, T>;
|
|
127
121
|
export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
|
|
128
122
|
/**
|
|
@@ -134,5 +128,11 @@ export type Env = {
|
|
|
134
128
|
export type NodeProgramOptions = {
|
|
135
129
|
readonly args: readonly string[];
|
|
136
130
|
readonly env: Env;
|
|
131
|
+
readonly std: {
|
|
132
|
+
readonly [k in WriteConsoles]: {
|
|
133
|
+
readonly isTTY: boolean;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
137
136
|
};
|
|
138
|
-
export type
|
|
137
|
+
export type Program<O extends Operation> = (options: NodeProgramOptions) => Effect<O, number>;
|
|
138
|
+
export type NodeProgram = Program<NodeOp>;
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js effect operations: filesystem (`mkdir`, `readFile`, `readdir`,
|
|
3
|
+
* `writeFile`, `rm`, `access`), networking (`fetch`, `createServer`, `listen`),
|
|
4
|
+
* subprocess `exec`, `log`/`error` (wrappers over `write`), `import_`, `now`,
|
|
5
|
+
* `sandbox`, `forever`, and `all`/`both` parallelism; defines the
|
|
6
|
+
* `NodeOp`/`NodeProgram` types used by the Node runner.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { utf8 } from "../../../text/module.f.js";
|
|
1
11
|
import { do_ } from "../module.f.js";
|
|
2
12
|
const doAll = do_('all');
|
|
3
13
|
/**
|
|
@@ -17,12 +27,20 @@ export const writeFile = do_('writeFile');
|
|
|
17
27
|
export const rm = do_('rm');
|
|
18
28
|
export const exec = do_('exec');
|
|
19
29
|
export const access = do_('access');
|
|
20
|
-
export const error = do_('error');
|
|
21
|
-
export const log = do_('log');
|
|
22
30
|
export const createServer = do_('createServer');
|
|
23
31
|
export const listen = do_('listen');
|
|
24
32
|
export const forever = do_('forever');
|
|
25
33
|
export const import_ = do_('import');
|
|
34
|
+
export const write = do_('write');
|
|
35
|
+
/**
|
|
36
|
+
* Encodes `s + '\n'` as UTF-8 and emits a `Write` effect to `stream`.
|
|
37
|
+
* Shared implementation for `log` and `error`.
|
|
38
|
+
*/
|
|
39
|
+
const writeString = (stream) => (s) => write(stream, utf8(s + '\n'));
|
|
40
|
+
/** Writes a line to `stdout`. Replaces the retired `Log` effect. */
|
|
41
|
+
export const log = writeString('stdout');
|
|
42
|
+
/** Writes a line to `stderr`. Replaces the retired `Error` effect. */
|
|
43
|
+
export const error = writeString('stderr');
|
|
26
44
|
export const now = do_('now');
|
|
27
45
|
/**
|
|
28
46
|
* Runs a plain synchronous function in an isolated, measured environment.
|
|
@@ -64,11 +64,11 @@ export default {
|
|
|
64
64
|
throw result;
|
|
65
65
|
}
|
|
66
66
|
const tmp = state.root.tmp;
|
|
67
|
-
if (tmp
|
|
67
|
+
if (typeof tmp !== 'object') {
|
|
68
68
|
throw state.root;
|
|
69
69
|
}
|
|
70
70
|
const cache = tmp.cache;
|
|
71
|
-
if (cache
|
|
71
|
+
if (typeof cache !== 'object') {
|
|
72
72
|
throw tmp;
|
|
73
73
|
}
|
|
74
74
|
},
|
|
@@ -294,7 +294,7 @@ export default {
|
|
|
294
294
|
throw result;
|
|
295
295
|
}
|
|
296
296
|
const tmp = state.root.tmp;
|
|
297
|
-
if (tmp
|
|
297
|
+
if (typeof tmp !== 'object') {
|
|
298
298
|
throw state.root;
|
|
299
299
|
}
|
|
300
300
|
if (tmp.cache !== undefined) {
|
|
@@ -333,8 +333,11 @@ export default {
|
|
|
333
333
|
}
|
|
334
334
|
},
|
|
335
335
|
sandbox: {
|
|
336
|
+
// Virtual `sandbox` is now a pass-through: the function is expected
|
|
337
|
+
// to return a `SandboxResult` directly. Fixtures dictate the result
|
|
338
|
+
// (and `duration`) instead of the runner measuring.
|
|
336
339
|
ok: () => {
|
|
337
|
-
const [_, { result, duration }] = virtual(emptyState)(sandbox(() => 42));
|
|
340
|
+
const [_, { result, duration }] = virtual(emptyState)(sandbox(() => ({ result: ['ok', 42], duration: 0 })));
|
|
338
341
|
if (result[0] !== 'ok') {
|
|
339
342
|
throw result;
|
|
340
343
|
}
|
|
@@ -347,7 +350,7 @@ export default {
|
|
|
347
350
|
},
|
|
348
351
|
error: () => {
|
|
349
352
|
const err = new Error('fail');
|
|
350
|
-
const [_, { result }] = virtual(emptyState)(sandbox(() => {
|
|
353
|
+
const [_, { result }] = virtual(emptyState)(sandbox(() => ({ result: ['error', err], duration: 0 })));
|
|
351
354
|
if (result[0] !== 'error') {
|
|
352
355
|
throw result;
|
|
353
356
|
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { type Vec } from '../../../bit_vec/module.f.ts';
|
|
2
2
|
import { type RunInstance } from '../../mock/module.f.ts';
|
|
3
|
-
import type { NodeOp } from '../module.f.ts';
|
|
3
|
+
import type { Module, NodeOp } from '../module.f.ts';
|
|
4
|
+
/**
|
|
5
|
+
* In-memory JS module entry. When `import_` is called on the path, the
|
|
6
|
+
* function is invoked and its return value is the module value (with a
|
|
7
|
+
* `default` export and optional named exports). Using a function (not a
|
|
8
|
+
* plain value) lets the entry be distinguished from `Vec`/`Dir` at runtime
|
|
9
|
+
* via `typeof === 'function'`, and lets the fixture compute the module on
|
|
10
|
+
* each import for closures/state.
|
|
11
|
+
*/
|
|
12
|
+
export type JsModule = () => Module;
|
|
4
13
|
export type Dir = {
|
|
5
|
-
readonly [name in string]?: Dir | Vec;
|
|
14
|
+
readonly [name in string]?: Dir | Vec | JsModule;
|
|
6
15
|
};
|
|
7
16
|
export type State = {
|
|
8
17
|
stdout: string;
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { todo } from "../../../../dev/module.f.js";
|
|
6
|
+
import { assert, todo } from "../../../../dev/module.f.js";
|
|
7
7
|
import { parse } from "../../../../path/module.f.js";
|
|
8
|
+
import { utf8ToString } from "../../../../text/module.f.js";
|
|
8
9
|
import { isVec } from "../../../bit_vec/module.f.js";
|
|
9
10
|
import { error, ok } from "../../../result/module.f.js";
|
|
10
11
|
import { run } from "../../mock/module.f.js";
|
|
@@ -22,7 +23,7 @@ const operation = (op) => {
|
|
|
22
23
|
}
|
|
23
24
|
const [first, ...rest] = path;
|
|
24
25
|
const subDir = dir[first];
|
|
25
|
-
if (subDir
|
|
26
|
+
if (typeof subDir !== 'object') {
|
|
26
27
|
return op(dir, path);
|
|
27
28
|
}
|
|
28
29
|
const [newSubDir, r] = f(subDir, rest);
|
|
@@ -54,11 +55,24 @@ const readFile = readOperation((dir, path) => {
|
|
|
54
55
|
return readFileError;
|
|
55
56
|
}
|
|
56
57
|
const file = dir[path[0]];
|
|
58
|
+
if (typeof file === 'function') {
|
|
59
|
+
throw new Error(`'${path[0]}' is a JsModule; readFile not supported`);
|
|
60
|
+
}
|
|
57
61
|
if (!isVec(file)) {
|
|
58
62
|
return error(`'${path[0]}' is not a file`);
|
|
59
63
|
}
|
|
60
64
|
return ok(file);
|
|
61
65
|
});
|
|
66
|
+
const import_ = readOperation((dir, path) => {
|
|
67
|
+
if (path.length !== 1) {
|
|
68
|
+
return error('no such file');
|
|
69
|
+
}
|
|
70
|
+
const entry = dir[path[0]];
|
|
71
|
+
if (typeof entry !== 'function') {
|
|
72
|
+
return error(`'${path[0]}' is not a JsModule`);
|
|
73
|
+
}
|
|
74
|
+
return ok(entry());
|
|
75
|
+
});
|
|
62
76
|
const writeFileError = error('invalid file');
|
|
63
77
|
const writeFile = (payload) => operation((dir, path) => {
|
|
64
78
|
if (path.length !== 1) {
|
|
@@ -85,7 +99,7 @@ const readdir = (base, recursive) => readOperation((dir, path) => {
|
|
|
85
99
|
if (content === undefined) {
|
|
86
100
|
continue;
|
|
87
101
|
}
|
|
88
|
-
const isFile =
|
|
102
|
+
const isFile = typeof content !== 'object';
|
|
89
103
|
result = [...result, { name, parentPath, isFile }];
|
|
90
104
|
if (!isFile && recursive) {
|
|
91
105
|
result = [...result, ...f(`${parentPath}/${name}`, content)];
|
|
@@ -113,13 +127,12 @@ const rm = operation((dir, path) => {
|
|
|
113
127
|
if (entry === undefined) {
|
|
114
128
|
return [dir, error('no such file')];
|
|
115
129
|
}
|
|
116
|
-
if (
|
|
130
|
+
if (typeof entry === 'object') {
|
|
117
131
|
return [dir, error('is a directory')];
|
|
118
132
|
}
|
|
119
133
|
const { [name]: _, ...rest } = dir;
|
|
120
134
|
return [rest, okVoid];
|
|
121
135
|
});
|
|
122
|
-
const console = (name) => (state, payload) => [{ ...state, [name]: `${state[name]}${payload}\n` }, undefined];
|
|
123
136
|
const map = {
|
|
124
137
|
all: (state, ...a) => {
|
|
125
138
|
let e = [];
|
|
@@ -130,8 +143,6 @@ const map = {
|
|
|
130
143
|
}
|
|
131
144
|
return [state, e];
|
|
132
145
|
},
|
|
133
|
-
error: console('stderr'),
|
|
134
|
-
log: console('stdout'),
|
|
135
146
|
fetch: (state, url) => {
|
|
136
147
|
const result = state.internet[url];
|
|
137
148
|
return result === undefined ? [state, error('not found')] : [state, ok(result)];
|
|
@@ -141,22 +152,24 @@ const map = {
|
|
|
141
152
|
readdir: (state, path, { recursive }) => readdir(path, recursive === true)(state, path),
|
|
142
153
|
writeFile: (state, path, payload) => writeFile(payload)(state, path),
|
|
143
154
|
access,
|
|
144
|
-
import:
|
|
155
|
+
import: import_,
|
|
145
156
|
rm,
|
|
146
157
|
exec: todo,
|
|
147
158
|
createServer: todo,
|
|
148
159
|
listen: todo,
|
|
149
160
|
forever: todo,
|
|
150
161
|
now: (state) => [state, state.epochNs],
|
|
151
|
-
sandbox:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
162
|
+
// Virtual sandbox is a pass-through: the fixture's test function is
|
|
163
|
+
// expected to return a `SandboxResult` directly (encoding pass/fail and a
|
|
164
|
+
// chosen duration), so the handler invokes it without try/catch or clock
|
|
165
|
+
// reads. This makes test outcomes deterministic — fixtures dictate the
|
|
166
|
+
// result instead of the runner measuring real execution. A genuine
|
|
167
|
+
// exception in a fixture propagates loudly as a bug in the fixture.
|
|
168
|
+
// See: issues/156-tf-virtual-tests.md
|
|
169
|
+
sandbox: (state, f) => [state, f()],
|
|
170
|
+
write: (state, stream, data) => {
|
|
171
|
+
const s = utf8ToString(data);
|
|
172
|
+
return [{ ...state, [stream]: `${state[stream]}${s}` }, undefined];
|
|
160
173
|
},
|
|
161
174
|
};
|
|
162
175
|
export const virtual = run(map);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { Index3, Index5, Array2 } from '../../array/module.f.ts';
|
|
7
7
|
export type Sign = -1 | 0 | 1;
|
|
8
8
|
export type Compare<T> = (_: T) => Sign;
|
|
9
|
+
export type Cmp<T> = (a: T) => Compare<T>;
|
|
9
10
|
export declare const index3: <T>(cmp: Compare<T>) => (value: T) => Index3;
|
|
10
11
|
export declare const index5: <T>(cmp: Compare<T>) => (v2: Array2<T>) => Index5;
|
|
11
12
|
export type Cmp1 = boolean | string | number | bigint;
|
|
@@ -23,3 +24,14 @@ export type Cmp2<A, B> = [
|
|
|
23
24
|
B
|
|
24
25
|
] extends [bigint, bigint] ? bigint : never;
|
|
25
26
|
export declare const cmp: <A extends Cmp1>(a: A) => <B extends Cmp2<A, B>>(b: B) => Sign;
|
|
27
|
+
/**
|
|
28
|
+
* Binary search over `[0, len)`. `probe(mid)` returns the sign of the search
|
|
29
|
+
* key relative to the element at `mid` (`-1` before, `0` at, `1` after). On a
|
|
30
|
+
* hit it returns the matching index; on a miss it returns the converged lower
|
|
31
|
+
* bound `b` (the insertion point), which may equal `len`.
|
|
32
|
+
*
|
|
33
|
+
* `probe` must be monotonic over `[0, len)`: scanning indices left to right its
|
|
34
|
+
* result is non-increasing — a run of `1`s, then `0`s, then `-1`s. A
|
|
35
|
+
* non-monotonic probe yields an undefined position.
|
|
36
|
+
*/
|
|
37
|
+
export declare const bsearch: (len: number) => (probe: (mid: number) => Sign) => number;
|
|
@@ -4,3 +4,36 @@ export const index5 = cmp => ([v0, v1]) => {
|
|
|
4
4
|
return (_0 <= 0 ? _0 + 1 : cmp(v1) + 3);
|
|
5
5
|
};
|
|
6
6
|
export const cmp = (a) => (b) => a < b ? -1 : a > b ? 1 : 0;
|
|
7
|
+
/**
|
|
8
|
+
* Binary search over `[0, len)`. `probe(mid)` returns the sign of the search
|
|
9
|
+
* key relative to the element at `mid` (`-1` before, `0` at, `1` after). On a
|
|
10
|
+
* hit it returns the matching index; on a miss it returns the converged lower
|
|
11
|
+
* bound `b` (the insertion point), which may equal `len`.
|
|
12
|
+
*
|
|
13
|
+
* `probe` must be monotonic over `[0, len)`: scanning indices left to right its
|
|
14
|
+
* result is non-increasing — a run of `1`s, then `0`s, then `-1`s. A
|
|
15
|
+
* non-monotonic probe yields an undefined position.
|
|
16
|
+
*/
|
|
17
|
+
export const bsearch = (len) => (probe) => {
|
|
18
|
+
let b = 0;
|
|
19
|
+
let e = len - 1;
|
|
20
|
+
while (true) {
|
|
21
|
+
if (e < b) {
|
|
22
|
+
return b;
|
|
23
|
+
}
|
|
24
|
+
const mid = b + (e - b >> 1);
|
|
25
|
+
switch (probe(mid)) {
|
|
26
|
+
case -1: {
|
|
27
|
+
e = mid - 1;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
case 0: {
|
|
31
|
+
return mid;
|
|
32
|
+
}
|
|
33
|
+
case 1: {
|
|
34
|
+
b = mid + 1;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const joinTest: () => void;
|
|
2
|
+
export declare const concatTest: () => void;
|
|
3
|
+
export declare const logicalNotTest: () => void;
|
|
4
|
+
export declare const strictEqualTest: () => void;
|
|
5
|
+
export declare const additionTest: () => void;
|
|
6
|
+
export declare const minTest: () => void;
|
|
7
|
+
export declare const maxTest: () => void;
|
|
8
|
+
export declare const incrementTest: () => void;
|
|
9
|
+
export declare const foldToScanTest: () => void;
|
|
10
|
+
export declare const reduceToScanTest: () => void;
|