@amritk/nish-x86_64-linux 0.20.0 → 0.22.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/INSTALL.md +52 -10
- package/bin/nish +0 -0
- package/docs/AI.md +2777 -0
- package/package.json +2 -1
- package/runtime/browser.d.ts +6 -0
- package/runtime/browser.mjs +77 -0
- package/runtime/core/arrays.d.ts +36 -0
- package/runtime/core/arrays.mjs +153 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +80 -0
- package/runtime/core/host.mjs +325 -0
- package/runtime/core/numbers.d.ts +66 -0
- package/runtime/core/numbers.mjs +343 -0
- package/runtime/core/result.d.ts +45 -0
- package/runtime/core/result.mjs +140 -0
- package/runtime/core/strings.d.ts +14 -0
- package/runtime/core/strings.mjs +169 -0
- package/runtime/core.d.ts +9 -0
- package/runtime/core.mjs +55 -0
- package/runtime/host.d.ts +6 -0
- package/runtime/nish.d.ts +46 -2
- package/runtime/nish.h +75 -2
- package/runtime/nish.mjs +23 -3
- package/runtime/node.mjs +386 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +1 -1
- package/runtime/runtime-host.c +1 -1
- package/runtime/runtime-net.c +12 -1
- package/runtime/runtime-os.c +1 -1
- package/runtime/runtime-parallel.c +1 -1
- package/runtime/runtime-simd.c +231 -7
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +0 -11
- package/runtime/runtime.c +190 -6
- package/runtime/shim.mjs +6 -985
- package/runtime/sqlite/LICENSE.md +85 -0
- package/runtime/sqlite/README.md +45 -0
- package/runtime/sqlite/nish-sqlite.c +359 -0
- package/runtime/sqlite/sqlite3.c +271131 -0
- package/runtime/sqlite/sqlite3.h +14447 -0
- package/scripts/build.sh +311 -59
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +32 -12
- package/std/base64.ts +116 -0
- package/std/bytes.ts +256 -0
- package/std/cli.ts +344 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +1557 -0
- package/std/crypto/hmac.ts +138 -2
- package/std/crypto/x509.ts +11 -122
- package/std/json-tokenizer.ts +2998 -57
- package/std/json-value.ts +22 -3
- package/std/json.ts +87 -8
- package/std/math.ts +608 -0
- package/std/net/http1-server.ts +282 -40
- package/std/net/http1.ts +40 -21
- package/std/net/http3-server.ts +50 -12
- package/std/net/postgres.ts +2294 -0
- package/std/net/prefork.ts +23 -3
- package/std/net/quic-listener.ts +20 -4
- package/std/random.ts +66 -4
- package/std/regex.ts +4274 -0
- package/std/sqlite.ts +391 -0
- package/std/term-io.ts +978 -0
- package/std/term-pixels.ts +874 -0
- package/std/term-probe.ts +459 -0
- package/std/term-rate.ts +993 -0
- package/std/term.ts +2453 -0
- package/std/text.ts +90 -7
- package/std/yaml-lines.ts +141 -0
- package/std/yaml.ts +4675 -0
package/runtime/node.mjs
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Node host (WP33 R3): importing this module installs, into
|
|
3
|
+
* `runtime/core/host.mjs`, the output, exit, arguments and host-bound builtins
|
|
4
|
+
* Node gives a rewritten program. Output is written with `fs.writeSync`, so
|
|
5
|
+
* `process.exit` cannot lose it. What each builtin answers, and where it parts
|
|
6
|
+
* from the native runtime (runtime/runtime-os.c, runtime/runtime-host.c), is
|
|
7
|
+
* written beside it.
|
|
8
|
+
*/
|
|
9
|
+
import child_process from "node:child_process";
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import os from "node:os";
|
|
12
|
+
import tty from "node:tty";
|
|
13
|
+
import { installHost } from "./core/host.mjs";
|
|
14
|
+
|
|
15
|
+
function ioFail(verb, path) {
|
|
16
|
+
fs.writeSync(2, `nish: cannot ${verb} ${path}\n`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function readFileSync(path) {
|
|
21
|
+
try {
|
|
22
|
+
return fs.readFileSync(path, "utf8");
|
|
23
|
+
} catch {
|
|
24
|
+
return ioFail("read", path);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** `readFileSyncOrNull(path)`: null instead of exiting, so the program decides. */
|
|
29
|
+
function readFileSyncOrNull(path) {
|
|
30
|
+
try {
|
|
31
|
+
return fs.readFileSync(path, "utf8");
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** `readFileBytesSync(path)` (WP34 N2): the bytes as a plain array of numbers, or null. */
|
|
38
|
+
function readFileBytesSync(path) {
|
|
39
|
+
try {
|
|
40
|
+
return Array.from(fs.readFileSync(path));
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The flags `runtime-os.c` opens a file it writes with, `O_NOFOLLOW` included
|
|
48
|
+
* (docs/security/runtime.md, RT-4): a symbolic link as the last component of
|
|
49
|
+
* the path is refused rather than followed, so `writeFileSync`,
|
|
50
|
+
* `appendFileSync` and `spawnSyncTo` fail here where they fail natively. Node's
|
|
51
|
+
* `"w"` and `"a"` follow the link. A link among the directories on the way is
|
|
52
|
+
* still followed, as it is natively. Windows has no `O_NOFOLLOW`, and there the
|
|
53
|
+
* flag is 0.
|
|
54
|
+
*/
|
|
55
|
+
const { O_WRONLY, O_CREAT, O_TRUNC, O_APPEND, O_NOFOLLOW = 0 } = fs.constants;
|
|
56
|
+
const WRITE_FLAGS = O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW;
|
|
57
|
+
const APPEND_FLAGS = O_WRONLY | O_CREAT | O_APPEND | O_NOFOLLOW;
|
|
58
|
+
|
|
59
|
+
function writeFileSync(path, data) {
|
|
60
|
+
try {
|
|
61
|
+
fs.writeFileSync(path, data, { encoding: "utf8", flag: WRITE_FLAGS });
|
|
62
|
+
} catch {
|
|
63
|
+
ioFail("write", path);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function appendFileSync(path, data) {
|
|
68
|
+
try {
|
|
69
|
+
fs.appendFileSync(path, data, { encoding: "utf8", flag: APPEND_FLAGS });
|
|
70
|
+
} catch {
|
|
71
|
+
ioFail("write", path);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* `mkdirSync(path)` (WP14 D4): one directory, not recursive, answering whether
|
|
77
|
+
* a directory is there afterwards. Node throws where the runtime answers
|
|
78
|
+
* false, and `EEXIST` on a plain file is a failure here as it is there, so the
|
|
79
|
+
* `statSync` decides rather than the exception.
|
|
80
|
+
*/
|
|
81
|
+
function mkdirSync(path) {
|
|
82
|
+
try {
|
|
83
|
+
fs.mkdirSync(path);
|
|
84
|
+
return true;
|
|
85
|
+
} catch {
|
|
86
|
+
try {
|
|
87
|
+
return fs.statSync(path).isDirectory();
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* `isDirectorySync(path)` (WP14 §7a): one stat, and a boolean out of it rather
|
|
96
|
+
* than an exception, which is what the runtime's `stat` answers too.
|
|
97
|
+
*/
|
|
98
|
+
function isDirectorySync(path) {
|
|
99
|
+
try {
|
|
100
|
+
return fs.statSync(path).isDirectory();
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* `readdirSync(path)`: the entries, sorted ascending by bytes, or `null` when
|
|
108
|
+
* the directory cannot be read. Node throws where the runtime answers a value,
|
|
109
|
+
* so the `catch` is what makes the two agree, and a directory that exists and
|
|
110
|
+
* is empty answers an empty array on both sides. Node's readdir never yields
|
|
111
|
+
* `.` or `..` — the pair `runtime-os.c` skips explicitly — so there is nothing
|
|
112
|
+
* to filter out here.
|
|
113
|
+
*
|
|
114
|
+
* The sort is the semantic point. `runtime-os.c` orders the names with
|
|
115
|
+
* `strcmp`, which compares UTF-8 bytes, and `Array#sort` compares UTF-16 code
|
|
116
|
+
* units. The two agree on ASCII names and part company above the BMP, where a
|
|
117
|
+
* surrogate pair sorts below `U+E000`..`U+FFFF` in UTF-16 and above them in
|
|
118
|
+
* UTF-8. So the comparison is over the encoded bytes, which is the native order
|
|
119
|
+
* exactly rather than the native order for the names that happen to be ASCII.
|
|
120
|
+
*/
|
|
121
|
+
function readdirSync(path) {
|
|
122
|
+
let names;
|
|
123
|
+
try {
|
|
124
|
+
names = fs.readdirSync(path);
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return names.sort((a, b) => Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8")));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* `realpathSync(path)` (WP19 §5a item 4): `path` with its symbolic links
|
|
133
|
+
* resolved, or `null` when it does not resolve.
|
|
134
|
+
*
|
|
135
|
+
* `fs.realpathSync` throws where the native `realpath` answers NULL — a
|
|
136
|
+
* missing path, a loop, a component that is not a directory — so the `catch`
|
|
137
|
+
* is what makes the two runtimes agree, exactly as it does for `readdirSync`
|
|
138
|
+
* above. Node's own answer is already absolute and already normalised, which
|
|
139
|
+
* is what POSIX `realpath` guarantees, so nothing here has to normalise it.
|
|
140
|
+
*/
|
|
141
|
+
function realpathSync(path) {
|
|
142
|
+
try {
|
|
143
|
+
return fs.realpathSync(path);
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* `process.platform` / `process.arch` (WP14 §7a). Node's spellings are the
|
|
151
|
+
* ones `runtime-os.c` answers with, so on any machine this compiler has a
|
|
152
|
+
* triple for the two runtimes give the same string; elsewhere the native build
|
|
153
|
+
* says `unknown` where Node names the platform, which is the one place they
|
|
154
|
+
* part.
|
|
155
|
+
*/
|
|
156
|
+
function platform() {
|
|
157
|
+
return process.platform;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function arch() {
|
|
161
|
+
return process.arch;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* `getenv(name)` (WP19 R1): the value, or `null` when the variable is unset.
|
|
166
|
+
* Node answers `undefined` there and the language has no `undefined`, so the
|
|
167
|
+
* `??` is what makes the two runtimes agree; an empty value stays an empty
|
|
168
|
+
* string on both sides, because `CC=` is set and `CC` unset is not.
|
|
169
|
+
*/
|
|
170
|
+
function getenv(name) {
|
|
171
|
+
return process.env[name] ?? null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* `spawnSync(argv)` and `spawnSyncTo(argv, out, err)` (WP14 D4), which are one
|
|
176
|
+
* run with its streams answered differently: the child's exit status, 128 + n
|
|
177
|
+
* when signal n killed it, -1 for an empty vector or a program that would not
|
|
178
|
+
* start. `runtime-os.c` puts one `static nish_spawn_impl` behind both builtins
|
|
179
|
+
* for the same reason this module puts one function behind both helpers — the
|
|
180
|
+
* argument vector, the wait and the signal convention are written once and
|
|
181
|
+
* cannot drift between the two.
|
|
182
|
+
*
|
|
183
|
+
* `out` and `err` are paths for the child's stdout and stderr, and an **empty**
|
|
184
|
+
* string leaves that stream inherited. Each file is created or truncated at
|
|
185
|
+
* 0644 with `WRITE_FLAGS`, which is what the runtime's file actions ask for:
|
|
186
|
+
* `O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW`, so a symbolic link at the path
|
|
187
|
+
* is refused and the answer is -1, on both sides. The descriptors opened here are
|
|
188
|
+
* closed again whichever way the child went; natively the child opens them and
|
|
189
|
+
* its exit drops them.
|
|
190
|
+
*/
|
|
191
|
+
function spawnImpl(argv, out, err) {
|
|
192
|
+
if (argv.length === 0) return -1;
|
|
193
|
+
const opened = [];
|
|
194
|
+
const stream = (target) => {
|
|
195
|
+
if (target.length === 0) return "inherit";
|
|
196
|
+
const fd = fs.openSync(target, WRITE_FLAGS, 0o644);
|
|
197
|
+
opened.push(fd);
|
|
198
|
+
return fd;
|
|
199
|
+
};
|
|
200
|
+
try {
|
|
201
|
+
const r = child_process.spawnSync(argv[0], argv.slice(1), {
|
|
202
|
+
stdio: ["inherit", stream(out), stream(err)],
|
|
203
|
+
});
|
|
204
|
+
if (r.error !== undefined) return -1;
|
|
205
|
+
if (r.signal !== null && r.signal !== undefined) return 128 + (os.constants.signals[r.signal] ?? 0);
|
|
206
|
+
return r.status === null ? -1 : r.status;
|
|
207
|
+
} catch {
|
|
208
|
+
// A path that cannot be opened is, natively, a file action the child could
|
|
209
|
+
// not perform, and `posix_spawnp` reports that through its return value:
|
|
210
|
+
// -1, with no child having run. Opening the second path is what can fail
|
|
211
|
+
// after the first file was already created, so the truncation a caller can
|
|
212
|
+
// observe happens on both sides.
|
|
213
|
+
return -1;
|
|
214
|
+
} finally {
|
|
215
|
+
for (const fd of opened) fs.closeSync(fd);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** `spawnSync(argv)`: the child inherits this process's streams, as it does natively. */
|
|
220
|
+
function spawnSync(argv) {
|
|
221
|
+
return spawnImpl(argv, "", "");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** `spawnSyncTo(argv, stdoutPath, stderrPath)`: the same run with a stream sent to a file. */
|
|
225
|
+
function spawnSyncTo(argv, out, err) {
|
|
226
|
+
return spawnImpl(argv, out, err);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* `kill(pid, sig)`: 0, or a negative errno, as `runtime-os.c`'s `nish_kill`
|
|
231
|
+
* answers. Only 0, 2, 9 and 15 are sent and a pid of 0 or below is refused,
|
|
232
|
+
* both -22, before Node is asked; Node's error then names its errno, which
|
|
233
|
+
* `os.constants.errno` turns back into the number (-3 for no such process).
|
|
234
|
+
*/
|
|
235
|
+
function kill(pid, sig) {
|
|
236
|
+
if (pid <= 0 || !(sig === 0 || sig === 2 || sig === 9 || sig === 15)) return -22;
|
|
237
|
+
try {
|
|
238
|
+
process.kill(pid, sig);
|
|
239
|
+
return 0;
|
|
240
|
+
} catch (e) {
|
|
241
|
+
return -(os.constants.errno[e.code] ?? 22);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* `availableParallelism()`: Node's own count, which reads the affinity mask as
|
|
247
|
+
* the native one does, lowered by a positive `NISH_NUM_THREADS` the way the
|
|
248
|
+
* native count is. The native count also reads a cgroup v2 CPU quota, which
|
|
249
|
+
* Node does not, so inside a container with a quota the two may differ; both
|
|
250
|
+
* are at least 1.
|
|
251
|
+
*/
|
|
252
|
+
function availableParallelism() {
|
|
253
|
+
const n = os.availableParallelism();
|
|
254
|
+
const want = /^[0-9]+$/.test(process.env.NISH_NUM_THREADS ?? "") ? Number(process.env.NISH_NUM_THREADS) : 0;
|
|
255
|
+
return want > 0 && want < n ? want : n;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** `totalMemory()`: `os.totalmem()`, as a BigInt because an `i64` is held as one here. */
|
|
259
|
+
function totalMemory() {
|
|
260
|
+
return BigInt(os.totalmem());
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* `monotonicNanos()`: `process.hrtime.bigint()`, a monotonic clock in
|
|
265
|
+
* nanoseconds (`CLOCK_MONOTONIC` on every platform that has it, which is the
|
|
266
|
+
* one `runtime-os.c` reads). The value is a BigInt because that is how an `i64`
|
|
267
|
+
* is held on this side.
|
|
268
|
+
*
|
|
269
|
+
* No rewrite can make a *reading* agree with a native run: both origins are
|
|
270
|
+
* arbitrary and neither is the other's. Only the difference between two reads
|
|
271
|
+
* means anything, so a differential program may compare two readings and must
|
|
272
|
+
* never print one.
|
|
273
|
+
*/
|
|
274
|
+
function monotonicNanos() {
|
|
275
|
+
return process.hrtime.bigint();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* `statMtimeSync(path)`: Node's `mtimeMs` for the path, or NaN when it cannot
|
|
280
|
+
* be stat'd, which is the native answer too. `mtimeMs` is the same arithmetic
|
|
281
|
+
* `runtime-host.c` does, so the two print the same digits, fraction and all.
|
|
282
|
+
*/
|
|
283
|
+
function statMtimeSync(path) {
|
|
284
|
+
const st = fs.statSync(path, { throwIfNoEntry: false });
|
|
285
|
+
if (st === undefined) {
|
|
286
|
+
return Number.NaN;
|
|
287
|
+
}
|
|
288
|
+
return st.mtimeMs;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The RT-9 owner checks (#386): `lstatOwnerModeSync(path)` is the owner's uid
|
|
293
|
+
* in the high 32 bits and `st_mode` in the low 32 of one `lstat`, so a
|
|
294
|
+
* symbolic link answers for itself, or -1 when the path does not resolve or
|
|
295
|
+
* holds a NUL; `geteuid()` is the effective uid; `isExecutableSync(path)` is
|
|
296
|
+
* `access(path, X_OK)`. Each answers what `runtime-host.c` answers, the two
|
|
297
|
+
* `i64`s as BigInts because that is how an `i64` is held on this side. Node's
|
|
298
|
+
* `fs` throws on a NUL in a path where the runtime names nothing, so the
|
|
299
|
+
* `catch` is what makes the two agree there too.
|
|
300
|
+
*/
|
|
301
|
+
function lstatOwnerModeSync(path) {
|
|
302
|
+
try {
|
|
303
|
+
const st = fs.lstatSync(path);
|
|
304
|
+
return BigInt.asIntN(64, (BigInt(st.uid >>> 0) << 32n) | BigInt(st.mode >>> 0));
|
|
305
|
+
} catch {
|
|
306
|
+
return -1n;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function geteuid() {
|
|
311
|
+
return BigInt(process.geteuid());
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function isExecutableSync(path) {
|
|
315
|
+
try {
|
|
316
|
+
fs.accessSync(path, fs.constants.X_OK);
|
|
317
|
+
return true;
|
|
318
|
+
} catch {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* `isTerminal(fd)` and `terminalSize()` (`nish:term`): whether a descriptor is
|
|
325
|
+
* a terminal, and the size Node already keeps for stdout's, or stderr's (a
|
|
326
|
+
* `tty.ReadStream` keeps none, so stdin has no size here),
|
|
327
|
+
* `[-25, 0]` when neither is one, as `runtime-term.c` answers.
|
|
328
|
+
*/
|
|
329
|
+
function isTerminal(fd) {
|
|
330
|
+
return tty.isatty(fd);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function terminalSize() {
|
|
334
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
335
|
+
if (stream.isTTY && stream.columns !== undefined) return [stream.columns, stream.rows];
|
|
336
|
+
}
|
|
337
|
+
return [-25, 0];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** The host-bound builtins by name, for the host's `call`. */
|
|
341
|
+
const CALLS = {
|
|
342
|
+
readFileSync,
|
|
343
|
+
readFileSyncOrNull,
|
|
344
|
+
readFileBytesSync,
|
|
345
|
+
writeFileSync,
|
|
346
|
+
appendFileSync,
|
|
347
|
+
mkdirSync,
|
|
348
|
+
isDirectorySync,
|
|
349
|
+
readdirSync,
|
|
350
|
+
realpathSync,
|
|
351
|
+
platform,
|
|
352
|
+
arch,
|
|
353
|
+
getenv,
|
|
354
|
+
spawnSync,
|
|
355
|
+
spawnSyncTo,
|
|
356
|
+
kill,
|
|
357
|
+
availableParallelism,
|
|
358
|
+
totalMemory,
|
|
359
|
+
monotonicNanos,
|
|
360
|
+
statMtimeSync,
|
|
361
|
+
lstatOwnerModeSync,
|
|
362
|
+
geteuid,
|
|
363
|
+
isExecutableSync,
|
|
364
|
+
isTerminal,
|
|
365
|
+
terminalSize,
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// A rewritten program prints most values with Node's own `console.log`, which
|
|
369
|
+
// queues a write to a pipe the reader has not drained, and `process.exit`
|
|
370
|
+
// drops what is queued: under load the tail of the output was lost with exit 0
|
|
371
|
+
// (`corpus/bit_fnv1a` under the emit-ts differential). Blocking writes make it
|
|
372
|
+
// as synchronous as `writeOut`.
|
|
373
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
374
|
+
stream._handle?.setBlocking?.(true);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
installHost({
|
|
378
|
+
writeOut: (s) => fs.writeSync(1, s),
|
|
379
|
+
writeErr: (s) => fs.writeSync(2, s),
|
|
380
|
+
exit: (code) => process.exit(code),
|
|
381
|
+
// Index 0 is the program on both sides: the executable natively, the entry
|
|
382
|
+
// script here, which is one index later than Node's own `argv`. The global
|
|
383
|
+
// is left as Node set it; the output reads `argv()` (`src/emit-ts-records.ts`).
|
|
384
|
+
argv: () => process.argv.slice(1),
|
|
385
|
+
call: (name, args) => CALLS[name](...args),
|
|
386
|
+
});
|