@amritk/nish-x86_64-linux 0.18.0 → 0.21.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.
Files changed (104) hide show
  1. package/INSTALL.md +58 -28
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +3 -2
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +99 -5
  22. package/runtime/nish.h +289 -19
  23. package/runtime/nish.mjs +39 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-aes.c +490 -0
  26. package/runtime/runtime-bytes.c +401 -0
  27. package/runtime/runtime-cross.c +366 -0
  28. package/runtime/runtime-host.c +8 -7
  29. package/runtime/runtime-net.c +93 -34
  30. package/runtime/runtime-os.c +214 -16
  31. package/runtime/runtime-parallel.c +590 -107
  32. package/runtime/runtime-simd.c +235 -11
  33. package/runtime/runtime-term.c +504 -0
  34. package/runtime/runtime-wasm.c +5 -14
  35. package/runtime/runtime.c +257 -17
  36. package/runtime/shim.mjs +6 -915
  37. package/runtime/sqlite/LICENSE.md +85 -0
  38. package/runtime/sqlite/README.md +45 -0
  39. package/runtime/sqlite/nish-sqlite.c +359 -0
  40. package/runtime/sqlite/sqlite3.c +271131 -0
  41. package/runtime/sqlite/sqlite3.h +14447 -0
  42. package/scripts/build.sh +437 -28
  43. package/std/LICENSE-fast-float +23 -0
  44. package/std/LICENSE-fdlibm +8 -0
  45. package/std/README.md +108 -58
  46. package/std/base64.ts +116 -0
  47. package/std/bytes.ts +419 -0
  48. package/std/cli.ts +328 -0
  49. package/std/collections.ts +6 -5
  50. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  51. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  52. package/std/crypto/aes.ts +238 -76
  53. package/std/crypto/base64url.ts +125 -0
  54. package/std/crypto/bcrypt.ts +760 -0
  55. package/std/crypto/chacha20poly1305.ts +43 -9
  56. package/std/crypto/ed25519.ts +840 -0
  57. package/std/crypto/hkdf.ts +304 -12
  58. package/std/crypto/hmac.ts +382 -54
  59. package/std/crypto/p256.ts +932 -61
  60. package/std/crypto/x25519.ts +55 -37
  61. package/std/crypto/x509-random.ts +71 -0
  62. package/std/crypto/x509.ts +20 -131
  63. package/std/json-tokenizer.ts +4237 -0
  64. package/std/json-typed.ts +1014 -0
  65. package/std/json-value.ts +1182 -0
  66. package/std/json.ts +1202 -34
  67. package/std/math.ts +608 -0
  68. package/std/net/buffer-pool.ts +151 -0
  69. package/std/net/hpack.ts +416 -112
  70. package/std/net/http-fields.ts +93 -0
  71. package/std/net/http1-server.ts +501 -62
  72. package/std/net/http1.ts +31 -6
  73. package/std/net/http2-tls.ts +112 -24
  74. package/std/net/http2.ts +333 -75
  75. package/std/net/http3-server.ts +261 -43
  76. package/std/net/http3.ts +187 -28
  77. package/std/net/postgres.ts +1875 -0
  78. package/std/net/prefork.ts +412 -0
  79. package/std/net/quic-listener.ts +108 -11
  80. package/std/net/quic-packet.ts +243 -82
  81. package/std/net/quic-recovery.ts +361 -30
  82. package/std/net/quic-stream.ts +424 -24
  83. package/std/net/quic.ts +519 -167
  84. package/std/net/router.ts +676 -0
  85. package/std/net/tls/codec.ts +426 -27
  86. package/std/net/tls/record-server.ts +478 -132
  87. package/std/net/tls/record.ts +133 -42
  88. package/std/net/tls/schedule.ts +464 -55
  89. package/std/net/tls/ticket.ts +610 -0
  90. package/std/net/tls-tcp.ts +196 -53
  91. package/std/net/tls.ts +1018 -260
  92. package/std/net/url.ts +571 -0
  93. package/std/random.ts +94 -0
  94. package/std/regex.ts +4274 -0
  95. package/std/sqlite.ts +391 -0
  96. package/std/term-io.ts +978 -0
  97. package/std/term-pixels.ts +874 -0
  98. package/std/term-probe.ts +459 -0
  99. package/std/term-rate.ts +993 -0
  100. package/std/term.ts +2453 -0
  101. package/std/text.ts +90 -7
  102. package/std/threads.ts +438 -14
  103. package/std/yaml-lines.ts +141 -0
  104. package/std/yaml.ts +4675 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@amritk/nish-x86_64-linux",
3
- "version": "0.18.0",
4
- "description": "The @amritk/nish native compiler for Linux on x86_64",
3
+ "version": "0.21.0",
4
+ "description": "The nish native compiler for Linux on x86_64",
5
5
  "license": "MIT",
6
6
  "os": [
7
7
  "linux"
@@ -17,6 +17,7 @@
17
17
  "runtime",
18
18
  "scripts",
19
19
  "std",
20
+ "docs",
20
21
  "LICENSE",
21
22
  "INSTALL.md"
22
23
  ],
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `nish/runtime/browser`: importing it installs the browser host on the core
3
+ * in `nish/runtime` (docs/guide/using-nish/to-typescript.md).
4
+ */
5
+
6
+ /**
7
+ * What the program's `exit(code)`, and every panic, throws: a page cannot end
8
+ * itself. Tell it apart with `instanceof`, not by its `name`.
9
+ */
10
+ export class NishExit extends Error {
11
+ /** The 8-bit status a native exit would leave: `code & 0xff`. */
12
+ readonly code: number;
13
+ constructor(code: number);
14
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The browser host (WP33 R3): importing this installs, on the core in
3
+ * `runtime/core.mjs`, the answers a web page can give. It names no `node:`
4
+ * module and reads only web globals (`console`, `performance`,
5
+ * `queueMicrotask`), so it loads in a page, a worker or any engine that has
6
+ * those.
7
+ *
8
+ * - Output goes to the console a line at a time: what `write` leaves without
9
+ * a newline waits for the next one, for `exit`, or for the program to yield.
10
+ * - `exit(code)` throws a `NishExit` carrying the code, since a page cannot
11
+ * end itself; the caller that started the program catches it, and does
12
+ * not call the module again (docs/security/runtime-browser.md, BROWSER-2).
13
+ * - `monotonicNanos` reads `performance.now()`, `getenv` answers `null`,
14
+ * `isTerminal` answers `false` and `terminalSize` `[-25, 0]`, and `argv()`
15
+ * is the program alone.
16
+ * - Every file-system, process and machine call panics, naming itself and
17
+ * the browser.
18
+ */
19
+ import { installHost, panic } from "./core/host.mjs";
20
+
21
+ /**
22
+ * What `exit(code)` throws: `code` is the 8-bit status a native exit would
23
+ * leave. Exported so a caller tells it apart with `instanceof`, which a thrown
24
+ * object that only carries the name does not pass.
25
+ */
26
+ export class NishExit extends Error {
27
+ constructor(code) {
28
+ super(`the program exited with ${code}`);
29
+ this.name = "NishExit";
30
+ this.code = code & 0xff;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * One console stream, buffered to the next newline. The `"%s"` keeps a `%d`
36
+ * or `%o` in the program's text from being read as a format directive.
37
+ */
38
+ function lineStream(print) {
39
+ let pending = "";
40
+ let scheduled = false;
41
+ const flush = () => {
42
+ scheduled = false;
43
+ if (pending !== "") print("%s", pending);
44
+ pending = "";
45
+ };
46
+ return {
47
+ write(s) {
48
+ // Only the text up to the last newline is split, so a run of writes
49
+ // with no newline costs their length once, not once per write.
50
+ const text = String(s);
51
+ const last = text.lastIndexOf("\n");
52
+ if (last < 0) {
53
+ pending += text;
54
+ } else {
55
+ const lines = (pending + text.slice(0, last)).split("\n");
56
+ pending = text.slice(last + 1);
57
+ for (const line of lines) print("%s", line);
58
+ }
59
+ if (pending !== "" && !scheduled) {
60
+ scheduled = true;
61
+ queueMicrotask(flush);
62
+ }
63
+ },
64
+ flush,
65
+ };
66
+ }
67
+
68
+ const out = lineStream((...a) => console.log(...a));
69
+ const err = lineStream((...a) => console.error(...a));
70
+
71
+ installHost({
72
+ writeOut: out.write,
73
+ writeErr: err.write,
74
+ exit(code) {
75
+ out.flush();
76
+ err.flush();
77
+ throw new NishExit(code);
78
+ },
79
+ argv: () => ["main"],
80
+ call(name) {
81
+ if (name === "getenv") return null;
82
+ if (name === "monotonicNanos") return BigInt(Math.round(performance.now() * 1e6));
83
+ // A page has no terminal, which `nish:term` answers as a pipe does.
84
+ if (name === "isTerminal") return false;
85
+ if (name === "terminalSize") return [-25, 0];
86
+ return panic(`${name} is not available in a browser`);
87
+ },
88
+ });
@@ -0,0 +1,18 @@
1
+ /** The array half of the core: bounds-checked access, and the arena stubs. */
2
+ import type { i32 } from "./numbers.js";
3
+ export declare function idx<T>(a: readonly T[], i: number | bigint): T;
4
+ export declare function pop<T>(a: T[]): T;
5
+ export declare function setIdx<T>(a: T[], i: number | bigint, v: T): T;
6
+ export declare function updIdx<T>(a: T[], i: number | bigint, f: (old: T) => T): T;
7
+ export declare function uncheckedGet<T extends number>(xs: readonly T[], i: i32): T;
8
+ export declare function uncheckedSet<T extends number>(xs: T[], i: i32, v: T): void;
9
+ export declare function copyIn<T>(v: T): T;
10
+ export declare function storeIdx<T>(a: T[], i: number | bigint, v: T): T;
11
+ export declare function arraySet<T>(dst: T[], src: readonly T[], offset?: number): void;
12
+ export declare function newArray<T>(n: number | bigint, zero: T): T[];
13
+ export declare function arenaUsed(): number;
14
+ export declare function arenaMark(): number;
15
+ export declare function arenaRelease(m?: unknown): void;
16
+ export declare function arenaReset(): void;
17
+ /** `using a = arena()`: an object whose `Symbol.dispose` does nothing. */
18
+ export declare function arena(): object;
@@ -0,0 +1,125 @@
1
+ import { panicIndex, panicSlice, toIndex } from "./checks.mjs";
2
+
3
+ // The native check compares the index as an unsigned 64-bit number, so a
4
+ // negative one fails, and prints, as that number: `-1` is 18446744073709551615.
5
+ // A NaN or infinite `number` has no such number and prints as it is.
6
+ function outOfRange(i, len) {
7
+ const n = toIndex(i);
8
+ if (typeof i !== "bigint" && !Number.isFinite(n)) panicIndex(n, len);
9
+ const k = typeof i === "bigint" ? i : BigInt(n);
10
+ panicIndex(k < 0n ? BigInt.asUintN(64, k) : k, len);
11
+ }
12
+
13
+ /** `a[i]` read with the WP4 bounds check (negative indices fail like the unsigned compare does). */
14
+ export function idx(a, i) {
15
+ const k = toIndex(i);
16
+ if (!(k >= 0 && k < a.length)) outOfRange(i, a.length);
17
+ return a[k];
18
+ }
19
+
20
+ /** `a.pop()`: the last element, or the bounds panic — Nish has no `undefined` to return. */
21
+ export function pop(a) {
22
+ if (a.length === 0) panicIndex(0, 0);
23
+ return a.pop();
24
+ }
25
+
26
+ /** `a[i] = v`: evaluates `a`, `i`, `v`, then checks and stores; yields `v`. */
27
+ export function setIdx(a, i, v) {
28
+ const k = toIndex(i);
29
+ if (!(k >= 0 && k < a.length)) outOfRange(i, a.length);
30
+ a[k] = v;
31
+ return v;
32
+ }
33
+
34
+ /**
35
+ * `a[i] op= v`: evaluates `a`, `i`, checks, loads, then `f(old)` computes the
36
+ * new element, and checks again before the store, as `f` can shrink `a`.
37
+ */
38
+ export function updIdx(a, i, f) {
39
+ const k = toIndex(i);
40
+ if (!(k >= 0 && k < a.length)) outOfRange(i, a.length);
41
+ const v = f(a[k]);
42
+ if (!(k < a.length)) outOfRange(i, a.length);
43
+ a[k] = v;
44
+ return v;
45
+ }
46
+
47
+ /** `nish:unsafe`'s `uncheckedGet(a, i)`: the element with no bounds check, as the native load is. */
48
+ export function uncheckedGet(a, i) {
49
+ return a[i];
50
+ }
51
+
52
+ /** `nish:unsafe`'s `uncheckedSet(a, i, v)`: the store with no bounds check. */
53
+ export function uncheckedSet(a, i, v) {
54
+ a[i] = v;
55
+ }
56
+
57
+ // A record an array holds inline is an object literal here, and nothing else
58
+ // is a plain object: a class instance has its class's prototype, and an array
59
+ // has Array's.
60
+ function isRecord(v) {
61
+ return v !== null && typeof v === "object" && Object.getPrototypeOf(v) === Object.prototype;
62
+ }
63
+
64
+ /** A value put into an array slot from a generic body: a copy when it is a record, as a slot holds it inline. */
65
+ export function copyIn(v) {
66
+ return isRecord(v) ? { ...v } : v;
67
+ }
68
+
69
+ /**
70
+ * `a[i] = v` from a generic body: as `setIdx`, but a record is written field
71
+ * by field into the record already in the slot, as an inline element is.
72
+ */
73
+ export function storeIdx(a, i, v) {
74
+ const k = toIndex(i);
75
+ if (!(k >= 0 && k < a.length)) outOfRange(i, a.length);
76
+ const old = a[k];
77
+ if (isRecord(old) && isRecord(v)) Object.assign(old, v);
78
+ else a[k] = v;
79
+ return v;
80
+ }
81
+
82
+ /**
83
+ * `dst.set(src, offset)` (WP34 N2): `TypedArray.prototype.set`'s copy on the
84
+ * plain array a `u8[]` is here. The source is copied first, so a self-copy or
85
+ * an overlapping one reads what was there before, as `memmove` does natively;
86
+ * a range past the end fails with the native panic and its words, where a
87
+ * typed array would throw a `RangeError` for the same offsets.
88
+ */
89
+ export function arraySet(dst, src, offset) {
90
+ // `ToIntegerOrInfinity`: NaN is 0, as `llvm.fptosi.sat` makes it natively.
91
+ // `Math.trunc` rather than `toIndex`, which converts a bigint: an `i64` or
92
+ // `u64` offset is a bigint here, and it throws the `TypeError` the typed
93
+ // array and `Array.prototype.fill` throw for one, instead of being rounded
94
+ // to the nearest double past 2^53 (docs/RUN_UNDER_NODE.md).
95
+ const at = offset === undefined ? 0 : Math.trunc(offset) || 0;
96
+ const end = at + src.length;
97
+ if (!(at >= 0 && end <= dst.length)) panicSlice(at, end, dst.length);
98
+ // Two plain arrays overlap only when they are one array, which is the one
99
+ // case that must read the source before writing it.
100
+ const from = src === dst ? src.slice() : src;
101
+ for (let i = 0; i < from.length; i++) dst[at + i] = from[i];
102
+ }
103
+
104
+ /** `new Array<T>(n)`: `n` zero-filled elements (`0`, `0n`, or `false`). */
105
+ export function newArray(n, zero) {
106
+ return new Array(toIndex(n)).fill(zero);
107
+ }
108
+
109
+ // Arena introspection has no JS counterpart: the stubs keep programs that only
110
+ // compare `Arena.used()` before/after (a "stayed flat" check) in agreement, while
111
+ // programs that print raw byte counts are listed as known differences.
112
+ //
113
+ // `nish --threads` makes the native arena thread-local (WP20 T0) and nothing
114
+ // here moves with it: a rewritten program runs on the one thread Node gives it,
115
+ // so "the arena of the calling thread" and "the arena" are the same object, and
116
+ // these stubs answer for both. If T1 ever lands a spawn the rewrite can reach,
117
+ // that is when this file grows a second arena to keep count of.
118
+ export function arenaUsed() { return 0; }
119
+ export function arenaMark() { return 0; }
120
+ export function arenaRelease() {}
121
+ export function arenaReset() {}
122
+ /** `using a = arena()`: a disposable whose disposal does nothing, because there is no arena to release. */
123
+ export function arena() {
124
+ return { [Symbol.dispose]() {} };
125
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The checks the strings and arrays share: an index as the compiler converts
3
+ * it, and the two bounds panics with their native words. Internal to the core:
4
+ * `runtime/core.mjs` does not re-export them.
5
+ */
6
+ import { panic } from "./host.mjs";
7
+
8
+ /** Index conversion as in the compiler: `sext` from i32, `fptosi` (truncation) from f64. */
9
+ export function toIndex(i) {
10
+ return typeof i === "bigint" ? Number(i) : Math.trunc(i);
11
+ }
12
+
13
+ export function panicIndex(i, len) {
14
+ panic(`index out of range: ${i} >= ${len}`);
15
+ }
16
+
17
+ /** `nish_panic_slice`: the failed range check of `s.slice(start, end)`; the offsets print signed. */
18
+ export function panicSlice(start, end, len) {
19
+ panic(`slice out of range: [${start}, ${end}) of length ${len}`);
20
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The host the core runs on, and every builtin that asks it. Import
3
+ * `nish/runtime/node` or `nish/runtime/browser` to install one; until then each
4
+ * of these throws a `NishNoHost`.
5
+ */
6
+ import type { f64, i32, i64, u8 } from "./numbers.js";
7
+
8
+ /** What an adapter installs: `exit` never returns, and `call` answers the builtin `name`. */
9
+ export interface Host {
10
+ writeOut(s: string): void;
11
+ writeErr(s: string): void;
12
+ exit(code: number): never;
13
+ argv(): string[];
14
+ call(name: string, args: unknown[]): unknown;
15
+ }
16
+
17
+ /** What every member of the default host throws: no adapter has been imported. */
18
+ export declare class NishNoHost extends Error {
19
+ constructor(name: string);
20
+ readonly requested: string;
21
+ }
22
+
23
+ export declare function installHost(next: Host): void;
24
+ export declare function log(x: unknown): void;
25
+ export declare function error(x: unknown): void;
26
+ export declare function write(s: string): void;
27
+ export declare function writeError(s: string): void;
28
+ export declare function panic(message: string): never;
29
+ export declare function exit(code: i32): never;
30
+ export declare function argv(): string[];
31
+ export declare function readFileSync(path: string): string;
32
+ export declare function readFileSyncOrNull(path: string): string | null;
33
+ export declare function readFileBytesSync(path: string): u8[] | null;
34
+ export declare function writeFileSync(path: string, data: string): void;
35
+ export declare function appendFileSync(path: string, data: string): void;
36
+ export declare function mkdirSync(path: string): boolean;
37
+ export declare function isDirectorySync(path: string): boolean;
38
+ export declare function readdirSync(path: string): string[] | null;
39
+ export declare function realpathSync(path: string): string | null;
40
+ export declare function platform(): string;
41
+ export declare function arch(): string;
42
+ export declare function getenv(name: string): string | null;
43
+ export declare function spawnSync(argv: string[]): number;
44
+ export declare function spawnSyncTo(argv: string[], stdoutPath: string, stderrPath: string): number;
45
+ export declare function kill(pid: i32, sig: i32): i32;
46
+ export declare function availableParallelism(): i32;
47
+ export declare function totalMemory(): i64;
48
+ export declare function monotonicNanos(): i64;
49
+ export declare function statMtimeSync(path: string): f64;
50
+ export declare function lstatOwnerModeSync(path: string): i64;
51
+ export declare function geteuid(): i64;
52
+ export declare function isExecutableSync(path: string): boolean;
53
+ export declare function isTerminal(fd: i32): boolean;
54
+ export declare function terminalSize(): i32[];
55
+ /** `crypto.getRandomValues(bytes)` for a `u8[]`; a typed array goes straight through. */
56
+ export declare function getRandomValues<T extends u8[] | Uint8Array>(bytes: T): T;
57
+ /** These four have no synchronous reading: each throws, naming itself, and is typed as `runtime/nish.d.ts` types it. */
58
+ export declare function spawn(argv: string[]): i32;
59
+ export declare function waitPid(pid: i32, flags: i32): i32[];
60
+ export declare function signalFd(): i32;
61
+ export declare function readSignal(fd: i32): i32;
62
+ /** What each `nish:net` function is under the core: a throw naming `name`. */
63
+ export declare function noNetReading(name: string): never;
64
+ /** What `readStdin`, `waitStdin`, `setRawMode` and `setTerminalRestore` are under the core: a throw naming `name`. */
65
+ export declare function noTermReading(name: string): never;
66
+ /** `nish:term`'s four that have no reading under Node: each throws, naming itself. */
67
+ export declare function readStdin(buf: u8[], off: i32, len: i32): i32;
68
+ export declare function waitStdin(timeoutMs: i32): boolean;
69
+ export declare function setRawMode(on: boolean): i32;
70
+ export declare function setTerminalRestore(bytes: string): i32;
@@ -0,0 +1,311 @@
1
+ /**
2
+ * The host the core runs on (WP33 R3). Everything a program asks of the world
3
+ * outside it — its output, its exit, its arguments, its files, its environment,
4
+ * its children and its clocks — goes through one installed host object:
5
+ *
6
+ * {
7
+ * writeOut(s), writeErr(s): write `s` to stdout or stderr, synchronously;
8
+ * exit(code): end the process with `code` and never return;
9
+ * argv(): the program then its arguments, as `process.argv` is under Nish;
10
+ * call(name, args): the host-bound builtin `name`, applied to `args`;
11
+ * }
12
+ *
13
+ * `runtime/node.mjs` installs Node's on import. Until a host is installed every
14
+ * member throws a `NishNoHost` naming what was asked for, so a core loaded on
15
+ * its own fails loudly rather than writing nowhere.
16
+ *
17
+ * The functions that have no synchronous reading under Node (`spawn`,
18
+ * `waitPid`, `signalFd`, `readSignal`, the `nish:net` set) throw here without
19
+ * asking any host.
20
+ */
21
+
22
+ /** What every member of the default host throws: no adapter has been imported. */
23
+ export class NishNoHost extends Error {
24
+ constructor(name) {
25
+ super(`\`${name}\` needs a host, and none is installed: import runtime/node.mjs (or another adapter) first`);
26
+ this.name = "NishNoHost";
27
+ this.requested = name;
28
+ }
29
+ }
30
+
31
+ const NO_HOST = {
32
+ writeOut() {
33
+ throw new NishNoHost("writeOut");
34
+ },
35
+ writeErr() {
36
+ throw new NishNoHost("writeErr");
37
+ },
38
+ exit() {
39
+ throw new NishNoHost("exit");
40
+ },
41
+ argv() {
42
+ throw new NishNoHost("argv");
43
+ },
44
+ call(name) {
45
+ throw new NishNoHost(name);
46
+ },
47
+ };
48
+
49
+ let host = NO_HOST;
50
+
51
+ /** Make `next` the host every later call goes through. */
52
+ export function installHost(next) {
53
+ host = next;
54
+ }
55
+
56
+ /** `console.log(x)`: `String(x)` (no `n` suffix for i64) plus a newline, written synchronously. */
57
+ export function log(x) {
58
+ host.writeOut(`${String(x)}\n`);
59
+ }
60
+
61
+ /** `console.error(x)`: the same, on stderr. */
62
+ export function error(x) {
63
+ host.writeErr(`${String(x)}\n`);
64
+ }
65
+
66
+ /** `write(s)` / `writeError(s)`: the bytes as they are, no trailing newline. */
67
+ export function write(s) {
68
+ host.writeOut(s);
69
+ }
70
+
71
+ export function writeError(s) {
72
+ host.writeErr(s);
73
+ }
74
+
75
+ /** `panic(message)`: the message on stderr, then exit 1. */
76
+ export function panic(message) {
77
+ host.writeErr(`${message}\n`);
78
+ host.exit(1);
79
+ }
80
+
81
+ /**
82
+ * `process.exit(code)`: libc `exit` truncates to 8 bits exactly like Node does.
83
+ * The code is converted as an index is (`sext` from i32, truncation from f64).
84
+ */
85
+ export function exit(code) {
86
+ host.exit(typeof code === "bigint" ? Number(code) : Math.trunc(code));
87
+ }
88
+
89
+ /** `process.argv`: index 0 is the program (the script here, the executable natively), then the arguments. */
90
+ export function argv() {
91
+ return host.argv();
92
+ }
93
+
94
+ // ---- The host-bound builtins ----------------------------------------------------
95
+ //
96
+ // Each is the installed host's answer, under the same name and with the same
97
+ // arguments; what each answers is written beside its Node body in
98
+ // `runtime/node.mjs`.
99
+
100
+ export function readFileSync(path) {
101
+ return host.call("readFileSync", [path]);
102
+ }
103
+
104
+ export function readFileSyncOrNull(path) {
105
+ return host.call("readFileSyncOrNull", [path]);
106
+ }
107
+
108
+ export function readFileBytesSync(path) {
109
+ return host.call("readFileBytesSync", [path]);
110
+ }
111
+
112
+ export function writeFileSync(path, data) {
113
+ return host.call("writeFileSync", [path, data]);
114
+ }
115
+
116
+ export function appendFileSync(path, data) {
117
+ return host.call("appendFileSync", [path, data]);
118
+ }
119
+
120
+ export function mkdirSync(path) {
121
+ return host.call("mkdirSync", [path]);
122
+ }
123
+
124
+ export function isDirectorySync(path) {
125
+ return host.call("isDirectorySync", [path]);
126
+ }
127
+
128
+ export function readdirSync(path) {
129
+ return host.call("readdirSync", [path]);
130
+ }
131
+
132
+ export function realpathSync(path) {
133
+ return host.call("realpathSync", [path]);
134
+ }
135
+
136
+ export function platform() {
137
+ return host.call("platform", []);
138
+ }
139
+
140
+ export function arch() {
141
+ return host.call("arch", []);
142
+ }
143
+
144
+ export function getenv(name) {
145
+ return host.call("getenv", [name]);
146
+ }
147
+
148
+ export function spawnSync(argv) {
149
+ return host.call("spawnSync", [argv]);
150
+ }
151
+
152
+ export function spawnSyncTo(argv, out, err) {
153
+ return host.call("spawnSyncTo", [argv, out, err]);
154
+ }
155
+
156
+ export function kill(pid, sig) {
157
+ return host.call("kill", [pid, sig]);
158
+ }
159
+
160
+ export function availableParallelism() {
161
+ return host.call("availableParallelism", []);
162
+ }
163
+
164
+ export function totalMemory() {
165
+ return host.call("totalMemory", []);
166
+ }
167
+
168
+ export function monotonicNanos() {
169
+ return host.call("monotonicNanos", []);
170
+ }
171
+
172
+ export function statMtimeSync(path) {
173
+ return host.call("statMtimeSync", [path]);
174
+ }
175
+
176
+ export function lstatOwnerModeSync(path) {
177
+ return host.call("lstatOwnerModeSync", [path]);
178
+ }
179
+
180
+ export function geteuid() {
181
+ return host.call("geteuid", []);
182
+ }
183
+
184
+ export function isExecutableSync(path) {
185
+ return host.call("isExecutableSync", [path]);
186
+ }
187
+
188
+ export function isTerminal(fd) {
189
+ return host.call("isTerminal", [fd]);
190
+ }
191
+
192
+ export function terminalSize() {
193
+ return host.call("terminalSize", []);
194
+ }
195
+
196
+ // ---- Randomness ----------------------------------------------------------------
197
+
198
+ /**
199
+ * The host's own fill, taken from the web `crypto` global before
200
+ * `runtime/nish.mjs` puts the one below in its place on the same object.
201
+ */
202
+ const webRandom = globalThis.crypto.getRandomValues.bind(globalThis.crypto);
203
+
204
+ /**
205
+ * `crypto.getRandomValues(bytes)` for the plain array a `u8[]` is here. The
206
+ * web's own takes only a typed array, so the bytes are drawn into one and copied
207
+ * across. More than 65,536 fails with the native panic and its words, where
208
+ * the web's would throw a `QuotaExceededError`: the exit status is 1 either
209
+ * way. A typed array goes straight through.
210
+ */
211
+ export function getRandomValues(bytes) {
212
+ if (!Array.isArray(bytes)) {
213
+ return webRandom(bytes);
214
+ }
215
+ if (bytes.length > 65536) {
216
+ panic(`crypto.getRandomValues: ${bytes.length} bytes asked for, and one call fills at most 65536`);
217
+ }
218
+ const drawn = webRandom(new Uint8Array(bytes.length));
219
+ for (let i = 0; i < drawn.length; i++) {
220
+ bytes[i] = drawn[i];
221
+ }
222
+ return bytes;
223
+ }
224
+
225
+ // ---- No synchronous reading ------------------------------------------------------
226
+
227
+ /**
228
+ * `spawn(argv)` and `waitPid(pid, flags)` (WP34 N12) have no faithful reading
229
+ * under Node, for `signalFd`'s reason: Node reaps its children itself, on the
230
+ * event loop, and hands their status to an `exit` listener, so no synchronous
231
+ * function here can wait for one, and a child Node started is never there for
232
+ * a wait-less poll to find. Each throws, naming itself.
233
+ */
234
+ function noProcessReading(name) {
235
+ throw new Error(
236
+ `\`${name}\` has no synchronous reading under Node: a child is reaped by the event loop, which a synchronous wait never returns to (docs/wp33-round-trip.md)`
237
+ );
238
+ }
239
+
240
+ export function spawn() {
241
+ return noProcessReading("spawn");
242
+ }
243
+
244
+ export function waitPid() {
245
+ return noProcessReading("waitPid");
246
+ }
247
+
248
+ /**
249
+ * `signalFd()` and `readSignal(fd)` have no faithful reading under Node, and
250
+ * these say so rather than answer something else. Node delivers a signal to
251
+ * its event loop (`process.on("SIGTERM")`), and a blocking read keeps the loop
252
+ * from ever running, so no synchronous function here can learn that one
253
+ * arrived. docs/wp33-round-trip.md §3.5 has the row and the translation.
254
+ */
255
+ export function signalFd() {
256
+ throw new Error(
257
+ "signalFd has no synchronous reading under Node: a signal reaches the event loop, which a blocking readSignal never returns to (docs/wp33-round-trip.md)"
258
+ );
259
+ }
260
+
261
+ export function readSignal() {
262
+ return signalFd();
263
+ }
264
+
265
+ /**
266
+ * The `nish:net` functions (WP34 N5) have no faithful reading under Node
267
+ * either, for `signalFd`'s reason: Node's sockets are asynchronous only, and a
268
+ * socket becomes readable, writable or connected only to the event loop, which
269
+ * a program that owns its loop and spins or blocks in it never returns to. So
270
+ * each throws, naming itself, rather than answer -11 forever; `nish.mjs`
271
+ * installs one per name.
272
+ * docs/wp33-round-trip.md is the note that asks for one stated answer.
273
+ */
274
+ export function noNetReading(name) {
275
+ throw new Error(
276
+ `\`${name}\` has no synchronous reading under Node: a socket is ready only to the event loop, which a program that owns its loop never returns to (docs/wp33-round-trip.md)`
277
+ );
278
+ }
279
+
280
+ /**
281
+ * `readStdin`, `waitStdin` and `setRawMode` (`nish:term`) throw too, naming
282
+ * themselves, for `noNetReading`'s reason: Node reads stdin only to its event
283
+ * loop, and its own raw mode turns Ctrl-C into a byte, which is not the native
284
+ * answer. `setTerminalRestore` throws as well: its bytes go out from the
285
+ * native runtime's exit and signal paths, which Node does not run. The core
286
+ * exports one per name, for `--emit-ts` output to import, and
287
+ * `nish.mjs` installs one per name as a global.
288
+ */
289
+ export function noTermReading(name) {
290
+ throw new Error(
291
+ `\`${name}\` has no synchronous reading under Node: stdin is read only by the event loop, which a program that owns its loop never returns to (docs/RUN_UNDER_NODE.md)`
292
+ );
293
+ }
294
+
295
+ export function readStdin() {
296
+ return noTermReading("readStdin");
297
+ }
298
+
299
+ export function waitStdin() {
300
+ return noTermReading("waitStdin");
301
+ }
302
+
303
+ export function setRawMode() {
304
+ return noTermReading("setRawMode");
305
+ }
306
+
307
+ export function setTerminalRestore() {
308
+ throw new Error(
309
+ "`setTerminalRestore` has no reading under Node: its bytes go out from the native runtime's exit, panic and signal paths, which Node does not run (docs/RUN_UNDER_NODE.md)"
310
+ );
311
+ }