@amritk/nish-aarch64-darwin 0.34.0 → 0.35.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/INSTALL.md +16 -12
- package/bin/nish +0 -0
- package/docs/AI.md +77 -14
- package/package.json +1 -1
- package/runtime/async.d.mts +49 -0
- package/runtime/async.mjs +27 -0
- package/runtime/nish.d.ts +72 -0
- package/runtime/nish.h +6 -2
- package/runtime/nish.mjs +4 -0
- package/runtime/runtime-net.c +22 -0
- package/runtime/runtime-shm.c +93 -2
- package/std/README.md +131 -131
- package/std/bytes.ts +28 -5
- package/std/crypto/base64url.ts +42 -21
- package/std/crypto/bcrypt.ts +222 -183
- package/std/net/http1-codec.ts +262 -92
- package/std/net/http1-server.ts +87 -9
- package/std/net/http3-server.ts +30 -6
- package/std/net/postgres.ts +129 -40
- package/std/net/quic-recovery.ts +36 -8
- package/std/net/quic.ts +664 -59
- package/std/sync/ring.ts +236 -16
- package/std/sync/shm.ts +29 -0
package/INSTALL.md
CHANGED
|
@@ -54,6 +54,8 @@ export PATH="$(brew --prefix llvm@18)/bin:$PATH" # add to your shell profile
|
|
|
54
54
|
Xcode's `clang` (`xcode-select --install`) also works for native binaries;
|
|
55
55
|
the Homebrew LLVM is needed for the `wasm` and `wasi` profiles (`wasm-ld`).
|
|
56
56
|
|
|
57
|
+
<a id="wasi-optional-for---profile-wasi" style="scroll-margin-top: 65px"></a>
|
|
58
|
+
|
|
57
59
|
### WASI (optional, for `--profile wasi`)
|
|
58
60
|
|
|
59
61
|
The `wasi` profile links the runtime against wasi-libc so that whole programs
|
|
@@ -105,7 +107,7 @@ of its own architecture by the release workflow before the release existed, and
|
|
|
105
107
|
installing is a download and an unpack.
|
|
106
108
|
|
|
107
109
|
Through npm, which is also how an Nish *program* resolves its dependencies
|
|
108
|
-
([wp21-packages.md](wp21-packages.md)), so this is the one to pick if you want
|
|
110
|
+
([wp21-packages.md](https://github.com/amritk/nish/blob/main/docs/wp21-packages.md)), so this is the one to pick if you want
|
|
109
111
|
the compiler pinned per project in a `package.json`:
|
|
110
112
|
|
|
111
113
|
```bash
|
|
@@ -169,7 +171,7 @@ release also attaches `nish-<version>.spdx.json`, an SPDX 2.3 software bill of
|
|
|
169
171
|
materials: every attached file with its SHA-256, the clang, linker, Node.js
|
|
170
172
|
and seed compiler that built each tarball (recorded by the build row itself),
|
|
171
173
|
and the vendored code from
|
|
172
|
-
[THIRD_PARTY_NOTICES.md](
|
|
174
|
+
[THIRD_PARTY_NOTICES.md](https://github.com/amritk/nish/blob/main/THIRD_PARTY_NOTICES.md) that each of them contains.
|
|
173
175
|
How these fit the rest of the release's defences is
|
|
174
176
|
[docs/security/supply-chain.md](security/supply-chain.md).
|
|
175
177
|
|
|
@@ -198,13 +200,13 @@ never fails over it.
|
|
|
198
200
|
> installers above handle it by running the binary through a one-line `exec` of
|
|
199
201
|
> an absolute path. If you unpack a tarball by hand, invoke it by path
|
|
200
202
|
> (`nish-<version>-<asset>/bin/nish`) or write that wrapper yourself. The
|
|
201
|
-
> underlying defect is [wp19 §5a](wp19-stage0-retirement.md#5a-what-r6-is-waiting-on)
|
|
203
|
+
> underlying defect is [wp19 §5a](https://github.com/amritk/nish/blob/main/docs/wp19-stage0-retirement.md#5a-what-r6-is-waiting-on)
|
|
202
204
|
> item 4.
|
|
203
205
|
|
|
204
206
|
**The package is `nish` from 0.20.0.** Releases up to 0.18.0 were published
|
|
205
207
|
as `@amritk/nish` (0.19.0's publish to npm failed), which is deprecated and
|
|
206
208
|
points here; `npm install -g nish`
|
|
207
|
-
replaces it ([docs/wp12-release.md](wp12-release.md#the-npm-name)). Versions
|
|
209
|
+
replaces it ([docs/wp12-release.md](https://github.com/amritk/nish/blob/main/docs/wp12-release.md#the-npm-name)). Versions
|
|
208
210
|
0.0.0 and 0.0.1 of `nish` are an unrelated 2014 project that held the name
|
|
209
211
|
before it, so pin `nish@^0.20.0` or later rather than an older range.
|
|
210
212
|
|
|
@@ -233,9 +235,9 @@ nish: no prebuilt compiler for freebsd/x64
|
|
|
233
235
|
Until 0.6.0 it ran the TypeScript compiler that shipped in the same package
|
|
234
236
|
instead — the same compiler by every test here, about eight times slower, and
|
|
235
237
|
no C toolchain needed. That compiler was stage0, which was deleted in R6
|
|
236
|
-
([wp19](wp19-stage0-retirement.md)), so there is nothing left in the package to
|
|
238
|
+
([wp19](https://github.com/amritk/nish/blob/main/docs/wp19-stage0-retirement.md)), so there is nothing left in the package to
|
|
237
239
|
fall back to. The cost is stated where the rest of that deletion's costs are,
|
|
238
|
-
in [wp19 §6](wp19-stage0-retirement.md#6-what-retirement-costs-stated-plainly),
|
|
240
|
+
in [wp19 §6](https://github.com/amritk/nish/blob/main/docs/wp19-stage0-retirement.md#6-what-retirement-costs-stated-plainly),
|
|
239
241
|
and it is a real one: **musl, FreeBSD and 32-bit platforms have no `npm
|
|
240
242
|
install` route to a compiler any more.**
|
|
241
243
|
|
|
@@ -276,7 +278,7 @@ to have on the issue tracker rather than a workaround to discover here.
|
|
|
276
278
|
`nish` from 0.20.0; 0.19.0 is on GitHub only, because its npm publish
|
|
277
279
|
failed. Every release publishes the main package and its platform packages to
|
|
278
280
|
npm from `release.yml`, by trusted publishing
|
|
279
|
-
([docs/wp12-release.md](wp12-release.md#release-procedure) step 4), so
|
|
281
|
+
([docs/wp12-release.md](https://github.com/amritk/nish/blob/main/docs/wp12-release.md#release-procedure) step 4), so
|
|
280
282
|
`npm install -g nish` is the whole install. The rest of this section is
|
|
281
283
|
for a machine that cannot reach the registry, or a release before 0.10.0.
|
|
282
284
|
|
|
@@ -358,7 +360,7 @@ v0.16.0; v0.1.1 was the first release with a binary attached.
|
|
|
358
360
|
The `v0.1.0` tag exists but has no release behind it and no assets, so every
|
|
359
361
|
`v0.1.0` download URL is a 404 — the tag was pushed by a workflow, and GitHub
|
|
360
362
|
raises no event for that, so nothing ever built it
|
|
361
|
-
([docs/wp12-release.md](wp12-release.md#release-procedure) step 2). When a newer
|
|
363
|
+
([docs/wp12-release.md](https://github.com/amritk/nish/blob/main/docs/wp12-release.md#release-procedure) step 2). When a newer
|
|
362
364
|
release exists, take its version from
|
|
363
365
|
[the releases page](https://github.com/amritk/nish/releases/latest).
|
|
364
366
|
|
|
@@ -435,7 +437,7 @@ over, `runtime-checksum.c` the CRC-32 kernels of `nish/compress/checksum`,
|
|
|
435
437
|
`runtime-sandbox.c` the confinement a `--sandbox` build applies, and
|
|
436
438
|
`runtime-shm.c` the shared-memory regions of `nish/sync/shm`.
|
|
437
439
|
They are separate so that each carries its own measured size ceiling
|
|
438
|
-
([docs/wp7-runtime.md](wp7-runtime.md)); nothing in the core calls into the
|
|
440
|
+
([docs/wp7-runtime.md](https://github.com/amritk/nish/blob/main/docs/wp7-runtime.md)); nothing in the core calls into the
|
|
439
441
|
others, so a line that names only `runtime.c` and `runtime-os.c` still links a
|
|
440
442
|
program that uses none of the other nine. `scripts/build.sh` compiles the other
|
|
441
443
|
nine beside any
|
|
@@ -451,7 +453,7 @@ beside `-DNISH_THREADS=1`).
|
|
|
451
453
|
## 2a. What `npm run build` does
|
|
452
454
|
|
|
453
455
|
`src/` is the compiler, written in Nish, and it compiles itself
|
|
454
|
-
([docs/wp14-selfhost.md](wp14-selfhost.md)). From a checkout, with clang on
|
|
456
|
+
([docs/wp14-selfhost.md](https://github.com/amritk/nish/blob/main/docs/wp14-selfhost.md)). From a checkout, with clang on
|
|
455
457
|
`PATH`, `npm run build` runs `scripts/bootstrap.sh` with the seed from §2:
|
|
456
458
|
|
|
457
459
|
```bash
|
|
@@ -486,7 +488,7 @@ in `src/` and becomes usable inside `src/` from the next release on; CI's
|
|
|
486
488
|
`bootstrap` job is what checks that the released seed still builds stage1.
|
|
487
489
|
Until R6 the seed was a TypeScript compiler, stage0, run under Node; it was
|
|
488
490
|
deleted once the native one answered every flag it did
|
|
489
|
-
([wp19](wp19-stage0-retirement.md)).
|
|
491
|
+
([wp19](https://github.com/amritk/nish/blob/main/docs/wp19-stage0-retirement.md)).
|
|
490
492
|
|
|
491
493
|
## 3. Hello world
|
|
492
494
|
|
|
@@ -518,7 +520,7 @@ nish hello.ts -o hello.ll
|
|
|
518
520
|
|
|
519
521
|
Other build profiles: `--profile size` (smallest binary), `--profile debug`
|
|
520
522
|
(no optimisation, symbols kept). Run `nish --help` for every flag, and
|
|
521
|
-
see the [README](
|
|
523
|
+
see the [README](https://github.com/amritk/nish/blob/main/README.md) for the language subset.
|
|
522
524
|
|
|
523
525
|
To use a program as a script rather than keep the binary, run it:
|
|
524
526
|
|
|
@@ -549,6 +551,8 @@ it cannot make private, compiles the runtime itself as it always did; the
|
|
|
549
551
|
wasi profile and `scripts/build.sh` called directly never use the cache.
|
|
550
552
|
Removing the directory is always safe.
|
|
551
553
|
|
|
554
|
+
<a id="4-exit-codes" style="scroll-margin-top: 65px"></a>
|
|
555
|
+
|
|
552
556
|
## 4. Exit codes
|
|
553
557
|
|
|
554
558
|
| Code | Meaning |
|
package/bin/nish
CHANGED
|
Binary file
|
package/docs/AI.md
CHANGED
|
@@ -108,7 +108,9 @@ the difference is a row of [the user guide's table](guide/using-nish/to-typescri
|
|
|
108
108
|
or a bug in `nish` worth reporting. A program on `nish:net`, the signal
|
|
109
109
|
descriptor or a C declaration (NL4010, NL4011), a compound assignment whose
|
|
110
110
|
index would run twice (NL4012) or a generic whose element type decides its
|
|
111
|
-
code (NL4013, NL4014) is refused and has no TypeScript to run.
|
|
111
|
+
code (NL4013, NL4014) is refused and has no TypeScript to run. An `async`
|
|
112
|
+
program is written as Node async/await, whose spawned tasks may print in
|
|
113
|
+
another order than native.
|
|
112
114
|
|
|
113
115
|
Two more surfaces worth knowing: `nish --emit-ast f.ts` prints what was parsed
|
|
114
116
|
and `nish --emit-checked f.ts` prints the side tables the emitter reads. Both
|
|
@@ -140,7 +142,11 @@ everywhere, a template literal instead of `String(n)`.
|
|
|
140
142
|
- A module is one `.ts` file. At the top level it may hold **only**
|
|
141
143
|
declarations: `const` arrows (functions), `class`, `interface`, module
|
|
142
144
|
`const`s, `type` aliases, numeric `enum`s, and `import`s. There is no
|
|
143
|
-
top-level code, so there is no initialisation order to think about.
|
|
145
|
+
top-level code, so there is no initialisation order to think about. The
|
|
146
|
+
one exception is an entry module with no `main`: its top-level statements
|
|
147
|
+
run as `main` (`docs/LANGUAGE.md` → "Top-level code"). Declare every
|
|
148
|
+
function above the first statement, and pass a function what it needs
|
|
149
|
+
rather than naming a variable of the code.
|
|
144
150
|
- `export const main` lives in the entry module only, takes no parameters, and
|
|
145
151
|
returns `void` or an `i32`-lowered `number` — the process exit code. Under
|
|
146
152
|
`--number-mode f64`, declare it `main(): i32`.
|
|
@@ -175,12 +181,12 @@ rejects. This table is the highest-value part of the page.
|
|
|
175
181
|
| `h.get<i32>(7)` (type argument at a method call) | `Type arguments are not written at a call site in Nish` | `h.get(7)` — a generic method infers like a generic function |
|
|
176
182
|
| `<T>(p: T) => p.x` (a member of a type parameter) | `` Cannot read `x` of `T`: an unconstrained type parameter has no members `` | `<T extends Point>(p: T) => p.x`, where `Point` is a class or interface that declares `x` |
|
|
177
183
|
| `identity<i32>(7)` (type argument at a call) | `Type arguments are not written at a call site in Nish` | `identity(7)` — `T` is inferred from the arguments |
|
|
178
|
-
|
|
|
184
|
+
| a promise in a variable, `.then`, `Promise.all` | forbidden: a promise is never a value | `await` each call where it is made; see [Async functions](#async-functions) |
|
|
179
185
|
| `namespace N { }`, `declare global { }`, `@decorator` | forbidden | one module per file; a plain function in place of a decorator |
|
|
180
186
|
| `keyof T`, `<T = i32>` (a default type argument) | `` Unsupported type `keyof T` (Phase 1 supports …) `` / `a default type argument … is not supported` | name the type; a type argument is always inferred |
|
|
181
187
|
| `class B extends A` | `` `extends` is not supported: Nish has no inheritance `` | repeat the fields and `implements` an interface |
|
|
182
188
|
| `static` members, `get x()` / `set x(v)` | `` … `static` members are not supported `` / `` Getters and setters are not supported in class `C` (use a method) `` | module `const`s and plain methods |
|
|
183
|
-
| `const [a, b] = xs`, `({ x }: Point) =>`, `(n?: i32)`, `(n = 1)`, `f(...xs)` | `Destructuring is not supported` / `Destructured parameters are not supported` / `Optional parameters are not supported` / `` Parameter `n` has a default and no type annotation `` / `Spread arguments are not supported` | one name per binding; `n: i32 = 1` (a constant or an earlier parameter) for a default; `...ns: i32[]` as the last parameter, or pass the array itself |
|
|
189
|
+
| `const [a, b] = xs`, `({ x }: Point) =>`, `(n?: i32)`, `(n = 1)`, `f(...xs)` | `Destructuring is not supported` / `Destructured parameters are not supported` / `Optional parameters are not supported` / `` Parameter `n` has a default and no type annotation `` / `Spread arguments are not supported` | one name per binding; `n: i32 = 1` (a constant or an earlier parameter) for a default, `o: Opts | null = null` for an options argument; `...ns: i32[]` as the last parameter, or pass the array itself |
|
|
184
190
|
| `function f(): void;` (an overload), `export default function ()` | `Functions must have a body` / `Functions must be named` | one named function, with its body |
|
|
185
191
|
| a method in an `interface` | `` Interface `I` cannot declare methods (interfaces describe layout only) `` | fields only; a top-level function over the interface |
|
|
186
192
|
| `abstract class`, `declare class`, `constructor(public x: i32)`, `interface B extends A`, `[k: string]: T` | `Abstract classes are not supported` / `` `declare class` is not supported `` / `Parameter properties … are not supported; declare the field and assign it` / ``Interface inheritance (`extends`) is not supported; list every field`` / `Index signatures are not supported …` | a plain class with its fields declared and assigned in the constructor; every field listed; a `Map` for a keyed table |
|
|
@@ -583,8 +589,11 @@ const double = (n: i32): i32 => n * 2; // a concise body is that one `return`
|
|
|
583
589
|
- **Parameters are immutable**: `p = 1`, `p++`, `p += 1` are all rejected. Copy
|
|
584
590
|
into a `let` first.
|
|
585
591
|
- A non-`void` function must return on every path.
|
|
586
|
-
- Rejected: generators, `async`, destructured
|
|
587
|
-
parameters, overloads, nested function declarations.
|
|
592
|
+
- Rejected: generators, a method that is `async`, destructured and optional
|
|
593
|
+
(`x?: T`) parameters, overloads, nested function declarations. A default
|
|
594
|
+
(`n: i32 = 1`) or a rest parameter (`...ns: i32[]`) is allowed on a
|
|
595
|
+
function, a method, a constructor or a module `const` arrow — see
|
|
596
|
+
"Optional parameters" in [Recipes for what is missing](#recipes-for-what-is-missing). Type parameters *are*
|
|
588
597
|
allowed — see [Generic functions](#generic-functions).
|
|
589
598
|
|
|
590
599
|
### Generic functions
|
|
@@ -850,8 +859,8 @@ export const main = (): i32 => {
|
|
|
850
859
|
(`(x) => { if (x > limit) { hits.push(x) } }`); each capture becomes one more
|
|
851
860
|
argument of the direct call. It may not assign to one, read a `let`
|
|
852
861
|
(`` The arrow reads `k`, a `let` of the function it is written in: ... bind the value to a `const` before the call ``),
|
|
853
|
-
read `this` (bind `const self = this`), or read a function parameter
|
|
854
|
-
function. An arrow inside another arrow, or given to a method, a
|
|
862
|
+
read `this` (bind `const self = this`), or read a function-typed parameter
|
|
863
|
+
of its function. An arrow inside another arrow, or given to a method, a
|
|
855
864
|
constructor, `expose` or `exposeWith`, captures nothing; `xs.sort(cmp)`'s
|
|
856
865
|
comparator is the exception, and captures as an arrow argument does.
|
|
857
866
|
- **The argument's type must match exactly**: `(x: i32) => i64` for a
|
|
@@ -873,6 +882,52 @@ export const main = (): i32 => {
|
|
|
873
882
|
};
|
|
874
883
|
```
|
|
875
884
|
|
|
885
|
+
### Async functions
|
|
886
|
+
|
|
887
|
+
`async` compiles natively: a function that awaits becomes a frame and a resume
|
|
888
|
+
switch, one that awaits nothing a plain function, and `export async function
|
|
889
|
+
main` runs to its end, then finishes every task it spawned. Nothing waits on
|
|
890
|
+
sockets or timers in this release, so every task runs to completion.
|
|
891
|
+
`--emit-ts` writes it as Node async/await, where `spawn`ed tasks resume in
|
|
892
|
+
Node's order, not the native one. A call
|
|
893
|
+
that can fail answers `Promise<Result<T, E>>`: test the `Result` after the
|
|
894
|
+
`await`. The rules:
|
|
895
|
+
|
|
896
|
+
- `async` goes on a top-level `function` or an arrow, never a method
|
|
897
|
+
(`NL1063`). Its return type is `Promise<T>` (`NL2655`), and `Promise<T>`
|
|
898
|
+
appears nowhere else — not on a parameter, field, local or alias (`NL2654`).
|
|
899
|
+
- **`await` takes a direct call to an `async` function** (`NL2643` for a value,
|
|
900
|
+
`NL2644` for a plain function's call), **at the top of a statement**: alone,
|
|
901
|
+
as a `const`/`let` initializer, after `return`, or right of a plain `=`.
|
|
902
|
+
Anywhere inside a larger expression is `NL2642`, so bind first:
|
|
903
|
+
`const n = await load(1); console.log(n);`.
|
|
904
|
+
- An `async` call that is not awaited is `NL2645`: a promise is never a value.
|
|
905
|
+
No `.then` (`NL2650`), no `Promise.all` (`NL2649`), no `for await`
|
|
906
|
+
(`NL2133`).
|
|
907
|
+
- Several at once: `import { awaitAll, spawn } from "nish:async"`, then
|
|
908
|
+
`const [a, b] = await awaitAll(f(x), g(y))` (two to eight calls, one name
|
|
909
|
+
each, `NL2647`). `spawn(f(x))` starts a task without waiting; `f` answers
|
|
910
|
+
`void` or a `Result` (`NL2646`).
|
|
911
|
+
- No recursion through `await`, direct or mutual (`NL2648`): write a loop.
|
|
912
|
+
- An `async` arrow goes only to a parameter whose function type answers
|
|
913
|
+
`Promise<T>` (`NL2639`), and only an `async` one goes there (`NL2640`).
|
|
914
|
+
- No `async` under a wasm target or `--profile wasi` (`NL2652`).
|
|
915
|
+
- `spawn(f(x))` runs `f` at once, until it first waits on the outside world,
|
|
916
|
+
before it returns; Node runs `f` only to its first `await` and the rest
|
|
917
|
+
after the spawning task yields. Do not rely on either order.
|
|
918
|
+
|
|
919
|
+
```ts nish:err NL2642
|
|
920
|
+
async function load(id: i32): Promise<i32> {
|
|
921
|
+
return id;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
async function run(): Promise<void> {
|
|
925
|
+
console.log(await load(1));
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
export const main = (): i32 => 0;
|
|
929
|
+
```
|
|
930
|
+
|
|
876
931
|
### Data parallelism: `nish/threads`
|
|
877
932
|
|
|
878
933
|
`parallelMapInto(src, dst, f)` writes `f(src[i])` into `dst[i]`, and
|
|
@@ -2243,7 +2298,8 @@ backlog)`, `tcpAccept(fd, peer)`, `netRead(fd, buf, off, len)`,
|
|
|
2243
2298
|
`i32`: **a negative number is a failure, not an exception** — `-11` means "would
|
|
2244
2299
|
block, call again", `-32` a peer that has gone (never SIGPIPE), `-98` the port
|
|
2245
2300
|
is in use, `-111` refused, `-22` a bad argument. Every socket is non-blocking,
|
|
2246
|
-
and there is no
|
|
2301
|
+
and there is no callback, and an `async` function cannot wait on a socket yet
|
|
2302
|
+
(its tasks run on an in-process queue): your `main` is the loop. A client calls
|
|
2247
2303
|
`tcpConnect(addr)`, which answers the descriptor while it is still connecting;
|
|
2248
2304
|
wait for it to be writable, then `connectResult(fd)` is 0 or the failure. Buffers
|
|
2249
2305
|
are `u8[]` only; an address is 18 bytes of one (IPv4 as `::ffff:a.b.c.d`, then
|
|
@@ -2254,6 +2310,9 @@ To fetch a URL from a CLI, skip all of this: `nish/net/fetch`'s
|
|
|
2254
2310
|
response's head is in and answers it with `body.readAll(limit)`, or null on a
|
|
2255
2311
|
failure (a `FetchSyncClient` keeps the connection and says why in `error`).
|
|
2256
2312
|
`netRead` / `netWrite` panic on a range outside the buffer.
|
|
2313
|
+
`netWrite2(fd, a, aOff, aLen, b, bOff, bLen)` writes two ranges in one
|
|
2314
|
+
`sendmsg` (a header and a body without a copy) and answers the bytes written,
|
|
2315
|
+
which may stop inside either range; it checks both ranges, the first one first.
|
|
2257
2316
|
`tcpListenFlags(host, port, backlog, flags)` is `tcpListen` with flag 1
|
|
2258
2317
|
`SO_REUSEPORT`, so several listeners, in one process or many, share a port (Linux
|
|
2259
2318
|
spreads the connections), and flag 2 `TCP_NODELAY` on every socket `tcpAccept`
|
|
@@ -2796,13 +2855,16 @@ or `PG_LOST` to `fail(run)` (on a spent handle these do nothing and count in
|
|
|
2796
2855
|
`stray`), and after each call `next(run)` until
|
|
2797
2856
|
`PG_BATCH_NONE`: `PG_BATCH_ROW` (the connection's row answers request `tag`),
|
|
2798
2857
|
then one of `PG_BATCH_DONE`, `PG_BATCH_MISSING` or `PG_BATCH_FAILED` per
|
|
2799
|
-
request.
|
|
2858
|
+
request. `sorted = false` drops `q.ord` from the statement's `order by` (and
|
|
2859
|
+
its Sort node, with `orderBy` empty): rows route as they come, and every last
|
|
2860
|
+
event waits for `end`. The batcher reads no clock: when to take is the loop's choice, or a `PgBatchClock`'s (`PgBatchClockConfig`: `minCount`, `factor` × the moving average of batch sizes, `maxWaitUs`, the idle bypass `lightCount`; defaults are a fixed window), which `takeDue(conn, clock, nowUs, pool.inFlight())` consults after `clock.arrived(nowUs)` per add; `PgBatchTurns` wakes one collecting connection only when its batch is due. (No block;
|
|
2800
2861
|
`tests/postgres/live`'s `batch` scenario runs it against a server, and the user
|
|
2801
2862
|
guide's [PostgreSQL page](guide/using-nish/postgres.md#batching-keyed-reads)
|
|
2802
2863
|
its walk-through.)
|
|
2803
2864
|
|
|
2804
2865
|
**Call another service from a route handler** — there is no `await fetch()`
|
|
2805
|
-
in a handler and no event-loop
|
|
2866
|
+
in a handler (an `async` task does not wait on a socket yet) and no event-loop
|
|
2867
|
+
callback: `nish/net/upstream`'s `Upstream(h,
|
|
2806
2868
|
origin, limits)` is a pool of keep-alive connections to one `http:` origin on
|
|
2807
2869
|
the `HttpHandler`'s own loop. `users.get(req, path)` returns at once — test it
|
|
2808
2870
|
for `UPSTREAM_E_FULL`, which holds nothing — and keeps `req` held; after each
|
|
@@ -2857,9 +2919,10 @@ export const main = (): i32 => {
|
|
|
2857
2919
|
|
|
2858
2920
|
**Optional parameters** — write a default instead: `(n: i32 = 1)`. A default
|
|
2859
2921
|
is a literal, an enum member or an earlier parameter, and the call site fills
|
|
2860
|
-
it in.
|
|
2861
|
-
|
|
2862
|
-
`
|
|
2922
|
+
it in. An options argument is `(opts: Options | null = null)`: `null` is the
|
|
2923
|
+
one default of a `T | null` parameter. `x?: T` is refused. A variable count
|
|
2924
|
+
of one type is a rest parameter, `(...ns: i32[])`, last in the list; a call
|
|
2925
|
+
never spreads (`f(...xs)`), it passes `xs` to an array parameter.
|
|
2863
2926
|
|
|
2864
2927
|
**A calendar, regex, JSON, threads of your own** — not in the language (the
|
|
2865
2928
|
wall clock is `Date.now()`, and that is all of `Date`). Say so rather than
|
package/package.json
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// The declarations of `runtime/async.mjs`, `nish:async` under Node.
|
|
2
|
+
|
|
3
|
+
/** Starts `task` without waiting for it; the launcher waits before it exits. */
|
|
4
|
+
export declare function spawn(task: Promise<void>): void;
|
|
5
|
+
/** Every task's answer, in the order the calls are written, once all of them finish. */
|
|
6
|
+
export declare function awaitAll<A, B>(a: Promise<A>, b: Promise<B>): Promise<[A, B]>;
|
|
7
|
+
export declare function awaitAll<A, B, C>(a: Promise<A>, b: Promise<B>, c: Promise<C>): Promise<[A, B, C]>;
|
|
8
|
+
export declare function awaitAll<A, B, C, D>(
|
|
9
|
+
a: Promise<A>,
|
|
10
|
+
b: Promise<B>,
|
|
11
|
+
c: Promise<C>,
|
|
12
|
+
d: Promise<D>,
|
|
13
|
+
): Promise<[A, B, C, D]>;
|
|
14
|
+
export declare function awaitAll<A, B, C, D, E>(
|
|
15
|
+
a: Promise<A>,
|
|
16
|
+
b: Promise<B>,
|
|
17
|
+
c: Promise<C>,
|
|
18
|
+
d: Promise<D>,
|
|
19
|
+
e: Promise<E>,
|
|
20
|
+
): Promise<[A, B, C, D, E]>;
|
|
21
|
+
export declare function awaitAll<A, B, C, D, E, F>(
|
|
22
|
+
a: Promise<A>,
|
|
23
|
+
b: Promise<B>,
|
|
24
|
+
c: Promise<C>,
|
|
25
|
+
d: Promise<D>,
|
|
26
|
+
e: Promise<E>,
|
|
27
|
+
f: Promise<F>,
|
|
28
|
+
): Promise<[A, B, C, D, E, F]>;
|
|
29
|
+
export declare function awaitAll<A, B, C, D, E, F, G>(
|
|
30
|
+
a: Promise<A>,
|
|
31
|
+
b: Promise<B>,
|
|
32
|
+
c: Promise<C>,
|
|
33
|
+
d: Promise<D>,
|
|
34
|
+
e: Promise<E>,
|
|
35
|
+
f: Promise<F>,
|
|
36
|
+
g: Promise<G>,
|
|
37
|
+
): Promise<[A, B, C, D, E, F, G]>;
|
|
38
|
+
export declare function awaitAll<A, B, C, D, E, F, G, H>(
|
|
39
|
+
a: Promise<A>,
|
|
40
|
+
b: Promise<B>,
|
|
41
|
+
c: Promise<C>,
|
|
42
|
+
d: Promise<D>,
|
|
43
|
+
e: Promise<E>,
|
|
44
|
+
f: Promise<F>,
|
|
45
|
+
g: Promise<G>,
|
|
46
|
+
h: Promise<H>,
|
|
47
|
+
): Promise<[A, B, C, D, E, F, G, H]>;
|
|
48
|
+
/** Waits for every task `spawn` started, the tasks those tasks start included. */
|
|
49
|
+
export declare function settleTasks(): Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// `nish:async` under Node (WP49): what `nish --emit-ts` writes an async
|
|
2
|
+
// program against, and what `runtime/nish.mjs` answers `nish:async` with. A
|
|
3
|
+
// file of its own, not `nish/runtime`, because `nish:process` has a `spawn`
|
|
4
|
+
// there already.
|
|
5
|
+
//
|
|
6
|
+
// `awaitAll` is `Promise.all` over its two to eight tasks. `spawn` starts a
|
|
7
|
+
// task without awaiting it and keeps it, so the launcher's `settleTasks` waits
|
|
8
|
+
// for every task started before the process exits, as the native driver
|
|
9
|
+
// drains its queue after `main` answers. Node runs a task only until its first
|
|
10
|
+
// `await` where native runs it until it first waits, so spawned tasks may print
|
|
11
|
+
// in another order (`docs/RUN_UNDER_NODE.md`). A task that throws is Node's
|
|
12
|
+
// unhandled rejection, which exits 1 as a native panic does.
|
|
13
|
+
|
|
14
|
+
const pending = [];
|
|
15
|
+
|
|
16
|
+
export const spawn = (task) => {
|
|
17
|
+
pending.push(task);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const awaitAll = (...tasks) => Promise.all(tasks);
|
|
21
|
+
|
|
22
|
+
export const settleTasks = async () => {
|
|
23
|
+
for (let i = 0; i < pending.length; i++) {
|
|
24
|
+
await pending[i];
|
|
25
|
+
}
|
|
26
|
+
pending.length = 0;
|
|
27
|
+
};
|
package/runtime/nish.d.ts
CHANGED
|
@@ -444,6 +444,16 @@ declare function tcpAccept(fd: i32, peer: u8[]): i32;
|
|
|
444
444
|
declare function netRead(fd: i32, buf: u8[], off: i32, len: i32): i32;
|
|
445
445
|
/** Up to `len` bytes of `buf` from `off`: the count; a gone peer is -32, never SIGPIPE. */
|
|
446
446
|
declare function netWrite(fd: i32, buf: readonly u8[], off: i32, len: i32): i32;
|
|
447
|
+
/** `netWrite` of `a`'s range then `b`'s, in one system call: the count of both, at most `aLen + bLen`. */
|
|
448
|
+
declare function netWrite2(
|
|
449
|
+
fd: i32,
|
|
450
|
+
a: readonly u8[],
|
|
451
|
+
aOff: i32,
|
|
452
|
+
aLen: i32,
|
|
453
|
+
b: readonly u8[],
|
|
454
|
+
bOff: i32,
|
|
455
|
+
bLen: i32
|
|
456
|
+
): i32;
|
|
447
457
|
/** Shut the read side (0), the write side (1) or both (2). */
|
|
448
458
|
declare function netShutdown(fd: i32, how: i32): i32;
|
|
449
459
|
/** Close the descriptor. */
|
|
@@ -723,6 +733,16 @@ declare module "nish:net" {
|
|
|
723
733
|
export function netRead(fd: i32, buf: u8[], off: i32, len: i32): i32;
|
|
724
734
|
/** Up to `len` bytes of `buf` from `off`: the count; a gone peer is -32, never SIGPIPE. */
|
|
725
735
|
export function netWrite(fd: i32, buf: readonly u8[], off: i32, len: i32): i32;
|
|
736
|
+
/** `netWrite` of `a`'s range then `b`'s, in one system call: the count of both, at most `aLen + bLen`. */
|
|
737
|
+
export function netWrite2(
|
|
738
|
+
fd: i32,
|
|
739
|
+
a: readonly u8[],
|
|
740
|
+
aOff: i32,
|
|
741
|
+
aLen: i32,
|
|
742
|
+
b: readonly u8[],
|
|
743
|
+
bOff: i32,
|
|
744
|
+
bLen: i32
|
|
745
|
+
): i32;
|
|
726
746
|
/** Shut the read side (0), the write side (1) or both (2). */
|
|
727
747
|
export function netShutdown(fd: i32, how: i32): i32;
|
|
728
748
|
export function netClose(fd: i32): i32;
|
|
@@ -1059,6 +1079,58 @@ declare module "nish:region" {
|
|
|
1059
1079
|
export function free<T>(r: Region<T>): void;
|
|
1060
1080
|
}
|
|
1061
1081
|
|
|
1082
|
+
// `nish:async` (WP49, docs/LANGUAGE.md -> Async functions). `tsc` sees ordinary
|
|
1083
|
+
// promises; what `nish` adds is that each argument is a direct call to an
|
|
1084
|
+
// `async` function, written where it is made, which the checker holds it to
|
|
1085
|
+
// (`src/async-check.ts`), and that `awaitAll` takes two to eight of them.
|
|
1086
|
+
declare module "nish:async" {
|
|
1087
|
+
/** Start `task` and go on without its answer; it runs until it first waits. */
|
|
1088
|
+
export function spawn(task: Promise<void>): void;
|
|
1089
|
+
/** Await every call at once; the answers come in the order the calls are written. */
|
|
1090
|
+
export function awaitAll<A, B>(a: Promise<A>, b: Promise<B>): Promise<[A, B]>;
|
|
1091
|
+
export function awaitAll<A, B, C>(a: Promise<A>, b: Promise<B>, c: Promise<C>): Promise<[A, B, C]>;
|
|
1092
|
+
export function awaitAll<A, B, C, D>(
|
|
1093
|
+
a: Promise<A>,
|
|
1094
|
+
b: Promise<B>,
|
|
1095
|
+
c: Promise<C>,
|
|
1096
|
+
d: Promise<D>,
|
|
1097
|
+
): Promise<[A, B, C, D]>;
|
|
1098
|
+
export function awaitAll<A, B, C, D, E>(
|
|
1099
|
+
a: Promise<A>,
|
|
1100
|
+
b: Promise<B>,
|
|
1101
|
+
c: Promise<C>,
|
|
1102
|
+
d: Promise<D>,
|
|
1103
|
+
e: Promise<E>,
|
|
1104
|
+
): Promise<[A, B, C, D, E]>;
|
|
1105
|
+
export function awaitAll<A, B, C, D, E, F>(
|
|
1106
|
+
a: Promise<A>,
|
|
1107
|
+
b: Promise<B>,
|
|
1108
|
+
c: Promise<C>,
|
|
1109
|
+
d: Promise<D>,
|
|
1110
|
+
e: Promise<E>,
|
|
1111
|
+
f: Promise<F>,
|
|
1112
|
+
): Promise<[A, B, C, D, E, F]>;
|
|
1113
|
+
export function awaitAll<A, B, C, D, E, F, G>(
|
|
1114
|
+
a: Promise<A>,
|
|
1115
|
+
b: Promise<B>,
|
|
1116
|
+
c: Promise<C>,
|
|
1117
|
+
d: Promise<D>,
|
|
1118
|
+
e: Promise<E>,
|
|
1119
|
+
f: Promise<F>,
|
|
1120
|
+
g: Promise<G>,
|
|
1121
|
+
): Promise<[A, B, C, D, E, F, G]>;
|
|
1122
|
+
export function awaitAll<A, B, C, D, E, F, G, H>(
|
|
1123
|
+
a: Promise<A>,
|
|
1124
|
+
b: Promise<B>,
|
|
1125
|
+
c: Promise<C>,
|
|
1126
|
+
d: Promise<D>,
|
|
1127
|
+
e: Promise<E>,
|
|
1128
|
+
f: Promise<F>,
|
|
1129
|
+
g: Promise<G>,
|
|
1130
|
+
h: Promise<H>,
|
|
1131
|
+
): Promise<[A, B, C, D, E, F, G, H]>;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1062
1134
|
// ---- Arena (docs/LANGUAGE.md -> Arena) ---------------------------------------
|
|
1063
1135
|
|
|
1064
1136
|
declare const Arena: {
|
package/runtime/nish.h
CHANGED
|
@@ -714,8 +714,10 @@ bool nish_is_executable(const nish_str *path);
|
|
|
714
714
|
* `nish_tcp_accept(fd, peer)`: a new descriptor and the peer's address.
|
|
715
715
|
* `nish_net_read` / `nish_net_write(fd, buf, off, len)`: bytes `[off, off +
|
|
716
716
|
* len)` of `buf`, which the compiled call has range-checked; a write to a
|
|
717
|
-
* gone peer is -32, never SIGPIPE. `
|
|
718
|
-
*
|
|
717
|
+
* gone peer is -32, never SIGPIPE. `nish_net_write2(fd, a, aoff, alen, b,
|
|
718
|
+
* boff, blen)`: both ranges in one `sendmsg`, `a`'s bytes first, answering the
|
|
719
|
+
* count written (short when the socket buffer fills) or the same errors.
|
|
720
|
+
* `nish_net_shutdown(fd, how)`: 0 read, 1 write, 2 both. `nish_net_close(fd)`.
|
|
719
721
|
* `nish_net_set_option(fd, option, value)`: one socket option from a
|
|
720
722
|
* whitelist, by the runtime's own id (1 `TCP_NODELAY`, 2 `TCP_QUICKACK`, 3
|
|
721
723
|
* `SO_KEEPALIVE`, 4 `SO_SNDBUF`, 5 `SO_RCVBUF`); any other id is -22, and
|
|
@@ -784,6 +786,8 @@ int32_t nish_tcp_listen_flags(const nish_str *host, int32_t port, int32_t backlo
|
|
|
784
786
|
int32_t nish_tcp_accept(int32_t fd, nish_array *peer);
|
|
785
787
|
int32_t nish_net_read(int32_t fd, nish_array *buf, int64_t off, int64_t len);
|
|
786
788
|
int32_t nish_net_write(int32_t fd, const nish_array *buf, int64_t off, int64_t len);
|
|
789
|
+
int32_t nish_net_write2(int32_t fd, const nish_array *a, int64_t aoff, int64_t alen, const nish_array *b,
|
|
790
|
+
int64_t boff, int64_t blen);
|
|
787
791
|
int32_t nish_net_shutdown(int32_t fd, int32_t how);
|
|
788
792
|
int32_t nish_net_close(int32_t fd);
|
|
789
793
|
int32_t nish_net_set_option(int32_t fd, int32_t option, int32_t value);
|
package/runtime/nish.mjs
CHANGED
|
@@ -139,6 +139,7 @@ for (const name of [
|
|
|
139
139
|
"tcpAccept",
|
|
140
140
|
"netRead",
|
|
141
141
|
"netWrite",
|
|
142
|
+
"netWrite2",
|
|
142
143
|
"netShutdown",
|
|
143
144
|
"netClose",
|
|
144
145
|
"tcpConnect",
|
|
@@ -379,6 +380,9 @@ registerHooks({
|
|
|
379
380
|
if (specifier === "nish:simd") {
|
|
380
381
|
return next(new URL("./core.mjs", import.meta.url).href, context);
|
|
381
382
|
}
|
|
383
|
+
if (specifier === "nish:async") {
|
|
384
|
+
return next(new URL("./async.mjs", import.meta.url).href, context);
|
|
385
|
+
}
|
|
382
386
|
if (specifier === "nish:region") {
|
|
383
387
|
return { url: `data:text/javascript,${encodeURIComponent(regionSource)}`, shortCircuit: true };
|
|
384
388
|
}
|
package/runtime/runtime-net.c
CHANGED
|
@@ -417,6 +417,28 @@ int32_t nish_net_write(int32_t fd, const nish_array *buf, int64_t off, int64_t l
|
|
|
417
417
|
return n < 0 ? nish_net_fail() : (int32_t)n;
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
/* `netWrite2(fd, a, aoff, alen, b, boff, blen)`: `netWrite` of two ranges in
|
|
421
|
+
one `sendmsg`, so a response head and its body leave in one system call
|
|
422
|
+
without being copied together first. Both ranges are checked by the
|
|
423
|
+
compiled call. The count is at most `alen + blen`; a sum past `INT32_MAX`
|
|
424
|
+
is offered only up to it, so the answer always fits, and a short write is a
|
|
425
|
+
count, as `netWrite`'s is. */
|
|
426
|
+
int32_t nish_net_write2(int32_t fd, const nish_array *a, int64_t aoff, int64_t alen, const nish_array *b,
|
|
427
|
+
int64_t boff, int64_t blen) {
|
|
428
|
+
struct iovec iov[2];
|
|
429
|
+
struct msghdr msg;
|
|
430
|
+
if (blen > (int64_t)INT32_MAX - alen) blen = (int64_t)INT32_MAX - alen;
|
|
431
|
+
iov[0].iov_base = a->data + aoff;
|
|
432
|
+
iov[0].iov_len = (size_t)alen;
|
|
433
|
+
iov[1].iov_base = b->data + boff;
|
|
434
|
+
iov[1].iov_len = (size_t)blen;
|
|
435
|
+
memset(&msg, 0, sizeof msg);
|
|
436
|
+
msg.msg_iov = iov;
|
|
437
|
+
msg.msg_iovlen = 2;
|
|
438
|
+
ssize_t n = sendmsg(fd, &msg, NISH_NOSIGNAL);
|
|
439
|
+
return n < 0 ? nish_net_fail() : (int32_t)n;
|
|
440
|
+
}
|
|
441
|
+
|
|
420
442
|
/* `netShutdown(fd, how)`: 0 the read side, 1 the write side, 2 both, which
|
|
421
443
|
are `SHUT_RD`, `SHUT_WR` and `SHUT_RDWR` on both platforms. */
|
|
422
444
|
int32_t nish_net_shutdown(int32_t fd, int32_t how) {
|