castrum 0.9.7 → 0.9.9
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/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/castrum.darwin-arm64.node +0 -0
- package/castrum.darwin-x64.node +0 -0
- package/castrum.linux-arm64-gnu.node +0 -0
- package/castrum.linux-arm64-musl.node +0 -0
- package/castrum.linux-x64-gnu.node +0 -0
- package/castrum.linux-x64-musl.node +0 -0
- package/castrum.linux-x64-v3-gnu.node +0 -0
- package/castrum.win32-x64-msvc.node +0 -0
- package/dist/index.d.ts +664 -8
- package/dist/index.js +2014 -1327
- package/docs/BENCHMARKS.md +13 -0
- package/docs/CASE_STUDY.md +1 -1
- package/docs/ENVIRONMENT.md +8 -1
- package/docs/FFI_BUN_GUIDE.md +1 -1
- package/docs/INGRESS.md +4 -4
- package/docs/REPO_MAP.md +7 -5
- package/docs/RND-CONCURRENCY.md +20 -4
- package/docs/superpowers/plans/2026-09-18-castrum-hardening-cleanup.md +617 -0
- package/docs/superpowers/plans/2026-09-18-elysia-learnings-adoption.md +634 -0
- package/index.ts +6 -0
- package/package.json +4 -2
- package/rust/crypto/aead.rs +44 -0
- package/rust/crypto/argon2.rs +32 -2
- package/rust/crypto/jwt/tests.rs +19 -0
- package/rust/crypto/jwt/token.rs +43 -28
- package/rust/crypto/session.rs +6 -0
- package/rust/ffi/crypto.rs +63 -32
- package/rust/ffi/hashing.rs +48 -33
- package/rust/ffi/http.rs +62 -36
- package/rust/ffi/metrics.rs +6 -2
- package/rust/ffi/payload.rs +47 -27
- package/rust/ffi/task.rs +55 -35
- package/rust/ffi/tests.rs +24 -0
- package/rust/ffi/util.rs +40 -0
- package/rust/http/headers.rs +13 -0
- package/rust/http/multipart.rs +139 -0
- package/rust/ingress/api.rs +21 -5
- package/rust/ingress/mod.rs +7 -0
- package/rust/ingress/native_route.rs +25 -5
- package/rust/ingress/schema_cache.rs +266 -0
- package/rust/task/completion.rs +7 -0
- package/rust/task/mod.rs +2 -1
- package/rust/task/runtime.rs +55 -7
- package/rust/task/tests.rs +128 -1
- package/src/ingress/abort.ts +48 -0
- package/src/ingress/body.ts +104 -8
- package/src/ingress/decode/baked-result.ts +13 -5
- package/src/ingress/decode/fast-result.ts +1 -1
- package/src/ingress/decode/result-base.ts +16 -2
- package/src/ingress/handlers.ts +104 -31
- package/src/ingress/headers/memoized-headers.ts +40 -0
- package/src/ingress/health.ts +22 -0
- package/src/ingress/index.ts +3 -1
- package/src/ingress/native-route.ts +25 -6
- package/src/ingress/options.ts +26 -2
- package/src/ingress/packing/gather-raw-headers.ts +24 -8
- package/src/ingress/response/baked-response.ts +118 -28
- package/src/ingress/router.ts +50 -3
- package/src/ingress/routes/common.ts +35 -11
- package/src/ingress/routes/echo.ts +60 -7
- package/src/ingress/routes/json-write.ts +32 -8
- package/src/ingress/routes/native.ts +24 -5
- package/src/ingress/routes/options.ts +6 -2
- package/src/ingress/routes/responder.ts +29 -9
- package/src/ingress/routes/safe-record.ts +57 -0
- package/src/ingress/server-error.ts +111 -0
- package/src/ingress/server-node.ts +135 -37
- package/src/ingress/server.ts +143 -36
- package/src/ingress/shared.ts +1 -1
- package/src/ingress/types.ts +15 -0
- package/src/integration/pipeline.ts +6 -0
- package/src/loader/ops.ts +1 -1
- package/src/native/ffi/build/codecs-selftest.ts +305 -0
- package/src/native/ffi/build/codecs.ts +0 -296
- package/src/native/ffi/build/instances-selftest.ts +264 -0
- package/src/native/ffi/build/instances.ts +10 -259
- package/src/native/ffi/build/parse-selftest.ts +238 -0
- package/src/native/ffi/build/parse.ts +11 -234
- package/src/native/ffi/build/task.ts +2 -1
- package/src/native/ffi/build.ts +5 -1
- package/src/native/ffi/selftest.ts +3 -3
- package/src/native/ffi/symbols.ts +460 -0
- package/src/native/ffi/types.ts +6 -1
- package/src/native/ffi.ts +2 -434
- package/src/native/ingress-binding.ts +39 -0
- package/src/native/types.ts +6 -0
- package/src/runtime/codec.ts +24 -0
- package/src/runtime/detect.ts +14 -0
- package/src/runtime/types.ts +11 -0
- package/src/rust-ffi/batch/build.ts +18 -8
- package/src/rust-ffi/batch/types.ts +15 -0
- package/src/rust-ffi/context.ts +46 -3
- package/src/rust-ffi/options.ts +19 -0
- package/src/rust-ffi/scalar/crypto.ts +8 -0
- package/src/rust-ffi/scalar/factories.ts +53 -20
- package/src/rust-ffi/scalar/http.ts +60 -10
- package/src/rust-ffi/scalar/interface.ts +18 -1
- package/src/rust-ffi/scalar/payload.ts +27 -1
- package/src/rust-ffi/text.ts +5 -4
- package/src/shared/buffer-pool.ts +54 -9
- package/src/shared/codec.ts +26 -0
- package/src/shared/memory.ts +68 -0
- package/src/shared/metrics.ts +86 -3
- package/src/shared/packed/parsers.ts +64 -7
- package/src/shared/runtime.ts +7 -0
- package/src/task/runtime.ts +81 -37
package/docs/BENCHMARKS.md
CHANGED
|
@@ -162,6 +162,19 @@ Current medians (static path, 2000 connections, pipelining 1, single instance):
|
|
|
162
162
|
per-request `ReadableStream` construction is the cost, not the native body
|
|
163
163
|
write. Both servers default to copy mode; `INGRESS_ZERO_COPY=1` opts back in
|
|
164
164
|
for large-payload deployments.
|
|
165
|
+
- **Response construction dominates the JS-side cost — and is now memoized.**
|
|
166
|
+
A Bun CPU profile of the ingress route under load attributed ~80% of sampled
|
|
167
|
+
JS time to `new Response(...)` (the whole `run()` ~7%, the FFI pipeline call
|
|
168
|
+
~4%): passing an array-of-pairs makes Bun re-parse every header on every
|
|
169
|
+
response, measured **1832 ns vs 398 ns** for a persistent `Headers` instance
|
|
170
|
+
with the full security + CORS set. `buildSuccessInit` and the terminal
|
|
171
|
+
builders now pass a memoized `Headers`
|
|
172
|
+
(`src/ingress/headers/memoized-headers.ts`, keyed by the header array's
|
|
173
|
+
identity; the baked templates and per-origin cache return the same array
|
|
174
|
+
across requests, fresh per-request arrays are never retained). Interleaved
|
|
175
|
+
autocannon A/B (static `GET /api/users`, 8 workers, 2000 connections, three
|
|
176
|
+
independent runs): **+14.3% / +15.6% / +23.1% median RPS**, p50 −15%,
|
|
177
|
+
p99 −9…−24%, 0 errors, wire output byte-identical.
|
|
165
178
|
- Per-request cost breakdown (`bun bench/cost/router-cost.ts` +
|
|
166
179
|
`router-minimal-breakdown.ts`, min-of-5): the router's minimal route `run()`
|
|
167
180
|
is ~310ns (native pruned pipeline ~115ns + response/run machinery ~150ns +
|
package/docs/CASE_STUDY.md
CHANGED
|
@@ -58,7 +58,7 @@ The addon is built with napi-rs (so Node can load it), but it *also* exports
|
|
|
58
58
|
|
|
59
59
|
```mermaid
|
|
60
60
|
flowchart LR
|
|
61
|
-
TS["TypeScript (index.ts)"] -->|"bun:ffi ·
|
|
61
|
+
TS["TypeScript (index.ts)"] -->|"bun:ffi · 121 C-ABI symbols · ~10–20 ns"| RUST["Rust cdylib (rust/ffi/)"]
|
|
62
62
|
TS -.->|"NAPI fallback (Node / self-test failure)"| RUST
|
|
63
63
|
RUST --> PIPELINE["8-stage ingress pipeline"]
|
|
64
64
|
TS --> PACKED["Packed, length-prefixed buffers (zero-copy)"]
|
package/docs/ENVIRONMENT.md
CHANGED
|
@@ -10,7 +10,7 @@ warned about and replaced with the documented default.
|
|
|
10
10
|
|----------|------|---------|-------------|
|
|
11
11
|
| `INGRESS_TRUST_PROXY` | bool | `false` | **Opt-in.** When `true`, honors `X-Forwarded-For` / `X-Real-IP` for client-IP resolution. Keep **off** unless the server sits behind a trusted edge that strips these headers, otherwise clients can spoof arbitrary IPs for IP-based rate limiting. |
|
|
12
12
|
| `INGRESS_REQUEST_ID_HEADER` | bool | `false` | Emit an `x-request-id` response header per request. The request ID is always generated internally; this only controls whether it is echoed to clients. |
|
|
13
|
-
| `INGRESS_ZERO_COPY` | bool | `false` | Enable zero-copy response bodies (legacy alias `INGRESS_UNSAFE_ZERO_COPY` still honored).
|
|
13
|
+
| `INGRESS_ZERO_COPY` | bool | `false` | Enable zero-copy response bodies (legacy alias `INGRESS_UNSAFE_ZERO_COPY` still honored). Off by default; when on, the handler serves a pooled output buffer via `pooledBodyResponse` (per-response output buffers are implemented — `src/shared/buffer-pool.ts`, `baked-response.ts::zeroCopyResponse`). Bound the borrowed set with `maxInFlight`. |
|
|
14
14
|
| `INGRESS_ZERO_COPY_MAX_IN_FLIGHT` | int | `128` | Max pooled output buffers kept in flight for zero-copy responses (min `1`). |
|
|
15
15
|
| `INGRESS_ZERO_COPY_TIMEOUT_MS` | int | `1000` | Timeout before a zero-copy pooled buffer is force-released (min `0`). |
|
|
16
16
|
| `INGRESS_OUTPUT_BUF_BYTES` | int | `131072` | Size of the Rust output buffer (min `65536`). If cookies + query + metadata JSON exceed this, `FLAG_BODY_TRUNCATED` is set and the request fails closed. |
|
|
@@ -38,8 +38,15 @@ for false; anything else is warned about and falls back to the default.
|
|
|
38
38
|
| Variable | Type | Default | Description |
|
|
39
39
|
|----------|------|---------|-------------|
|
|
40
40
|
| `CASTRUM_TASK_THREADS` | int | `max(1, cores-1)` | Worker threads for the off-thread task runtime (castrum Tasks). Read when the pool first starts — the pool is process-wide and cannot be resized afterwards (`tasks.stats().threads` reports the live count). |
|
|
41
|
+
| `CASTRUM_TASK_QUEUE_MAX` | int | `4096` | Admission bound on the off-thread task work queue. Read when the pool first starts (minimum 1). A submit that would exceed it is rejected rather than growing memory: the native submit returns `2` and the task promise rejects with a typed `OVERLOADED` error. |
|
|
41
42
|
| `CASTRUM_TASK_PIN_CORES` | (presence) | — | When set (Linux), pin each task worker to its own core (core 0 is skipped so the JS event loop keeps it). `CASTRUM_PIN_CORES` enables it too. Off by default: pinning competes with the JS/rayon threads for the same box. |
|
|
42
43
|
|
|
44
|
+
## Compiled-schema cache (`rust/ingress/schema_cache.rs`)
|
|
45
|
+
|
|
46
|
+
| Variable | Type | Default | Description |
|
|
47
|
+
|----------|------|---------|-------------|
|
|
48
|
+
| `CASTRUM_SCHEMA_CACHE_MAX` | int | `128` | Max distinct request-body schemas retained process-wide. `Ingress`/`NativeRoute` share one compiled `IngressSchema` per distinct schema byte string; this bounds that LRU. Read at first schema compile (the cache is process-wide and cannot be resized afterwards). Values below 1 (unset, unparseable, `0`) fall back to the default; values are clamped to `4096`. Call `flushMemory()` to drop the cache explicitly. |
|
|
49
|
+
|
|
43
50
|
## Native transport selection (`src/native/ffi.ts`)
|
|
44
51
|
|
|
45
52
|
`bun:ffi` is the PRIMARY transport under Bun; NAPI is the fallback (Node,
|
package/docs/FFI_BUN_GUIDE.md
CHANGED
|
@@ -492,7 +492,7 @@ a type error, not a behavior change.
|
|
|
492
492
|
**Verify after any FFI change**:
|
|
493
493
|
|
|
494
494
|
```bash
|
|
495
|
-
nm -D --defined-only <addon> | grep -c castrum_ # = 109
|
|
495
|
+
nm -D --defined-only <addon> | grep -c castrum_ # = 121 (109 direct extern fns)
|
|
496
496
|
bun run bench:http:smoke # after touching decoders/handlers
|
|
497
497
|
bun test test/unit/native/ffi.test.ts # FFI↔napi parity + self-test
|
|
498
498
|
bun test test/unit/native/ffi-symbol-parity.test.ts
|
package/docs/INGRESS.md
CHANGED
|
@@ -415,14 +415,14 @@ interface SyncIngressHandler {
|
|
|
415
415
|
|
|
416
416
|
| Option | Type | Default | Description |
|
|
417
417
|
|--------|------|---------|-------------|
|
|
418
|
-
| `trustProxy` | `boolean` | `false` | Trust `X-Forwarded-For` and `X-Real-IP` headers |
|
|
418
|
+
| `trustProxy` | `boolean` | `false` | Trust `X-Forwarded-For` and `X-Real-IP` headers. Duplicate `X-Forwarded-For` lines: only the **last** is read (warning in `rust/http/headers.rs`) |
|
|
419
419
|
| `trustedProxies` | `{ enabled, networks? }` | — | Fine-grained proxy trust with optional network whitelist |
|
|
420
420
|
| `parseCookies` | `boolean` | `false` | Parse `Cookie` header into structured JSON |
|
|
421
421
|
| `parseQuery` | `boolean` | `false` | Parse URL query string into structured JSON |
|
|
422
422
|
| `requireJsonBody` | `boolean` | `false` | Require body to be valid JSON |
|
|
423
423
|
| `schema` | `Uint8Array` | — | JSON Schema (as UTF-8 bytes) for body validation |
|
|
424
|
-
| `cors` | `CorsOptions` | — | CORS configuration |
|
|
425
|
-
| `rateLimit` | `{ limit, windowMs, maxEntries }` | — | Rate limiting configuration |
|
|
424
|
+
| `cors` | `CorsOptions` | — | CORS configuration. A present-but-empty `{}` (no `allowOrigin`) resolves to wildcard allow-all |
|
|
425
|
+
| `rateLimit` | `{ limit, windowMs, maxEntries }` | — | Rate limiting configuration. Without a `getIp` resolver every client shares ONE bucket (warns once at router construction) |
|
|
426
426
|
| `security` | `SecurityHeadersOptions` | — | Security headers configuration |
|
|
427
427
|
| `https` | `boolean` | — | Force HTTPS detection (overrides auto-detection) |
|
|
428
428
|
| `maxBodyBytes` | `number` | 1,048,576 | Maximum body size (1 MB) |
|
|
@@ -438,7 +438,7 @@ interface SyncIngressHandler {
|
|
|
438
438
|
|
|
439
439
|
| Option | Type | Default | Description |
|
|
440
440
|
|--------|------|---------|-------------|
|
|
441
|
-
| `allowOrigin` | `string[]` | — | Allowed origins
|
|
441
|
+
| `allowOrigin` | `string[]` | — | Allowed origins. Absent, empty, or `["*"]` = wildcard allow-all; list exact origins (or patterns) to narrow |
|
|
442
442
|
| `allowMethods` | `string[]` | `["GET", "HEAD", "POST"]` | Allowed HTTP methods |
|
|
443
443
|
| `allowHeaders` | `string[]` | — | Allowed request headers |
|
|
444
444
|
| `exposeHeaders` | `string[]` | — | Exposed response headers |
|
package/docs/REPO_MAP.md
CHANGED
|
@@ -37,11 +37,11 @@ Bun is the primary runtime; Node.js ≥20.3 is supported via a compiled ESM entr
|
|
|
37
37
|
| Build Rust addon (LOCAL max perf) | `bun run build:perf` | x86-64-v3 + AVX2 — **never for publish** |
|
|
38
38
|
| Build Rust addon (debug) | `bun run build:debug` | |
|
|
39
39
|
| Build compiled JS entry (Node) | `bun run build:js` | bundle + types → `dist/` (gitignored) |
|
|
40
|
-
| TS unit tests | `bun test` |
|
|
41
|
-
| Rust unit tests | `bun run test:rust` | `cargo test`,
|
|
40
|
+
| TS unit tests | `bun test test/unit test/property test/compat` | 838 tests (paths are explicit — a bare `bun test` pulls in the Node `.mjs` suites that need `dist/`) |
|
|
41
|
+
| Rust unit tests | `bun run test:rust` | `cargo test`, 628 tests |
|
|
42
42
|
| Node smoke tests | `bun run test:node` | explicit `.mjs` paths (Node 24 rejects dir arg) |
|
|
43
43
|
| Installed-tarball e2e | `bun run verify:install` | pack → install → import from `node_modules` |
|
|
44
|
-
| Typecheck | `bun run typecheck` | `bunx tsc --noEmit` (
|
|
44
|
+
| Typecheck | `bun run typecheck` | `bunx tsc --noEmit` (`index.ts`, `bench.ts`, `src/`, `bench/`) |
|
|
45
45
|
| CPU benchmark + correctness | `bun run check` | == `bun bench.ts`; writes `bench/results/cpu/` |
|
|
46
46
|
| HTTP bench (all servers) | `bun run bench:http` | |
|
|
47
47
|
| HTTP smoke (fast sanity) | `bun run bench:http:smoke` | **the wire-format guard** (CI-gated) |
|
|
@@ -131,7 +131,7 @@ src/
|
|
|
131
131
|
shared.ts JS constants + buildHeaderPlan + assertSyncCallback (shared by both paths).
|
|
132
132
|
constants.ts Binary-layout constants read from Rust at runtime (SINGLE SOURCE).
|
|
133
133
|
status.ts / errors.ts / options.ts / types.ts / body.ts / context.ts
|
|
134
|
-
packing/ header-packing.ts, input-packer.ts, gather-raw-headers.ts
|
|
134
|
+
packing/ header-packing.ts, input-packer.ts, gather-raw-headers.ts, select-headers.ts, scratch.ts
|
|
135
135
|
headers/ cors.ts, hsts.ts, security.ts (baked security merge), fast-templates.ts, baked-templates.ts
|
|
136
136
|
decode/ fast-result.ts, baked-result.ts (TWO decoders — see §4.2)
|
|
137
137
|
response/ terminal.ts, error-bodies.ts, baked-response.ts (pre-baked response builders)
|
|
@@ -294,7 +294,9 @@ consumed.
|
|
|
294
294
|
- **Multi-platform (recommended)**: push a `v*` tag → `.github/workflows/ci.yml`
|
|
295
295
|
builds each platform addon, the `publish` job downloads them into
|
|
296
296
|
`./artifacts`, stages them, and runs `npm publish` (npm Trusted Publishing /
|
|
297
|
-
OIDC — no token
|
|
297
|
+
OIDC — preferred, no long-lived token; the job also targets the `NPM_TOKEN`
|
|
298
|
+
environment as a fallback secret. Needs the Trusted Publisher configured on
|
|
299
|
+
npmjs.com).
|
|
298
300
|
- **Manual single-platform**: `bun run release:manual`
|
|
299
301
|
(syncs package.json ↔ Cargo.toml ↔ CHANGELOG, tags, builds, publishes with
|
|
300
302
|
`CASTRUM_PUBLISH_ALLOW_PARTIAL=1`). `--dry-run` plans only.
|
package/docs/RND-CONCURRENCY.md
CHANGED
|
@@ -69,7 +69,8 @@ documented `JSTypedArrayBytesDeallocator` hook crashes the process (fault at
|
|
|
69
69
|
|
|
70
70
|
```
|
|
71
71
|
rust/task/runtime.rs condvar pool, N = cores−1 (CASTRUM_TASK_THREADS), auto-start on submit,
|
|
72
|
-
batch dequeue + bounded spin-then-park, optional core pinning
|
|
72
|
+
batch dequeue + bounded spin-then-park, optional core pinning,
|
|
73
|
+
HARD admission bound on the queue (CASTRUM_TASK_QUEUE_MAX)
|
|
73
74
|
rust/task/completion.rs result ring + ARMED coalescing doorbell + needed-size drain layout
|
|
74
75
|
rust/task/ops.rs op dispatch (header + payload split), cancellation set, catch_unwind
|
|
75
76
|
containment, `_into` (zero-copy output) and slice (zero-copy input) forms
|
|
@@ -121,6 +122,17 @@ right one is picked per op:
|
|
|
121
122
|
copied and the payload is read in place, so a large argument never crosses the
|
|
122
123
|
boundary. This one was forced by a measurement, not symmetry (§7b).
|
|
123
124
|
|
|
125
|
+
**Bounded admission (backpressure).** The work queue is HARD-bounded by
|
|
126
|
+
`CASTRUM_TASK_QUEUE_MAX` (default 4096, minimum 1; read once when the pool is
|
|
127
|
+
created). A submit that would exceed the bound is rejected instead of growing
|
|
128
|
+
memory: `castrum_task_submit{,_out,_slice}` return `2 = overloaded`, and the JS
|
|
129
|
+
runtime rejects the task promise with a typed `code: 'OVERLOADED'` error. The
|
|
130
|
+
existing return meanings are unchanged — `1` accepted, `0` invalid args / panic.
|
|
131
|
+
A rejected zero-copy submit drops its `output`/`keep` reference exactly like any
|
|
132
|
+
other rejection, so a full queue cannot leak buffers. (The richer M2 design — a
|
|
133
|
+
`WouldBlock` + re-queue-behind-drain policy and priority levels — remains a
|
|
134
|
+
later milestone; v1 is a hard cap with a distinct overload outcome.)
|
|
135
|
+
|
|
124
136
|
**Measured comparison** (`bun run bench:task`, Bun 1.4.2, 12 cores):
|
|
125
137
|
|
|
126
138
|
| variant | time | loop stall | note |
|
|
@@ -466,9 +478,12 @@ rust/task/
|
|
|
466
478
|
as in rayon/Go. Deliberately **separate from the rayon global pool**: rayon
|
|
467
479
|
stays the batch executor for `par_iter`; the task runtime owns offloaded ops.
|
|
468
480
|
`CASTRUM_TASK_THREADS` sizes it (default `cores − 1`).
|
|
469
|
-
- **Admission/backpressure
|
|
470
|
-
|
|
471
|
-
|
|
481
|
+
- **Admission/backpressure** (v1 shipped): the queue is hard-bounded by
|
|
482
|
+
`CASTRUM_TASK_QUEUE_MAX` (default 4096, min 1, read once at pool creation).
|
|
483
|
+
`submit` returns `false` when full; the C ABI reports `2 = overloaded` and JS
|
|
484
|
+
rejects that promise with a typed `OVERLOADED` error — no unbounded growth
|
|
485
|
+
(fixes F6). M2 replaces this hard cap with `WouldBlock` + requeue-behind-drain
|
|
486
|
+
and priority levels; v1 has neither.
|
|
472
487
|
- **Cancellation**: each task carries an `Arc<CancelToken>` (atomic + reason).
|
|
473
488
|
Ops check it at safepoints (between chunks: gzip blocks, schema subtrees,
|
|
474
489
|
Argon2 is atomic and reports "not cancellable"). Scope cancellation cascades
|
|
@@ -618,6 +633,7 @@ Each of these removes syscalls, lock traffic, or reallocations from the hot path
|
|
|
618
633
|
| **Zero-copy INPUT** (`castrum_task_submit_slice` + the header/payload split in `ops.rs`) | Forced by a measurement: packing the payload into the args blob copied the whole input on the JS thread, making offloaded `gzipCompress` of 24 MiB **18.6 ms / 11.7 ms stall against 5.6 ms synchronous** — a net loss on both axes. Reading the payload in place: **7.3 ms / 2.1 ms**. The split also simplified every op arm (no more `args[4..]` offset arithmetic). |
|
|
619
634
|
| **No BigInt, no per-completion DataView** | The drain loop reads ids and lengths as two `u32` reads at an offset — `getBigUint64` boxes a BigInt and `readU64` allocated a DataView *per completion*. Exact for any value under 2^53 (ids are a JS counter, sizes are byte counts). A zero-copy completion's body is never materialized at all. |
|
|
620
635
|
| **Reusable args scratch** (copy path only) | Rust copies args on submit, so one growable buffer serves every synchronous submission. Deliberately NOT used for secret-bearing ops (pbkdf2/argon2) — a shared buffer would keep plaintext passwords alive far longer than a per-call allocation — nor for zero-copy retries, which re-submit after an `await` and must own their memory. |
|
|
636
|
+
| **Bounded admission** (`CASTRUM_TASK_QUEUE_MAX`, default 4096) | A hard cap turns sustained overload into an explicit rejection (`2 = overloaded` → typed `OVERLOADED`) instead of unbounded queue/`inflight` growth. The bound is a length check inside the same lock that pushes, so it is one comparison on a path that already locks. |
|
|
621
637
|
|
|
622
638
|
---
|
|
623
639
|
|