@amritk/nish-aarch64-darwin 0.25.0 → 0.27.2
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/bin/nish +0 -0
- package/docs/AI.md +30 -4
- package/package.json +1 -1
- package/runtime/core/host.d.ts +4 -0
- package/runtime/core/host.mjs +24 -0
- package/runtime/nish.d.ts +53 -5
- package/runtime/nish.h +43 -4
- package/runtime/nish.mjs +4 -0
- package/runtime/node.mjs +79 -0
- package/runtime/runtime-os.c +257 -22
- package/runtime/runtime-simd.c +63 -2
- package/runtime/runtime-wasm.c +18 -0
- package/scripts/build.sh +16 -1
- package/std/README.md +3 -2
- package/std/crypto/sha256.ts +335 -188
- package/std/net/http-client.ts +1645 -0
- package/std/net/postgres.ts +425 -29
package/bin/nish
CHANGED
|
Binary file
|
package/docs/AI.md
CHANGED
|
@@ -2137,11 +2137,18 @@ a `\u` escape `nish/json` decodes) is one unit per byte, and counts its bytes.
|
|
|
2137
2137
|
it is; `i32`: the length, or a negative errno), `renameSync(from, to)` (`0` or
|
|
2138
2138
|
a negative errno; atomic on one filesystem, `-18` across two, so write the
|
|
2139
2139
|
temporary beside the target), `unlinkSync(path)` (`0` or a negative errno; a
|
|
2140
|
-
directory is refused), `
|
|
2140
|
+
directory is refused), `writeFileSyncMode(path, data, mode)` (the file left
|
|
2141
|
+
at exactly `mode` (`0..0o777`), set before the first byte, written in place; a symbolic link refused; the
|
|
2142
|
+
length or a negative errno), `createFileExclusiveSync(path, data, mode)` (the
|
|
2143
|
+
same, but `-17` when the name exists: a lock file), `mkdirSyncMode(path, mode)`
|
|
2144
|
+
(`0`, `-17` when it exists, the umask applies), `writeBytes(bytes, off, len)` (a `u8[]` range to stdout
|
|
2141
2145
|
as it is, the length or a negative errno; the range panics like
|
|
2142
2146
|
`netWrite`'s), `mkdirSync(path)` (one level, `boolean`),
|
|
2143
2147
|
`isDirectorySync(path)`, `readdirSync(path)` (`string[] | null`, sorted by
|
|
2144
2148
|
bytes, no `.`/`..`), `spawnSync(argv)`, `spawnSyncTo(argv, outPath, errPath)`,
|
|
2149
|
+
`spawnPiped(argv, stdin, stdout, stderr)` (stdin from a `u8[]`, both outputs
|
|
2150
|
+
appended to `u8[]`s, the status; `-2` past 16 MiB; the way to hand a child a
|
|
2151
|
+
secret, never argv or the environment),
|
|
2145
2152
|
`getenv(name)` (`string | null` — unset and empty are different answers),
|
|
2146
2153
|
`realpathSync(path)` (`string | null`; symbolic links resolved, absolute, and
|
|
2147
2154
|
`null` when it does not resolve),
|
|
@@ -2174,8 +2181,9 @@ also watches the signal fd. None of `spawn`, `kill`, `waitPid` may be called in
|
|
|
2174
2181
|
a task or a parallel body; the two host facts may. `availableParallelism()` (`i32`, at least 1, no `--threads` needed) and
|
|
2175
2182
|
`totalMemory()` (`i64` bytes, or `-1`) size a program to the machine. None of
|
|
2176
2183
|
the five exists on a wasm target, and `spawn` and `waitPid` throw under Node.
|
|
2177
|
-
`setNice(n)` sets the nice value
|
|
2178
|
-
|
|
2184
|
+
`setNice(n)` sets the nice value, and it is lower-only even as root: `n` takes
|
|
2185
|
+
0..19 (`-22` outside it) and a value below the current one is `-13`, with no
|
|
2186
|
+
system call either way. `setIdlePriority()` moves the program to Linux's `SCHED_IDLE`
|
|
2179
2187
|
(`-95` on Darwin); each answers 0 or a negative errno. Call them at the top of
|
|
2180
2188
|
`main`, never in a task. An unprivileged process can never leave `SCHED_IDLE`
|
|
2181
2189
|
and an idle one can starve on a saturated machine, so keep a normal-priority
|
|
@@ -2194,6 +2202,10 @@ wait for it to be writable, then `connectResult(fd)` is 0 or the failure. Buffer
|
|
|
2194
2202
|
are `u8[]` only; an address is 18 bytes of one (IPv4 as `::ffff:a.b.c.d`, then
|
|
2195
2203
|
the port big-endian); hosts are numeric literals; to reach a name, resolve it first with
|
|
2196
2204
|
`nish/net/dns` (`DnsClient` on your loop, or the blocking `resolveHost`).
|
|
2205
|
+
To fetch a URL from a CLI, skip all of this: `nish/net/http-client`'s
|
|
2206
|
+
`httpFetch(new HttpRequest("GET", url), new HttpOptions())` blocks until the
|
|
2207
|
+
response's head is in and answers it with `body.readAll(limit)`, or null on a
|
|
2208
|
+
failure (an `HttpClient` keeps the connection and says why in `error`).
|
|
2197
2209
|
`netRead` / `netWrite` panic on a range outside the buffer.
|
|
2198
2210
|
`tcpListenFlags(host, port, backlog, flags)` is `tcpListen` with flag 1
|
|
2199
2211
|
`SO_REUSEPORT`, so several listeners, in one process or many, share a port (Linux
|
|
@@ -2625,6 +2637,20 @@ the array open: end it and send the rest in the next statement. Every other
|
|
|
2625
2637
|
guide's [PostgreSQL page](guide/using-nish/postgres.md#array-parameters) its
|
|
2626
2638
|
walk-through.)
|
|
2627
2639
|
|
|
2640
|
+
**Read PostgreSQL ids, counts and times without parsing them** — there is no
|
|
2641
|
+
driver option for binary results: in `nish/net/postgres`, open the bind with
|
|
2642
|
+
`bindStartBinary(name)` instead of `bindStart(name)`, and every result column
|
|
2643
|
+
of that execution comes in binary. Read an `int2`/`int4`/`int8` with
|
|
2644
|
+
`binaryInt(c)`, a `bool` with `binaryBool(c)`, a `float8` with
|
|
2645
|
+
`binaryFloat(c)`, a `timestamptz` with `conn.binary.timestamp(conn.inputBuffer(),
|
|
2646
|
+
conn.colStart(c), conn.colLength(c), out, at)` (or its microseconds since 2000
|
|
2647
|
+
with `binaryInt(c)`, written out by `conn.binary.iso(micros, out, at)`), and a
|
|
2648
|
+
`text` column with `columnText(c)` as before. Cast anything else (`numeric`,
|
|
2649
|
+
`text[]`) to `text` in the SQL. (No block, as for `nish/text`;
|
|
2650
|
+
`tests/postgres/live`'s `binary` scenario runs it against a server, and the
|
|
2651
|
+
user guide's [PostgreSQL page](guide/using-nish/postgres.md#binary-results)
|
|
2652
|
+
its walk-through.)
|
|
2653
|
+
|
|
2628
2654
|
**Batch a server's keyed reads into one PostgreSQL query** — there is no
|
|
2629
2655
|
DataLoader and no `setTimeout` to collect a tick's keys: in `nish/net/postgres`,
|
|
2630
2656
|
a `PgBatcher` built from a `PgBatchConfig` (`name`, `select` reading the key as
|
|
@@ -2633,7 +2659,7 @@ a `PgBatcher` built from a `PgBatchConfig` (`name`, `select` reading the key as
|
|
|
2633
2659
|
queue is full, or for a text key too long for `maxBytes`, with no room left in
|
|
2634
2660
|
`queueBytes`, with a NUL or not UTF-8 — and `take(conn)`
|
|
2635
2661
|
binds the waiting ones, each key once, as one execution of its `sql` over
|
|
2636
|
-
`unnest($1) with ordinality`, answering a run handle or -1. Hand that
|
|
2662
|
+
`unnest($1) with ordinality`, answering a run handle or -1. Set `binary` in the `PgBatchConfig` and each batch comes in binary: `take` opens its bind with `bindStartBinary`, and `row` reads the routing ordinal with `binaryInt` (`binary.failed` is saved and restored, so the caller's own decoding is untouched). Hand that
|
|
2637
2663
|
execution's `PG_ROW` to `row(run, conn)`, `PG_COMPLETE` to `end(run)`, an error
|
|
2638
2664
|
or `PG_LOST` to `fail(run)` (on a spent handle these do nothing and count in
|
|
2639
2665
|
`stray`), and after each call `next(run)` until
|
package/package.json
CHANGED
package/runtime/core/host.d.ts
CHANGED
|
@@ -47,6 +47,9 @@ export declare function appendFileSync(path: string, data: string): void;
|
|
|
47
47
|
export declare function writeFileBytesSync(path: string, bytes: readonly u8[]): i32;
|
|
48
48
|
export declare function renameSync(from: string, to: string): i32;
|
|
49
49
|
export declare function unlinkSync(path: string): i32;
|
|
50
|
+
export declare function writeFileSyncMode(path: string, data: string, mode: i32): i32;
|
|
51
|
+
export declare function createFileExclusiveSync(path: string, data: string, mode: i32): i32;
|
|
52
|
+
export declare function mkdirSyncMode(path: string, mode: i32): i32;
|
|
50
53
|
export declare function mkdirSync(path: string): boolean;
|
|
51
54
|
export declare function isDirectorySync(path: string): boolean;
|
|
52
55
|
export declare function readdirSync(path: string): string[] | null;
|
|
@@ -56,6 +59,7 @@ export declare function arch(): string;
|
|
|
56
59
|
export declare function getenv(name: string): string | null;
|
|
57
60
|
export declare function spawnSync(argv: string[]): number;
|
|
58
61
|
export declare function spawnSyncTo(argv: string[], stdoutPath: string, stderrPath: string): number;
|
|
62
|
+
export declare function spawnPiped(argv: string[], stdin: readonly u8[], stdout: u8[], stderr: u8[]): i32;
|
|
59
63
|
export declare function kill(pid: i32, sig: i32): i32;
|
|
60
64
|
export declare function availableParallelism(): i32;
|
|
61
65
|
export declare function totalMemory(): i64;
|
package/runtime/core/host.mjs
CHANGED
|
@@ -157,6 +157,18 @@ export function unlinkSync(path) {
|
|
|
157
157
|
return host.call("unlinkSync", [path]);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
export function writeFileSyncMode(path, data, mode) {
|
|
161
|
+
return host.call("writeFileSyncMode", [path, data, mode]);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function createFileExclusiveSync(path, data, mode) {
|
|
165
|
+
return host.call("createFileExclusiveSync", [path, data, mode]);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function mkdirSyncMode(path, mode) {
|
|
169
|
+
return host.call("mkdirSyncMode", [path, mode]);
|
|
170
|
+
}
|
|
171
|
+
|
|
160
172
|
export function mkdirSync(path) {
|
|
161
173
|
return host.call("mkdirSync", [path]);
|
|
162
174
|
}
|
|
@@ -193,6 +205,18 @@ export function spawnSyncTo(argv, out, err) {
|
|
|
193
205
|
return host.call("spawnSyncTo", [argv, out, err]);
|
|
194
206
|
}
|
|
195
207
|
|
|
208
|
+
/**
|
|
209
|
+
* `spawnPiped(argv, stdin, stdout, stderr)`: the host runs the child and hands
|
|
210
|
+
* back its status and both streams' bytes, and they are appended here, as the
|
|
211
|
+
* runtime appends them to the caller's arrays.
|
|
212
|
+
*/
|
|
213
|
+
export function spawnPiped(argv, input, out, err) {
|
|
214
|
+
const [status, outBytes, errBytes] = host.call("spawnPiped", [argv, input]);
|
|
215
|
+
for (const b of outBytes) out.push(b);
|
|
216
|
+
for (const b of errBytes) err.push(b);
|
|
217
|
+
return status;
|
|
218
|
+
}
|
|
219
|
+
|
|
196
220
|
export function kill(pid, sig) {
|
|
197
221
|
return host.call("kill", [pid, sig]);
|
|
198
222
|
}
|
package/runtime/nish.d.ts
CHANGED
|
@@ -276,6 +276,33 @@ declare function writeFileBytesSync(path: string, bytes: readonly u8[]): i32;
|
|
|
276
276
|
declare function renameSync(from: string, to: string): i32;
|
|
277
277
|
/** The name `path` removed: 0, or a negative errno. A directory is refused, not removed. */
|
|
278
278
|
declare function unlinkSync(path: string): i32;
|
|
279
|
+
/**
|
|
280
|
+
* `writeFileSync` with an explicit `mode`: the file is exactly `mode`
|
|
281
|
+
* (`0o600` for a secret) before a byte of `data` is in it, whatever the umask
|
|
282
|
+
* or a file already there had, and a symbolic link at the path is refused
|
|
283
|
+
* (-40). The byte count, or a negative errno: -22 for a mode outside
|
|
284
|
+
* 0..0o777, the setuid, setgid and sticky bits included. The file is
|
|
285
|
+
* truncated and written in place, so a crash or a failed write leaves it
|
|
286
|
+
* empty or partial, and a file this process may write but does not own is
|
|
287
|
+
* emptied before the mode change answers -1 (`EPERM`). For a file that must
|
|
288
|
+
* be the old one or the new one, write a temporary name with this and move it
|
|
289
|
+
* with `renameSync`. Never an exit.
|
|
290
|
+
*/
|
|
291
|
+
declare function writeFileSyncMode(path: string, data: string, mode: i32): i32;
|
|
292
|
+
/**
|
|
293
|
+
* `data` in a new file of exactly `mode`, created only if nothing is at `path`:
|
|
294
|
+
* -17 when something is, a dangling symbolic link included. Of several
|
|
295
|
+
* processes racing for one path exactly one succeeds, so it takes a lock. The
|
|
296
|
+
* byte count, or a negative errno; a write that fails after the create
|
|
297
|
+
* removes the file, so the lock is not left taken. Never an exit.
|
|
298
|
+
*/
|
|
299
|
+
declare function createFileExclusiveSync(path: string, data: string, mode: i32): i32;
|
|
300
|
+
/**
|
|
301
|
+
* One directory made with `mode` less the umask (`0o700` is exact under any
|
|
302
|
+
* umask that leaves the owner's bits): 0, or a negative errno. A path already
|
|
303
|
+
* there is -17 and its mode is not changed; -22 for a mode outside 0..0o777.
|
|
304
|
+
*/
|
|
305
|
+
declare function mkdirSyncMode(path: string, mode: i32): i32;
|
|
279
306
|
/** One directory, not recursive; whether a directory is there afterwards. */
|
|
280
307
|
declare function mkdirSync(path: string): boolean;
|
|
281
308
|
/** Whether a directory is at `path` right now. One `stat`, and never an exit. */
|
|
@@ -299,6 +326,13 @@ declare function spawnSync(argv: string[]): number;
|
|
|
299
326
|
* either path is refused, as `writeFileSync` refuses one, and the answer is -1.
|
|
300
327
|
*/
|
|
301
328
|
declare function spawnSyncTo(argv: string[], stdoutPath: string, stderrPath: string): number;
|
|
329
|
+
/**
|
|
330
|
+
* The same run with all three streams on pipes: `stdin` is written to the
|
|
331
|
+
* child and closed, and what it prints is appended to `stdout` and `stderr`.
|
|
332
|
+
* The status as `spawnSync` answers it, or -2 when a stream passed 16 MiB:
|
|
333
|
+
* the child is then killed and each array keeps at most the first 16 MiB.
|
|
334
|
+
*/
|
|
335
|
+
declare function spawnPiped(argv: string[], stdin: readonly u8[], stdout: u8[], stderr: u8[]): i32;
|
|
302
336
|
/**
|
|
303
337
|
* Start `argv[0]` through `PATH` without waiting: the child's pid, or a
|
|
304
338
|
* negative errno. The child is the program's until `waitPid` reaps it. No
|
|
@@ -322,10 +356,10 @@ declare function availableParallelism(): i32;
|
|
|
322
356
|
/** Bytes of physical memory, or -1 when the host does not say. */
|
|
323
357
|
declare function totalMemory(): i64;
|
|
324
358
|
/**
|
|
325
|
-
* Set this process's nice value to `n
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* it. Throws under Node.
|
|
359
|
+
* Set this process's nice value to `n`, which must be 0..19: 0, or a negative
|
|
360
|
+
* errno. Lower-only, even as root: `n` outside 0..19 is -22 (EINVAL), and `n`
|
|
361
|
+
* below the current value is -13 (EACCES), both with no system call. Children
|
|
362
|
+
* started afterwards inherit it. Throws under Node.
|
|
329
363
|
*/
|
|
330
364
|
declare function setNice(n: i32): i32;
|
|
331
365
|
/**
|
|
@@ -492,6 +526,9 @@ declare var crypto: {
|
|
|
492
526
|
//
|
|
493
527
|
// `docs/LANGUAGE.md` -> Builtins -> Builtin modules is the normative list.
|
|
494
528
|
|
|
529
|
+
// Every negative errno a file call answers is the platform's own, untranslated:
|
|
530
|
+
// the numbers shown here are Linux's (-2 ENOENT, -17 EEXIST, -18 EXDEV, -22
|
|
531
|
+
// EINVAL, -40 ELOOP), and Darwin's differ (ELOOP is -62 there).
|
|
495
532
|
declare module "nish:fs" {
|
|
496
533
|
export function readFileSync(path: string): string;
|
|
497
534
|
export function readFileSyncOrNull(path: string): string | null;
|
|
@@ -506,6 +543,12 @@ declare module "nish:fs" {
|
|
|
506
543
|
export function renameSync(from: string, to: string): i32;
|
|
507
544
|
/** 0, or a negative errno: a directory is refused, not removed. */
|
|
508
545
|
export function unlinkSync(path: string): i32;
|
|
546
|
+
/** The byte count, or a negative errno; the file is exactly `mode` (0..0o777) before it holds a byte, a link refused, written in place. */
|
|
547
|
+
export function writeFileSyncMode(path: string, data: string, mode: i32): i32;
|
|
548
|
+
/** As `writeFileSyncMode`, but -17 when anything is at `path`: one racer wins; a failed write removes the file. */
|
|
549
|
+
export function createFileExclusiveSync(path: string, data: string, mode: i32): i32;
|
|
550
|
+
/** 0, or a negative errno: -17 for a path already there, whose mode is left alone. */
|
|
551
|
+
export function mkdirSyncMode(path: string, mode: i32): i32;
|
|
509
552
|
/** `true` when the directory was created, `false` when it already existed. */
|
|
510
553
|
export function mkdirSync(path: string): boolean;
|
|
511
554
|
export function isDirectorySync(path: string): boolean;
|
|
@@ -530,6 +573,11 @@ declare module "nish:process" {
|
|
|
530
573
|
* at either path is refused, and the answer is -1.
|
|
531
574
|
*/
|
|
532
575
|
export function spawnSyncTo(argv: string[], stdoutPath: string, stderrPath: string): number;
|
|
576
|
+
/**
|
|
577
|
+
* The same run with `stdin` written to the child and its stdout and stderr
|
|
578
|
+
* appended to the two arrays: the status, or -2 past 16 MiB on a stream.
|
|
579
|
+
*/
|
|
580
|
+
export function spawnPiped(argv: string[], stdin: readonly u8[], stdout: u8[], stderr: u8[]): i32;
|
|
533
581
|
/** Start a child without waiting: its pid, or a negative errno. */
|
|
534
582
|
export function spawn(argv: string[]): i32;
|
|
535
583
|
/** Send 0, 2, 9 or 15 to `pid`: 0, or a negative errno; any other signal is -22. */
|
|
@@ -540,7 +588,7 @@ declare module "nish:process" {
|
|
|
540
588
|
export function availableParallelism(): i32;
|
|
541
589
|
/** Bytes of physical memory, or -1. */
|
|
542
590
|
export function totalMemory(): i64;
|
|
543
|
-
/** Set this process's nice value
|
|
591
|
+
/** Set this process's nice value, 0..19 only: 0, -22 outside it, or -13 below the current value (lower-only, even as root). */
|
|
544
592
|
export function setNice(n: i32): i32;
|
|
545
593
|
/**
|
|
546
594
|
* Move this process to `SCHED_IDLE`: 0, a negative errno, or -95 off Linux.
|
package/runtime/nish.h
CHANGED
|
@@ -282,6 +282,20 @@ nish_array *nish_alloc_array(uint64_t elem_size, uint64_t len);
|
|
|
282
282
|
Variable-time: never call it on a secret. */
|
|
283
283
|
int64_t nish_regex_scan(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off, int64_t width,
|
|
284
284
|
int64_t from);
|
|
285
|
+
/* `Math.floor`, `Math.ceil` and `Math.trunc`, and the floor `Math.round`
|
|
286
|
+
takes (#943): each answers as C's `floor`, `ceil` and `trunc` do, -0, the
|
|
287
|
+
infinities and NaN included. A module for x86-64 calls them and registers a
|
|
288
|
+
constructor in `@llvm.global_ctors` that passes `nish_round_init` libm's
|
|
289
|
+
`floor`, `ceil` and `trunc`; it asks the CPU for SSE4.1 once before `main`
|
|
290
|
+
and keeps them on a machine without it, or under `NISH_SIMD=scalar`. Each
|
|
291
|
+
is otherwise SSE4.1's `roundsd`, so the constructor must run first. The
|
|
292
|
+
runtime references no libm symbol: the module that rounds does. They live
|
|
293
|
+
in runtime/runtime-simd.c; runtime-wasm.c has the wasm32 ones for host IR
|
|
294
|
+
that `scripts/build.sh --profile wasm` links. */
|
|
295
|
+
void nish_round_init(double (*floor_fn)(double), double (*ceil_fn)(double), double (*trunc_fn)(double));
|
|
296
|
+
double nish_floor(double x);
|
|
297
|
+
double nish_ceil(double x);
|
|
298
|
+
double nish_trunc(double x);
|
|
285
299
|
/* No array grows past 2^31 - 1 elements and no string past 2^31 - 1 bytes:
|
|
286
300
|
* `nish_array_grow`, `nish_alloc_array` and `nish_str_concat` fail as an
|
|
287
301
|
* allocation does (`nish: out of memory`, exit 1; a trap on wasm) rather than
|
|
@@ -403,10 +417,24 @@ nish_array *nish_read_file_bytes(const nish_str *path);
|
|
|
403
417
|
* errno — -EXDEV for a rename across file systems, which moves nothing, and
|
|
404
418
|
* -EISDIR or -EPERM for a directory handed to `nish_unlink`, which never calls
|
|
405
419
|
* rmdir. A path holding a NUL answers -EINVAL, not -ENOENT, so a program can
|
|
406
|
-
* tell a malformed path from a missing file.
|
|
420
|
+
* tell a malformed path from a missing file. The numbers are the platform's
|
|
421
|
+
* errno values, untranslated: -ELOOP is -40 on Linux and -62 on Darwin. */
|
|
407
422
|
int32_t nish_rename(const nish_str *from, const nish_str *to);
|
|
408
423
|
int32_t nish_unlink(const nish_str *path);
|
|
409
424
|
int32_t nish_write_file_bytes(const nish_str *path, const nish_array *bytes);
|
|
425
|
+
/* `writeFileSyncMode(path, data, mode)`, `createFileExclusiveSync(path, data,
|
|
426
|
+
* mode)` and `mkdirSyncMode(path, mode)`: a file written, or created only if
|
|
427
|
+
* nothing is at the path (`O_EXCL`), then `fchmod`ed to exactly `mode` before a
|
|
428
|
+
* byte is written; a directory made with `mode & ~umask`. A link at a file's
|
|
429
|
+
* path is refused. The file writers answer the byte count and `nish_mkdir_mode`
|
|
430
|
+
* 0, or a negative platform errno: -EEXIST for an exclusive create or a mkdir
|
|
431
|
+
* that finds the path taken (a directory's mode is then left alone), -EINVAL
|
|
432
|
+
* for a mode outside 0..0777 (the setuid, setgid and sticky bits included) or
|
|
433
|
+
* a path holding a NUL, -ENOSYS under WASI. A failed exclusive create leaves
|
|
434
|
+
* no file behind. */
|
|
435
|
+
int32_t nish_write_file_mode(const nish_str *path, const nish_str *data, int32_t mode);
|
|
436
|
+
int32_t nish_create_file_exclusive(const nish_str *path, const nish_str *data, int32_t mode);
|
|
437
|
+
int32_t nish_mkdir_mode(const nish_str *path, int32_t mode);
|
|
410
438
|
/* `writeBytes(bytes, off, len)` (nish:io), in runtime-os.c: the bytes
|
|
411
439
|
* `[off, off + len)` of a `u8[]` on stdout, a short write finished and `EINTR`
|
|
412
440
|
* retried. The caller has range-checked `off` and `len`. Answers `len`, or the
|
|
@@ -438,6 +466,17 @@ int32_t nish_spawn_to(const nish_array *argv, const nish_str *out, const nish_st
|
|
|
438
466
|
* own error otherwise, and -ENOSYS under WASI. The child is reaped by
|
|
439
467
|
* `nish_wait_pid`. */
|
|
440
468
|
int32_t nish_spawn_child(const nish_array *argv);
|
|
469
|
+
/* `spawnPiped(argv, stdin, stdout, stderr)`: start element 0 of `argv` as
|
|
470
|
+
* `nish_spawn` does, with the bytes of `stdin` (a `u8[]`) on its standard input
|
|
471
|
+
* and its standard output and error appended to `stdout` and `stderr`, wait for
|
|
472
|
+
* it, and answer `nish_spawn`'s status. -1 is `nish_spawn`'s -1, a pipe or
|
|
473
|
+
* `poll` that fails included, and always under WASI. -2 is a stream that passed
|
|
474
|
+
* 16 MiB: the child is killed with SIGKILL and reaped, and that array holds the
|
|
475
|
+
* stream's first 16 MiB. The two output arrays grow as `push` grows them, so
|
|
476
|
+
* the compiler treats both as pushed. `stdin` is read in place and never
|
|
477
|
+
* copied; SIGPIPE is blocked on the calling thread for the call, a SIGPIPE the
|
|
478
|
+
* call raised is consumed, and the thread's mask is restored after it. */
|
|
479
|
+
int32_t nish_spawn_piped(const nish_array *argv, const nish_array *in, nish_array *out, nish_array *err);
|
|
441
480
|
/* `kill(pid, sig)`: send `sig` to `pid` and answer 0, or a negative errno. Only
|
|
442
481
|
* 0, 2 (SIGINT), 9 (SIGKILL) and 15 (SIGTERM) are sent; any other signal, and
|
|
443
482
|
* a pid of 0 or below, is -EINVAL. -ESRCH when no process has the pid, -EPERM
|
|
@@ -455,9 +494,9 @@ nish_array *nish_wait_pid(int32_t pid, int32_t flags);
|
|
|
455
494
|
* page size; Darwin: `hw.memsize`), or -1 where the host does not say. */
|
|
456
495
|
int64_t nish_total_memory(void);
|
|
457
496
|
/* `setNice(n)`: this process's nice value set to `n` (`setpriority`), 0 or a
|
|
458
|
-
* negative errno.
|
|
459
|
-
*
|
|
460
|
-
* WASI. */
|
|
497
|
+
* negative errno. Lower-only, as root too: `n` takes 0..19, and anything else
|
|
498
|
+
* is -22 (-EINVAL) with no system call; `n` below the current value is -13
|
|
499
|
+
* (-EACCES) with none either. -ENOSYS under WASI. */
|
|
461
500
|
int32_t nish_set_nice(int32_t n);
|
|
462
501
|
/* `setIdlePriority()`: this process moved to `SCHED_IDLE` (Linux), 0 or a
|
|
463
502
|
* negative errno; -95 (Linux's EOPNOTSUPP) elsewhere, -ENOSYS under WASI. An
|
package/runtime/nish.mjs
CHANGED
|
@@ -94,12 +94,16 @@ provide("appendFileSync", shim.appendFileSync);
|
|
|
94
94
|
provide("writeFileBytesSync", shim.writeFileBytesSync);
|
|
95
95
|
provide("renameSync", shim.renameSync);
|
|
96
96
|
provide("unlinkSync", shim.unlinkSync);
|
|
97
|
+
provide("writeFileSyncMode", shim.writeFileSyncMode);
|
|
98
|
+
provide("createFileExclusiveSync", shim.createFileExclusiveSync);
|
|
99
|
+
provide("mkdirSyncMode", shim.mkdirSyncMode);
|
|
97
100
|
provide("mkdirSync", shim.mkdirSync);
|
|
98
101
|
provide("isDirectorySync", shim.isDirectorySync);
|
|
99
102
|
provide("readdirSync", shim.readdirSync);
|
|
100
103
|
provide("realpathSync", shim.realpathSync);
|
|
101
104
|
provide("spawnSync", shim.spawnSync);
|
|
102
105
|
provide("spawnSyncTo", shim.spawnSyncTo);
|
|
106
|
+
provide("spawnPiped", shim.spawnPiped);
|
|
103
107
|
// WP34 N12. `spawn` and `waitPid` throw, naming themselves: a child is reaped
|
|
104
108
|
// by Node's event loop. `kill`, `availableParallelism` and `totalMemory` answer.
|
|
105
109
|
provide("spawn", shim.spawn);
|
package/runtime/node.mjs
CHANGED
|
@@ -102,6 +102,46 @@ const unlinkSync = (path) => errnoOr(0, () => fs.unlinkSync(path));
|
|
|
102
102
|
const writeFileBytesSync = (path, bytes) =>
|
|
103
103
|
errnoOr(bytes.length, () => fs.writeFileSync(path, Uint8Array.from(bytes), { flag: WRITE_FLAGS, mode: 0o644 }));
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* `writeFileSyncMode` and `createFileExclusiveSync`'s shared body, as
|
|
107
|
+
* `runtime-os.c`'s: the file opened with `flag`, `fchmod`ed to exactly `mode`
|
|
108
|
+
* before a byte is written, then every byte of `data`, then the count, or a
|
|
109
|
+
* negative errno. A mode outside 0..0o777 is -22 before the file is touched.
|
|
110
|
+
* After an exclusive create (`"wx"`), any failure unlinks the file it made.
|
|
111
|
+
*/
|
|
112
|
+
function putFileMode(path, data, flag, mode) {
|
|
113
|
+
if (!(mode >= 0 && mode <= 0o777)) return -22;
|
|
114
|
+
let fd;
|
|
115
|
+
try {
|
|
116
|
+
fd = fs.openSync(path, flag, mode);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
return negErrno(e);
|
|
119
|
+
}
|
|
120
|
+
let r;
|
|
121
|
+
try {
|
|
122
|
+
fs.fchmodSync(fd, mode);
|
|
123
|
+
const buf = Buffer.from(data, "utf8");
|
|
124
|
+
for (let done = 0; done < buf.length; ) done += fs.writeSync(fd, buf, done);
|
|
125
|
+
r = buf.length;
|
|
126
|
+
} catch (e) {
|
|
127
|
+
r = negErrno(e);
|
|
128
|
+
}
|
|
129
|
+
const closed = errnoOr(0, () => fs.closeSync(fd));
|
|
130
|
+
const answer = r >= 0 && closed < 0 ? closed : r;
|
|
131
|
+
if (answer < 0 && flag === "wx") errnoOr(0, () => fs.unlinkSync(path));
|
|
132
|
+
return answer;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** `writeFileSyncMode(path, data, mode)`: `WRITE_FLAGS`, so a link at the path is refused. */
|
|
136
|
+
const writeFileSyncMode = (path, data, mode) => putFileMode(path, data, WRITE_FLAGS, mode);
|
|
137
|
+
|
|
138
|
+
/** `createFileExclusiveSync(path, data, mode)`: `"wx"`, -17 when anything is at the path. */
|
|
139
|
+
const createFileExclusiveSync = (path, data, mode) => putFileMode(path, data, "wx", mode);
|
|
140
|
+
|
|
141
|
+
/** `mkdirSyncMode(path, mode)`: 0, or a negative errno; -17 for a path already there. */
|
|
142
|
+
const mkdirSyncMode = (path, mode) =>
|
|
143
|
+
mode >= 0 && mode <= 0o777 ? errnoOr(0, () => fs.mkdirSync(path, { mode })) : -22;
|
|
144
|
+
|
|
105
145
|
/**
|
|
106
146
|
* `writeBytes`'s write, after the core checked the range `[from, to)`: every
|
|
107
147
|
* byte to stdout, then the count, or a negative errno. `fs.writeSync` may
|
|
@@ -268,6 +308,41 @@ function spawnSyncTo(argv, out, err) {
|
|
|
268
308
|
return spawnImpl(argv, out, err);
|
|
269
309
|
}
|
|
270
310
|
|
|
311
|
+
/** The bytes `spawnPiped` keeps of one stream, as `runtime-os.c`'s `NISH_PIPED_CAP`. */
|
|
312
|
+
const PIPED_CAP = 16 * 1024 * 1024;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* `spawnPiped(argv, stdin)`'s run, after the core took it apart: the status
|
|
316
|
+
* and the two streams' bytes, which the core appends to the caller's arrays.
|
|
317
|
+
* The status is `spawnImpl`'s, and -2 when a stream passed `PIPED_CAP`: Node
|
|
318
|
+
* then kills the child with `SIGKILL`, as the runtime does, and reports
|
|
319
|
+
* `ENOBUFS`, and what it read is cut to the cap here as the runtime keeps it.
|
|
320
|
+
* A child that exits without reading its stdin is not an error on either side.
|
|
321
|
+
*/
|
|
322
|
+
function spawnPiped(argv, input) {
|
|
323
|
+
if (argv.length === 0) return [-1, [], []];
|
|
324
|
+
let r;
|
|
325
|
+
// Node throws, rather than reporting, for an empty command or a NUL in any
|
|
326
|
+
// argument, which the runtime answers -1 for without starting a child.
|
|
327
|
+
try {
|
|
328
|
+
r = child_process.spawnSync(argv[0], argv.slice(1), {
|
|
329
|
+
input: Uint8Array.from(input),
|
|
330
|
+
maxBuffer: PIPED_CAP,
|
|
331
|
+
killSignal: "SIGKILL",
|
|
332
|
+
});
|
|
333
|
+
} catch {
|
|
334
|
+
return [-1, [], []];
|
|
335
|
+
}
|
|
336
|
+
const bytes = (b) => (b === null || b === undefined ? [] : Array.from(b.subarray(0, PIPED_CAP)));
|
|
337
|
+
const out = bytes(r.stdout);
|
|
338
|
+
const err = bytes(r.stderr);
|
|
339
|
+
// `EPIPE` is the child closing its stdin unread, which Node reports with
|
|
340
|
+
// the status it still waited for; the runtime ignores it the same way.
|
|
341
|
+
if (r.error !== undefined && r.error.code !== "EPIPE") return [r.error.code === "ENOBUFS" ? -2 : -1, out, err];
|
|
342
|
+
if (r.signal !== null && r.signal !== undefined) return [128 + (os.constants.signals[r.signal] ?? 0), out, err];
|
|
343
|
+
return [r.status === null ? -1 : r.status, out, err];
|
|
344
|
+
}
|
|
345
|
+
|
|
271
346
|
/**
|
|
272
347
|
* `kill(pid, sig)`: 0, or a negative errno, as `runtime-os.c`'s `nish_kill`
|
|
273
348
|
* answers. Only 0, 2, 9 and 15 are sent and a pid of 0 or below is refused,
|
|
@@ -393,6 +468,9 @@ const CALLS = {
|
|
|
393
468
|
writeFileBytesSync,
|
|
394
469
|
renameSync,
|
|
395
470
|
unlinkSync,
|
|
471
|
+
writeFileSyncMode,
|
|
472
|
+
createFileExclusiveSync,
|
|
473
|
+
mkdirSyncMode,
|
|
396
474
|
writeBytes,
|
|
397
475
|
mkdirSync,
|
|
398
476
|
isDirectorySync,
|
|
@@ -403,6 +481,7 @@ const CALLS = {
|
|
|
403
481
|
getenv,
|
|
404
482
|
spawnSync,
|
|
405
483
|
spawnSyncTo,
|
|
484
|
+
spawnPiped,
|
|
406
485
|
kill,
|
|
407
486
|
availableParallelism,
|
|
408
487
|
totalMemory,
|