castrum 0.7.0 → 0.9.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/LICENSE +21 -0
- package/README.md +322 -59
- package/castrum.linux-x64-gnu.node +0 -0
- package/dist/index.d.ts +3161 -0
- package/dist/index.js +7306 -0
- package/docs/ARCHITECTURE.md +415 -0
- package/docs/BENCHMARKS.md +307 -0
- package/docs/ENVIRONMENT.md +79 -0
- package/docs/GETTING_STARTED.md +209 -0
- package/docs/INGRESS.md +745 -0
- package/docs/REPO_MAP.md +259 -0
- package/docs/adr/0001-two-wire-formats.md +39 -0
- package/docs/adr/0002-zero-dom-fast-schema.md +38 -0
- package/docs/adr/0003-bun-builtins-delegation.md +42 -0
- package/docs/bun-builtins-decision-matrix.md +108 -0
- package/index.ts +51 -8
- package/package.json +73 -20
- package/src/data/json-rows.ts +13 -13
- package/src/ingress/body.ts +149 -0
- package/src/ingress/constants.ts +220 -48
- package/src/ingress/context.ts +136 -0
- package/src/ingress/decode/baked-result.ts +121 -0
- package/src/ingress/decode/fast-result.ts +160 -0
- package/src/ingress/decode/packed-sections.ts +56 -0
- package/src/ingress/decode/result-base.ts +172 -0
- package/src/ingress/errors.ts +72 -0
- package/src/ingress/fast.ts +152 -1021
- package/src/ingress/handlers.ts +563 -1001
- package/src/ingress/headers/baked-templates.ts +102 -0
- package/src/ingress/headers/cors.ts +44 -0
- package/src/ingress/headers/fast-templates.ts +238 -0
- package/src/ingress/headers/hsts.ts +83 -0
- package/src/ingress/health.ts +67 -0
- package/src/ingress/index.ts +130 -405
- package/src/ingress/metrics.ts +132 -0
- package/src/ingress/options.ts +197 -0
- package/src/ingress/packing/gather-raw-headers.ts +75 -0
- package/src/ingress/packing/header-packing.ts +75 -0
- package/src/ingress/packing/input-packer.ts +150 -0
- package/src/ingress/packing/scratch.ts +50 -0
- package/src/ingress/packing/select-headers.ts +96 -0
- package/src/ingress/response/error-bodies.ts +75 -0
- package/src/ingress/response/terminal.ts +59 -0
- package/src/ingress/routes/common.ts +38 -0
- package/src/ingress/routes/delete.ts +14 -0
- package/src/ingress/routes/echo.ts +107 -0
- package/src/ingress/routes/fallback.ts +23 -0
- package/src/ingress/routes/head.ts +37 -0
- package/src/ingress/routes/index.ts +10 -0
- package/src/ingress/routes/json-write.ts +153 -0
- package/src/ingress/routes/options.ts +36 -0
- package/src/ingress/routes/read.ts +45 -0
- package/src/ingress/server-node.ts +376 -0
- package/src/ingress/server.ts +443 -0
- package/src/ingress/shared.ts +132 -0
- package/src/ingress/status.ts +97 -0
- package/src/ingress/types.ts +157 -0
- package/src/integration/batch.ts +58 -0
- package/src/integration/index.ts +29 -0
- package/src/integration/pipeline.ts +217 -0
- package/src/integration/streaming.ts +79 -0
- package/src/integration/websocket.ts +92 -0
- package/src/loader/batch.ts +133 -0
- package/src/loader/cost.ts +104 -0
- package/src/loader/index.ts +930 -0
- package/src/loader/ops.ts +657 -0
- package/src/native/ffi.ts +1541 -0
- package/src/native/index.ts +8 -207
- package/src/native/loader.ts +194 -0
- package/src/native/types.ts +598 -0
- package/src/rust-ffi/addon.ts +10 -0
- package/src/rust-ffi/batch.ts +431 -0
- package/src/rust-ffi/client.ts +88 -0
- package/src/rust-ffi/context.ts +164 -0
- package/src/rust-ffi/index.ts +40 -541
- package/src/rust-ffi/options.ts +113 -0
- package/src/rust-ffi/packed.ts +253 -0
- package/src/rust-ffi/proven.ts +42 -0
- package/src/rust-ffi/scalar/crypto.ts +265 -0
- package/src/rust-ffi/scalar/factories.ts +233 -0
- package/src/rust-ffi/scalar/hashing.ts +72 -0
- package/src/rust-ffi/scalar/http.ts +206 -0
- package/src/rust-ffi/scalar/index.ts +31 -0
- package/src/rust-ffi/scalar/interface.ts +450 -0
- package/src/rust-ffi/scalar/json.ts +45 -0
- package/src/rust-ffi/scalar/payload.ts +124 -0
- package/src/rust-ffi/text.ts +95 -0
- package/src/selection.json +194 -0
- package/src/selection.ts +77 -0
- package/src/shared/adaptive.ts +68 -0
- package/src/shared/bench-classify.ts +209 -0
- package/src/shared/buffer-pool.ts +209 -0
- package/src/shared/bytes.ts +35 -5
- package/src/shared/env.ts +20 -0
- package/src/shared/log.ts +74 -0
- package/src/shared/metrics.ts +247 -0
- package/src/shared/packed.ts +350 -154
- package/src/shared/proven.ts +596 -0
- package/src/shared/request-id.ts +67 -0
- package/src/shared/response.ts +104 -0
- package/src/shared/runtime.ts +34 -0
- package/src/shared/trace.ts +81 -0
- package/src/shared/uuid.ts +31 -0
- package/Cargo.toml +0 -73
- package/bench.ts +0 -3
- package/rust/batch.rs +0 -195
- package/rust/cookie_parser.rs +0 -70
- package/rust/cors.rs +0 -263
- package/rust/hashing.rs +0 -164
- package/rust/headers.rs +0 -234
- package/rust/hmac_sha256.rs +0 -113
- package/rust/http_parser.rs +0 -132
- package/rust/ingress.rs +0 -616
- package/rust/ingress_constants.rs +0 -89
- package/rust/ip_trust.rs +0 -185
- package/rust/json_ops.rs +0 -66
- package/rust/json_patch_ops.rs +0 -25
- package/rust/json_schema.rs +0 -151
- package/rust/json_ser.rs +0 -429
- package/rust/lib.rs +0 -48
- package/rust/method.rs +0 -141
- package/rust/mime_lookup.rs +0 -76
- package/rust/output.rs +0 -238
- package/rust/proxy.rs +0 -90
- package/rust/query_parser.rs +0 -141
- package/rust/random_token.rs +0 -52
- package/rust/rate_limit.rs +0 -203
- package/rust/terminal.rs +0 -95
- package/rust/unit_tests.rs +0 -843
- package/rust/url_codec.rs +0 -133
- package/rust/util.rs +0 -537
- package/rust/validation.rs +0 -107
- package/rust/websocket.rs +0 -23
- package/src/baseline/index.ts +0 -12
- package/src/baseline/tasks/cookie.ts +0 -22
- package/src/baseline/tasks/hashing.ts +0 -18
- package/src/baseline/tasks/hmac.ts +0 -38
- package/src/baseline/tasks/http.ts +0 -62
- package/src/baseline/tasks/json-patch.ts +0 -12
- package/src/baseline/tasks/json.ts +0 -50
- package/src/baseline/tasks/mime.ts +0 -5
- package/src/baseline/tasks/query.ts +0 -14
- package/src/baseline/tasks/token.ts +0 -15
- package/src/baseline/tasks/url.ts +0 -11
- package/src/baseline/tasks/validation.ts +0 -19
- package/src/baseline/tasks/websocket.ts +0 -14
- package/src/bench/assert.ts +0 -70
- package/src/bench/checks.ts +0 -227
- package/src/bench/checksum.ts +0 -27
- package/src/bench/comparisons.ts +0 -70
- package/src/bench/concurrent-worker.ts +0 -135
- package/src/bench/fixtures.ts +0 -233
- package/src/bench/index.ts +0 -2
- package/src/bench/measure.ts +0 -177
- package/src/bench/now.ts +0 -3
- package/src/bench/report.ts +0 -79
- package/src/bench/run.ts +0 -57
- package/src/bench/tasks/complex.ts +0 -232
- package/src/bench/tasks/concurrent.ts +0 -213
- package/src/bench/tasks/cookie.ts +0 -44
- package/src/bench/tasks/hashing.ts +0 -33
- package/src/bench/tasks/hmac.ts +0 -36
- package/src/bench/tasks/http.ts +0 -50
- package/src/bench/tasks/index.ts +0 -52
- package/src/bench/tasks/json-patch.ts +0 -21
- package/src/bench/tasks/json.ts +0 -33
- package/src/bench/tasks/mime.ts +0 -21
- package/src/bench/tasks/query.ts +0 -44
- package/src/bench/tasks/stress.ts +0 -142
- package/src/bench/tasks/token.ts +0 -20
- package/src/bench/tasks/url.ts +0 -34
- package/src/bench/tasks/validation.ts +0 -57
- package/src/bench/tasks/websocket.ts +0 -21
- package/src/bench/types.ts +0 -60
- package/src/ingress/packed-input.ts +0 -59
- package/src/shared/json.ts +0 -18
package/docs/REPO_MAP.md
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# castrum — Repo Map (what is where, and why it works this way)
|
|
2
|
+
|
|
3
|
+
This is the **navigation doc**. It tells you where everything lives, how the
|
|
4
|
+
pieces connect, and how to run/build/test/bench/publish. If you are brand new,
|
|
5
|
+
read [`GETTING_STARTED.md`](./GETTING_STARTED.md) first; if you want deep-dive
|
|
6
|
+
architecture, read [`ARCHITECTURE.md`](./ARCHITECTURE.md). If you are an AI
|
|
7
|
+
agent editing this repo, [`AGENTS.md`](../AGENTS.md) is the agent-facing map.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. What this project is
|
|
12
|
+
|
|
13
|
+
`castrum` is a hybrid **Bun (TypeScript) + Rust** package:
|
|
14
|
+
|
|
15
|
+
- A **Rust cdylib** (`castrum.<platform>-<arch>.node`, built with napi-rs,
|
|
16
|
+
ALSO exporting `extern "C"` symbols for Bun's `bun:ffi` — see
|
|
17
|
+
`rust/ffi.rs`) provides the performance-critical primitives (parsers,
|
|
18
|
+
crypto, hashing, JSON, compression, and the HTTP **ingress pipeline**).
|
|
19
|
+
Under Bun, `bun:ffi` is the PRIMARY transport; NAPI is the fallback
|
|
20
|
+
(Node, `CASTRUM_FFI_MODE=napi`, or a failed ffi self-test).
|
|
21
|
+
- **TypeScript** (`src/`) provides the ergonomic public API: the flat `rust.*`
|
|
22
|
+
FFI client, the `proven` performance surface, and the ingress layer.
|
|
23
|
+
- It ships benchmarks: a **CPU benchmark** (`bench.ts` → `src/bench`) and an
|
|
24
|
+
**HTTP benchmark** (`bench/run-bench.ts` + `bench/servers/*`).
|
|
25
|
+
|
|
26
|
+
Bun is the primary runtime; Node.js ≥20.3 is supported via a compiled ESM entry
|
|
27
|
+
(`dist/`).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 2. Command cheat-sheet
|
|
32
|
+
|
|
33
|
+
| Task | Command | Notes |
|
|
34
|
+
|------|---------|-------|
|
|
35
|
+
| Install deps | `bun install` | |
|
|
36
|
+
| Build Rust addon (release) | `bun run build` | `napi build --release --platform` |
|
|
37
|
+
| Build Rust addon (LOCAL max perf) | `bun run build:perf` | x86-64-v3 + AVX2 — **never for publish** |
|
|
38
|
+
| Build Rust addon (debug) | `bun run build:debug` | |
|
|
39
|
+
| Build compiled JS entry (Node) | `bun run build:js` | bundle + types → `dist/` (gitignored) |
|
|
40
|
+
| TS unit tests | `bun test` | ~540 tests, `test/unit/**` |
|
|
41
|
+
| Rust unit tests | `bun run test:rust` | `cargo test`, ~440 tests |
|
|
42
|
+
| Node smoke tests | `bun run test:node` | explicit `.mjs` paths (Node 24 rejects dir arg) |
|
|
43
|
+
| Installed-tarball e2e | `bun run verify:install` | pack → install → import from `node_modules` |
|
|
44
|
+
| Typecheck | `bun run typecheck` | `bunx tsc --noEmit` (only `index.ts`, `bench.ts`, `src/`) |
|
|
45
|
+
| CPU benchmark + correctness | `bun run check` | == `bun bench.ts`; writes `bench/results/cpu/` |
|
|
46
|
+
| Proven-surface audit | `bun run check:proven[:fail]` | `:fail` gates CI on regressions |
|
|
47
|
+
| Performance JSDoc | `bun run check:annotate` | rewrites perf JSDoc from CPU report; `--dry-run` previews |
|
|
48
|
+
| HTTP bench (all servers) | `bun run bench:http` | |
|
|
49
|
+
| HTTP smoke (fast sanity) | `bun run bench:http:smoke` | **the wire-format guard** (CI-gated) |
|
|
50
|
+
| Startup bench | `bun run bench:startup` | import + first-call timing |
|
|
51
|
+
| JS lint (Biome) | `bun run lint` / `lint:fix` | `bunx biome check [--write] .` |
|
|
52
|
+
| JS format (Biome) | `bun run format` | `bunx biome format --write .` |
|
|
53
|
+
| Version consistency | `bun run check:version` | package.json ↔ Cargo.toml ↔ CHANGELOG |
|
|
54
|
+
| JS dependency audit | `bun run audit` | `bun audit` (reports advisories) |
|
|
55
|
+
| Cargo deny audit | `bun run deny` | `cargo deny check` |
|
|
56
|
+
| Publish (manual, single-platform) | `bun run publish:manual[:dry]` | see §6 |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 3. Directory map
|
|
61
|
+
|
|
62
|
+
### Source (what you edit)
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
index.ts Package entry (Bun). Re-exports src/rust-ffi, proven, shared helpers, src/ingress.
|
|
66
|
+
bench.ts CPU benchmark entry point → src/bench/run.ts.
|
|
67
|
+
|
|
68
|
+
examples/ Runnable sample app (basic-server.ts + README) — see also README Quick Start.
|
|
69
|
+
basic-server.ts Minimal pre-baked ingress server + rust.* primitives.
|
|
70
|
+
|
|
71
|
+
src/
|
|
72
|
+
native/ The native transport layer: bun:ffi (primary on Bun) + NAPI fallback.
|
|
73
|
+
types.ts NativeAddon interface + every instance type (mirrors index.d.ts).
|
|
74
|
+
loader.ts Addon path resolution (multi-root) + getAddon()/lazyAddon().
|
|
75
|
+
ffi.ts bun:ffi C-ABI bindings (Bun-only): lazy bind + bind-time self-test,
|
|
76
|
+
CASTRUM_FFI_MODE gating, castrum_ingress_layout blob for constants.ts.
|
|
77
|
+
index.ts Barrel.
|
|
78
|
+
rust-ffi/ The flat `rust.*` FFI client (TS wrappers over the addon).
|
|
79
|
+
client.ts createRust() factory + the default `rust` instance + configure().
|
|
80
|
+
scalar/ / text.ts / batch.ts / packed.ts namespaces (buildScalar, buildText, ...).
|
|
81
|
+
context.ts Per-instance state (caches, rayon-pool bookkeeping).
|
|
82
|
+
options.ts RustOptions + rayon-thread resolution + coercion helpers.
|
|
83
|
+
addon.ts The single shared lazy addon proxy.
|
|
84
|
+
proven.ts `proven` client — the FULL rust.* surface, annotated (same object as `rust`).
|
|
85
|
+
index.ts Barrel (exports rust, createRust, types).
|
|
86
|
+
shared/ Cross-cutting helpers.
|
|
87
|
+
runtime.ts THE ONLY place `typeof Bun` is checked (isBun/isNode).
|
|
88
|
+
bytes.ts Shared TextEncoder/Decoder singletons + toPlainBuffer.
|
|
89
|
+
packed.ts Packed-wire unpackers (unpackU32Array, packBatch, readPairsPacked, ...).
|
|
90
|
+
request-id.ts Zero-alloc request-id generator (both ingress paths).
|
|
91
|
+
buffer-pool.ts Generic reusable byte-buffer pool (pooled ingress output).
|
|
92
|
+
response.ts pooledBodyResponse (releases the pool on body consume).
|
|
93
|
+
log.ts createStructuredLogger (CASTRUM_LOG_LEVEL-gated JSON lines).
|
|
94
|
+
proven.ts PROVEN_SURFACE registry (PURE DATA — no addon imports).
|
|
95
|
+
ingress/ The HTTP ingress pipeline (two paths — see §4.2).
|
|
96
|
+
fast.ts PATH 1: createIngressFast (packed-input via handleRequestPacked).
|
|
97
|
+
handlers.ts PATH 2: createIngressHandler (JS-packs the frame via IngressInputPacker
|
|
98
|
+
+ gatherRawHeadersPacked → handleRequestPacked).
|
|
99
|
+
index.ts Barrel + createIngress / createIngressSync convenience factories.
|
|
100
|
+
shared.ts JS constants + buildHeaderPlan + assertSyncCallback (shared by both paths).
|
|
101
|
+
constants.ts Binary-layout constants read from Rust at runtime (SINGLE SOURCE).
|
|
102
|
+
status.ts / errors.ts / options.ts / types.ts / body.ts / context.ts
|
|
103
|
+
packing/ header-packing.ts, input-packer.ts, gather-raw-headers.ts
|
|
104
|
+
headers/ cors.ts, hsts.ts, fast-templates.ts, baked-templates.ts
|
|
105
|
+
decode/ fast-result.ts, baked-result.ts (TWO decoders — see §4.2)
|
|
106
|
+
response/ terminal.ts, error-bodies.ts
|
|
107
|
+
routes/ read/head/json-write/echo/fallback factories + common.ts
|
|
108
|
+
server.ts createIngressServer (Bun.serve) + buildRouteHandlers + gracefulShutdown.
|
|
109
|
+
server-node.ts createIngressServerNode (node:http adapter, same route handlers).
|
|
110
|
+
integration/ Framework integration (wraps path 2 — createIngressHandler).
|
|
111
|
+
pipeline.ts createPipeline (framework-agnostic request stage for Hono/Elysia/Bun.serve).
|
|
112
|
+
websocket.ts createWebSocketUpgrade (RFC 6455 101 handshake + subprotocol).
|
|
113
|
+
streaming.ts sseResponse (SSE frames via rust.sseEncodeEvent).
|
|
114
|
+
baseline/ Pure-JS baseline implementations (benchmark reference). Internal.
|
|
115
|
+
bench/ CPU benchmark framework (tasks, measure, report, checks, comparisons).
|
|
116
|
+
data/ json-rows.ts fixture generator (benchmark-only).
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
rust/ ONE cdylib crate (Cargo [lib] → lib.rs).
|
|
121
|
+
lib.rs Declaration hub + module map + crate docs.
|
|
122
|
+
util/ Shared infrastructure: bytes, packed, batch (+batch_core), threadpool, validation.
|
|
123
|
+
http/ HTTP wire formats & parsing: headers, method, http/cookie/query parsers,
|
|
124
|
+
form, media_type, url_codec, url_join, etag, accept, mime_lookup, multipart.
|
|
125
|
+
crypto/ Auth & hashing: hmac_sha256, cookie_sign, csrf, jwt, aead, argon2,
|
|
126
|
+
base64, hashing (fnv/crc32/xxh3), random_token.
|
|
127
|
+
json/ JSON & schema: json_ops, json_ser, json_patch_ops, json_schema (napi),
|
|
128
|
+
fast_schema/ (zero-DOM engine — pure, no napi).
|
|
129
|
+
payload/ Output & streaming: compress, sse, ws_frames, websocket, template.
|
|
130
|
+
ingress/ THE ingress pipeline: mod.rs (napi boundary), pipeline.rs (core 8-stage),
|
|
131
|
+
options/time/packed, cors, proxy, ip_trust, rate_limit, terminal,
|
|
132
|
+
output.rs (single numeric layout source), ingress_constants.rs (napi projection).
|
|
133
|
+
ffi.rs #[no_mangle] extern "C" exports (45 castrum_* symbols) for Bun's bun:ffi
|
|
134
|
+
primary transport, incl. castrum_ingress_layout (the layout blob).
|
|
135
|
+
unit_tests.rs / test_support.rs Cross-module Rust test suites.
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Generated / ignored (do NOT edit)
|
|
139
|
+
|
|
140
|
+
| Path | What it is | Why ignored |
|
|
141
|
+
|------|-----------|-------------|
|
|
142
|
+
| `dist/` | Compiled ESM entry + types for Node | Built by `build:js`; gitignored |
|
|
143
|
+
| `index.js` / `index.d.ts` | NAPI-RS generated loader/declarations | Regenerated by `napi build`; gitignored |
|
|
144
|
+
| `castrum.<platform>-<arch>.node` | The native addon binary | Built locally; gitignored |
|
|
145
|
+
| `bench/results/` | Bench reports (cpu/, http/) | Generated; gitignored |
|
|
146
|
+
| `target/` | Cargo build output | gitignored |
|
|
147
|
+
| `node_modules/` | Deps | gitignored |
|
|
148
|
+
| `artifacts/` | CI publish staging dir | gitignored |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 4. How the pieces connect (the 5 cross-cutting flows)
|
|
153
|
+
|
|
154
|
+
### 4.1 Constants never drift (single numeric source)
|
|
155
|
+
`rust/ingress/output.rs` defines every ingress layout constant once → projected
|
|
156
|
+
to JS two ways: `rust/ingress/ingress_constants.rs` via `#[napi] const` (NAPI)
|
|
157
|
+
AND `rust/ffi.rs` `IngressLayout` via `castrum_ingress_layout` (C ABI, primary
|
|
158
|
+
on Bun) → `src/ingress/constants.ts` reads them at runtime (ffi blob on Bun,
|
|
159
|
+
napi on Node). Drift is pinned by the Rust unit test
|
|
160
|
+
`ingress_layout_c_abi_matches_output_source` and the bun:ffi bind-time
|
|
161
|
+
self-test. **Never hardcode a layout value** — if you change the layout, change
|
|
162
|
+
`output.rs` and nothing else.
|
|
163
|
+
|
|
164
|
+
### 4.2 The TWO ingress paths (do not conflate)
|
|
165
|
+
1. **`src/ingress/fast.ts`** — `createIngressFast(options)`. JS packs request
|
|
166
|
+
headers into a binary buffer (`IngressInputPacker`), Rust parses via
|
|
167
|
+
`handleRequestPacked`. Responses: `{"error":{code,status,message,requestId}}`
|
|
168
|
+
with `x-ratelimit-*` headers.
|
|
169
|
+
2. **`src/ingress/handlers.ts`** — `createIngressHandler(options, runtime)`.
|
|
170
|
+
The request frame is packed in JS (`IngressInputPacker` +
|
|
171
|
+
`gatherRawHeadersPacked`) and driven through the SAME native core as path 1
|
|
172
|
+
(`handleRequestPacked`), writing into a **pooled** output buffer. Responses:
|
|
173
|
+
`{"ok":true,...,"requestId":...}` (success) / `{"ok":false,"error":{code,
|
|
174
|
+
message}}` (errors) with `ratelimit-*` headers.
|
|
175
|
+
|
|
176
|
+
These wire formats are a **benchmark contract** (`bench/load.ts` validates
|
|
177
|
+
them) — they are intentionally NOT unified. The two decoders
|
|
178
|
+
(`decode/fast-result.ts` vs `decode/baked-result.ts`) and the two header-template
|
|
179
|
+
builders (`headers/fast-templates.ts` vs `headers/baked-templates.ts`) exist for
|
|
180
|
+
this reason. **Shared between both paths**: `generateRequestId`
|
|
181
|
+
(`src/shared/request-id.ts`) and `buildHeaderPlan` / `assertSyncCallback`
|
|
182
|
+
(`src/ingress/shared.ts`).
|
|
183
|
+
|
|
184
|
+
### 4.3 Addon loading + runtime seam
|
|
185
|
+
- `src/native/loader.ts` resolves the `.node` from multiple roots so it works
|
|
186
|
+
from both the source layout (`src/…`) and the bundled layout (`dist/…`).
|
|
187
|
+
Honors `CASTRUM_NATIVE_LIBRARY_PATH` / `NAPI_RS_NATIVE_LIBRARY_PATH`.
|
|
188
|
+
- Runtime detection lives **only** in `src/shared/runtime.ts`
|
|
189
|
+
(`isBun`/`isNode`). `createIngressServer` is Bun-only; Node users use
|
|
190
|
+
`createIngressServerNode` (same route handlers via `buildRouteHandlers`).
|
|
191
|
+
- ⚠️ `src/ingress/constants.ts` is the **one module that dlopens the addon at
|
|
192
|
+
import time** (its constants are needed as soon as ingress is imported).
|
|
193
|
+
Everything else is lazy.
|
|
194
|
+
|
|
195
|
+
### 4.4 The `proven` performance surface
|
|
196
|
+
`src/shared/proven.ts` (`PROVEN_SURFACE`) is the single source of truth for the
|
|
197
|
+
per-function performance classifications. It is **pure data** (no addon
|
|
198
|
+
imports) so `scripts/check-proven.ts` can audit it without dlopening.
|
|
199
|
+
`src/rust-ffi/proven.ts` exposes the FULL `rust.*` surface (nothing is
|
|
200
|
+
filtered); each function's JSDoc carries its measured performance vs the JS
|
|
201
|
+
baseline, written by `scripts/annotate-performance.ts` (`bun run check:annotate`)
|
|
202
|
+
and marked `@deprecated` when slower. `bun run check:proven:fail` (CI) audits
|
|
203
|
+
the registry against the CPU benchmark report on a **release** build.
|
|
204
|
+
|
|
205
|
+
### 4.5 Pooled output buffers (path 2)
|
|
206
|
+
`createIngressHandler` owns a `BufferPool` (`src/shared/buffer-pool.ts`) sized
|
|
207
|
+
by `runtime.outputBufferSize`. `run()` packs the frame in JS (`IngressInputPacker`
|
|
208
|
+
+ `gatherRawHeadersPacked`) and writes via `handleRequestPacked`
|
|
209
|
+
into the pooled buffer, passing the exact written subarray to
|
|
210
|
+
`BakedIngressResult.refresh`. In copy
|
|
211
|
+
mode the buffer is released at the end of `run()`; in zero-copy mode
|
|
212
|
+
`ingress.zeroCopyResponse()` serves a `pooledBodyResponse`
|
|
213
|
+
(`src/shared/response.ts`) that keeps the buffer in flight until the body is
|
|
214
|
+
consumed.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 5. Testing & benchmarks
|
|
219
|
+
|
|
220
|
+
- **TS**: `bun test` → `test/unit/**` (`ingress/`, `shared/`, `features/`).
|
|
221
|
+
- **Rust**: `cargo test` → per-module `#[cfg(test)] mod tests` + cross-module
|
|
222
|
+
`rust/unit_tests.rs`.
|
|
223
|
+
- **Node**: `test/integration/node-smoke.test.mjs` + `node-enterprise.test.mjs`
|
|
224
|
+
via `node --test` (run with `bun run test:node`).
|
|
225
|
+
- **CPU bench**: `bun run check` (correctness checks + comparisons). Sub-µs ops
|
|
226
|
+
are batched (`CASTRUM_BENCH_BATCH_SIZE`). Report → `bench/results/cpu/`.
|
|
227
|
+
- **HTTP bench**: `bench/run-bench.ts` + `bench/load.ts` (load generator that
|
|
228
|
+
validates response SHAPE) + `bench/servers/{bun,elysia,ingress}-server.ts`.
|
|
229
|
+
`bench:http:smoke` is the fast CI wire-format gate.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 6. Publishing (npm)
|
|
234
|
+
|
|
235
|
+
- Package name `castrum` (unscoped, public). Native binary name `castrum`
|
|
236
|
+
(declared in package.json `napi.binaryName` + Cargo.toml — keep in sync).
|
|
237
|
+
- **Multi-platform (recommended)**: push a `v*` tag → `.github/workflows/ci.yml`
|
|
238
|
+
builds each platform addon, the `publish` job downloads them into
|
|
239
|
+
`./artifacts`, stages them, and runs `npm publish` (needs `NPM_TOKEN`).
|
|
240
|
+
- **Manual single-platform**: `bun run publish:manual --increment minor`
|
|
241
|
+
(syncs package.json ↔ Cargo.toml ↔ CHANGELOG, tags, builds, publishes with
|
|
242
|
+
`CASTRUM_PUBLISH_ALLOW_PARTIAL=1`). `--dry-run` plans only.
|
|
243
|
+
- `prepublishOnly` runs `build:js` + `scripts/prepublish.mjs` (stages
|
|
244
|
+
`artifacts/*.node` and verifies every `napi.targets` maps to a `.node`).
|
|
245
|
+
- Run `bun run check:version` before tagging.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 7. Cross-links
|
|
250
|
+
|
|
251
|
+
| Doc | Purpose |
|
|
252
|
+
|-----|---------|
|
|
253
|
+
| [`GETTING_STARTED.md`](./GETTING_STARTED.md) | Beginner tutorial (intern-friendly) |
|
|
254
|
+
| [`ARCHITECTURE.md`](./ARCHITECTURE.md) | Deep-dive: modules, data flow, output layout, memory, concurrency |
|
|
255
|
+
| [`INGRESS.md`](./INGRESS.md) | The pre-baked ingress API + route factories + servers |
|
|
256
|
+
| [`BENCHMARKS.md`](./BENCHMARKS.md) | Benchmark scenarios, report format, how to run |
|
|
257
|
+
| [`ENVIRONMENT.md`](./ENVIRONMENT.md) | All `CASTRUM_*` env vars (+ legacy aliases) |
|
|
258
|
+
| [`AGENTS.md`](../AGENTS.md) | AI-agent editing guidance (commands, constraints, gotchas) |
|
|
259
|
+
| `README.md` | Public-facing quick start + API reference |
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ADR-0001: The two ingress wire formats are a contract, not a bug
|
|
2
|
+
|
|
3
|
+
- **Status**: Accepted (2026-08-06)
|
|
4
|
+
- **Deciders**: maintainers (via benchmark + hardening passes)
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The ingress layer ships two paths that consume the SAME native core
|
|
9
|
+
(`handleRequestPacked`):
|
|
10
|
+
|
|
11
|
+
1. `src/ingress/fast.ts` — `createIngressFast`; JS packs headers via
|
|
12
|
+
`IngressInputPacker`; errors are `{"error":{code,status,message,requestId}}`
|
|
13
|
+
with `x-ratelimit-*` headers.
|
|
14
|
+
2. `src/ingress/handlers.ts` — `createIngressHandler`; JS packs the full frame;
|
|
15
|
+
success is Rust-generated `{"ok":true,...,"requestId":...}`, errors are
|
|
16
|
+
`{"ok":false,"error":{"code","message"}}`, with `ratelimit-*` headers.
|
|
17
|
+
|
|
18
|
+
They also use separate result decoders (`FastIngressResult` vs
|
|
19
|
+
`BakedIngressResult`) and separate header-template builders.
|
|
20
|
+
|
|
21
|
+
## Decision
|
|
22
|
+
|
|
23
|
+
**Do not unify them.** The HTTP benchmark load generator (`bench/load.ts`)
|
|
24
|
+
requires `ok === true` + `requestId: string` on success and `error.code` /
|
|
25
|
+
`error.message` on errors — i.e. path 2's format. The two formats are a
|
|
26
|
+
**benchmark contract**: the benchmark server uses path 2, and changing its
|
|
27
|
+
wire shape invalidates baselines.
|
|
28
|
+
|
|
29
|
+
## Consequences
|
|
30
|
+
|
|
31
|
+
- Both paths stay hot and zero-alloc.
|
|
32
|
+
- Duplicated decoders/templates are intentional; sharing them is
|
|
33
|
+
**forbidden** by AGENTS.md (it would couple two differently-shaped outputs).
|
|
34
|
+
- New contributors must not "helpfully" unify the formats.
|
|
35
|
+
|
|
36
|
+
## See also
|
|
37
|
+
|
|
38
|
+
- AGENTS.md §"Ingress: the two paths (do NOT conflate)"
|
|
39
|
+
- docs/INGRESS.md §"Wire format"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# ADR-0002: Zero-DOM fast path for JSON Schema validation
|
|
2
|
+
|
|
3
|
+
- **Status**: Accepted (2026-08-07), extended 2026-08-11
|
|
4
|
+
- **Deciders**: maintainers (measured via `bun run check`)
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
`jsonschema::Validator::is_valid` requires a `serde_json::Value` — a full DOM
|
|
9
|
+
per document, measured at ~95% of validation cost. That made the Rust schema
|
|
10
|
+
path parity with `ajv` at best, so castrum couldn't match its own zero-DOM
|
|
11
|
+
wins (json_valid / json_sum / packed parsers).
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
Implement a **zero-DOM draft-07 fast path** (`rust/json/fast_schema/`) that
|
|
16
|
+
compiles the common keyword subset into a `FastNode` AST and validates raw
|
|
17
|
+
bytes with a zero-alloc cursor. Unsupported keywords → `compile()` returns
|
|
18
|
+
`Err` → the caller falls back to the authoritative `jsonschema` crate (DOM
|
|
19
|
+
path). Parity is **byte-for-byte gated** against the crate
|
|
20
|
+
(`rust/json/fast_schema/tests.rs`).
|
|
21
|
+
|
|
22
|
+
Draft-07 is pinned because `jsonschema` 0.48's default draft is 2020-12 whose
|
|
23
|
+
`items` / `$ref`-sibling / boolean-exclusive semantics differ; both
|
|
24
|
+
`SchemaValidator` and `IngressSchema` build the reference with
|
|
25
|
+
`.with_draft(Draft7)` + `should_validate_formats(true)`.
|
|
26
|
+
|
|
27
|
+
## Consequences
|
|
28
|
+
|
|
29
|
+
- Fast path: scalar ~1.2–2.4x, batch ~1.0–1.56x vs `ajv` (release build).
|
|
30
|
+
- A `$schema` declaring non-draft-07 (or unsupported keywords) still falls
|
|
31
|
+
back — versatility preserved.
|
|
32
|
+
- The fast path MUST stay byte-parity with the crate for the supported
|
|
33
|
+
keyword surface; any divergence is a bug.
|
|
34
|
+
|
|
35
|
+
## See also
|
|
36
|
+
|
|
37
|
+
- rust/json/fast_schema/tests.rs (parity corpus)
|
|
38
|
+
- docs/ARCHITECTURE.md §JSON & schema
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# ADR-0003: Don't reinvent the wheel — benchmark, then delegate to Bun built-ins
|
|
2
|
+
|
|
3
|
+
- **Status**: Accepted (2026-08-11)
|
|
4
|
+
- **Deciders**: maintainers (measured on Bun 1.4.0 / release addon)
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
castrum ships Rust ops that overlap with Bun's native built-ins
|
|
9
|
+
(`Bun.hash`, `Bun.password`, `Bun.CryptoHasher`, `Bun.gzipSync`,
|
|
10
|
+
`Bun.randomUUIDv7`, `crypto.subtle`, `JSON.parse`, `URLSearchParams`). For the
|
|
11
|
+
same workload, the in-process Bun implementation can beat an FFI-crossing Rust
|
|
12
|
+
op — spending effort building or keeping such ops is reinventing the wheel.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
Every overlapping op is **benchmarked against its Bun built-in** before it is
|
|
17
|
+
built, kept, or delegated. Results live in
|
|
18
|
+
`docs/bun-builtins-decision-matrix.md`. Rules:
|
|
19
|
+
|
|
20
|
+
1. **Delegate to Bun** at the `@flux/native` wrapper layer where Bun wins
|
|
21
|
+
clearly: non-crypto hashing (`Bun.hash.crc32` ~3x, `xxHash3` ~4x),
|
|
22
|
+
gzip (`Bun.gzipSync`/`gunzipSync`), `Bun.randomUUIDv7` (~2x), HMAC
|
|
23
|
+
(`Bun.CryptoHasher` ~1.2x).
|
|
24
|
+
2. **Keep rust** where it wins or Bun has no synchronous equivalent:
|
|
25
|
+
argon2id password hashing (~1.8–2x vs `Bun.password`), bcrypt verify
|
|
26
|
+
(~1.5x), brotli, PBKDF2, zero-DOM JSON/parsers.
|
|
27
|
+
3. **Don't build** what Bun already wins at (UUIDv7 → `uuidv7()` delegates to
|
|
28
|
+
`Bun.randomUUIDv7`, falls back to `crypto.randomUUID`).
|
|
29
|
+
|
|
30
|
+
## Consequences
|
|
31
|
+
|
|
32
|
+
- New `rust.*` primitives ship with a Bun-built-in baseline + decision-matrix
|
|
33
|
+
entry (`xxh3`, `passwordHashBcrypt`/`verifyBcrypt`, `pbkdf2Sha256`).
|
|
34
|
+
- `PROVEN_SURFACE` classifications reflect the Bun/JS baseline honestly
|
|
35
|
+
(e.g. `xxh3` is `not-competitive` vs `Bun.hash.xxHash3`).
|
|
36
|
+
- Diagnostic benchmark tasks use `diag:` names so they never perturb the
|
|
37
|
+
`check:proven` audit.
|
|
38
|
+
|
|
39
|
+
## See also
|
|
40
|
+
|
|
41
|
+
- docs/bun-builtins-decision-matrix.md
|
|
42
|
+
- src/bench/tasks/bun-builtins.ts
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Bun Built-ins vs castrum — Decision Matrix
|
|
2
|
+
|
|
3
|
+
> **Question this answers**: for the same workload, does a Bun native built-in
|
|
4
|
+
> beat the castrum Rust op? Where it does, castrum should stop reinventing the
|
|
5
|
+
> wheel and `@flux/native` should delegate to Bun; where castrum wins (or Bun
|
|
6
|
+
> has no sync equivalent), the Rust op stands.
|
|
7
|
+
|
|
8
|
+
## Methodology
|
|
9
|
+
|
|
10
|
+
- **Runtime**: Bun **1.4.0** (the primary target).
|
|
11
|
+
- **Addon**: castrum **release** build (`bun run build`, baseline CPU — the
|
|
12
|
+
shipped profile).
|
|
13
|
+
- **Driver**: `bun run check` (CPU benchmark → `bench/results/cpu/latest.json`).
|
|
14
|
+
Each pair runs on the SAME input (fixture payloads, see table).
|
|
15
|
+
- **Naming**: diagnostic comparisons use `diag:` task names so
|
|
16
|
+
`scripts/check-proven.ts` never aggregates them into a `PROVEN_SURFACE`
|
|
17
|
+
entry (see `src/bench/comparisons.ts`).
|
|
18
|
+
- **Honesty guard**: every Bun API is verified to actually execute real work at
|
|
19
|
+
runtime before a result is trusted. This is important — the first run
|
|
20
|
+
produced false "wins" (brotli 156×, validators 3–5×) because this Bun
|
|
21
|
+
version has **no** `Bun.brotliCompressSync` and **no** `Bun.validators`, and
|
|
22
|
+
the feature-detect fell through to an empty return. Those pairs were removed,
|
|
23
|
+
not reported.
|
|
24
|
+
- Ratio is **nativeAvgMs / rustAvgMs** (>1 = Bun faster; <1 = rust faster).
|
|
25
|
+
|
|
26
|
+
## Results (Bun 1.4.0, release addon, 2026-08-11)
|
|
27
|
+
|
|
28
|
+
| castrum op | Bun built-in | Input | Winner | Ratio |
|
|
29
|
+
|---|---|---|---|---|
|
|
30
|
+
| `rust.fnv1a64` | `Bun.hash` (wyhash) | 42 B | Bun (parity) | 1.06× |
|
|
31
|
+
| `rust.crc32` | `Bun.hash.crc32` | 42 B | **Bun** | 2.8–8.4× |
|
|
32
|
+
| `rust.xxh3` (new) | `Bun.hash.xxHash3` | 42 B | **Bun** | 4.15× |
|
|
33
|
+
| `rust.hmacSha256` | `Bun.CryptoHasher("sha256", key)` | 32 B | Bun (mild) | 1.1–1.4× |
|
|
34
|
+
| `rust.passwordHash` (argon2id) | `Bun.password.hashSync` (argon2id) | 12 B | **rust** | 0.55× (rust 1.83×) |
|
|
35
|
+
| `rust.passwordVerify` | `Bun.password.verifySync` | 12 B | **rust** | 0.53× (rust 1.88×) |
|
|
36
|
+
| `rust.passwordHashBcrypt` (new) | `Bun.password.hashSync` (bcrypt) | 12 B | Bun (mild) | 1.24× |
|
|
37
|
+
| `rust.passwordVerifyBcrypt` (new) | `Bun.password.verifySync` (bcrypt) | 12 B | **rust** | 0.67× (rust 1.49×) |
|
|
38
|
+
| `rust.pbkdf2Sha256` (new) | `node:crypto.pbkdf2Sync` | 12 B | rust (parity) | 0.93× (rust 1.08×) |
|
|
39
|
+
| `rust.randomToken` (16 B) | `Bun.randomUUIDv7` | — | **Bun** | 1.62× |
|
|
40
|
+
| `rust.gzipCompress` | `Bun.gzipSync` | 11 179 B | **Bun** | 2.02× |
|
|
41
|
+
| `rust.gzipDecompress` | `Bun.gunzipSync` | 1 045 B | **Bun** | 1.38× |
|
|
42
|
+
| `rust.brotliCompress` | *no sync Bun API* | — | N/A | — |
|
|
43
|
+
| `rust.brotliDecompress` | *no sync Bun API* | — | N/A | — |
|
|
44
|
+
| `rust.validateEmail/Uuid/Ipv4/Ipv6` | `Bun.validators` (*absent in 1.4*) | — | N/A | — |
|
|
45
|
+
|
|
46
|
+
> Same-cost note: `Bun.password.hashSync` ran at `memoryCost 4096 / timeCost 1`
|
|
47
|
+
> (4 MiB, 1 iter) matching the rust bench cost (`mCost 4096 / tCost 1`) — the
|
|
48
|
+
> rust argon2id win is cost-for-cost, not a cost cheat.
|
|
49
|
+
|
|
50
|
+
> Transport note: "FFI-crossing Rust op" below means any Rust call over the
|
|
51
|
+
> native bridge. Under Bun that bridge is now `bun:ffi` (the PRIMARY transport);
|
|
52
|
+
> under Node it is the napi fallback. This Bun-builtin-vs-Rust axis is
|
|
53
|
+
> independent of the ffi-vs-napi transport axis — these decisions recommend
|
|
54
|
+
> WHICH implementation to call (`rust.*` vs a Bun built-in), not which transport
|
|
55
|
+
> carries the call.
|
|
56
|
+
|
|
57
|
+
## Decisions
|
|
58
|
+
|
|
59
|
+
### → Delegate to Bun at the `@flux/native` wrapper layer (Bun wins)
|
|
60
|
+
These rust ops lose to Bun's native implementation on the SAME workload — the
|
|
61
|
+
FFI crossing cannot beat Bun's in-process C++:
|
|
62
|
+
|
|
63
|
+
| Op | Recommendation |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `crc32` | Prefer `Bun.hash.crc32`; keep rust as fallback (pure-TS path already exists). |
|
|
66
|
+
| `xxh3` | Prefer `Bun.hash.xxHash3` (~4×); keep the rust export as the Node/non-Bun fast path. Classified `not-competitive` in `PROVEN_SURFACE`. |
|
|
67
|
+
| `gzipCompress` / `gzipDecompress` | Prefer `Bun.gzipSync` / `Bun.gunzipSync`. |
|
|
68
|
+
| `randomToken` | Prefer `Bun.randomUUIDv7()` (or `crypto.getRandomValues`) for token-sized output; keep rust for byte-precise control. |
|
|
69
|
+
| `hmacSha256` | Prefer `Bun.CryptoHasher("sha256", key)` (1.17× — mild; keep rust batch path, which wins on larger inputs). |
|
|
70
|
+
| `fnv1a64` | Parity — either is fine; `Bun.hash` (wyhash) is a stronger algorithm, prefer it for NEW non-crypto hashing needs. |
|
|
71
|
+
|
|
72
|
+
**Delegation rule of thumb**: rust stays the default only where it is
|
|
73
|
+
`proven` against its **Bun** baseline (not just its JS baseline). These are the
|
|
74
|
+
ops where rust genuinely wins or Bun has no equivalent.
|
|
75
|
+
|
|
76
|
+
### → Keep rust (rust wins or Bun has no equivalent)
|
|
77
|
+
| Op | Rationale |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `passwordHash` / `passwordVerify` (argon2id) | rust 1.83–1.88× faster than `Bun.password` at equal cost. **Do not delegate.** |
|
|
80
|
+
| `passwordHashBcrypt` / `passwordVerifyBcrypt` (new) | Hash ≈ parity (Bun 1.24×), verify rust 1.49×. Keep rust — self-contained and works under Node. |
|
|
81
|
+
| `pbkdf2Sha256` (new) | Bun has no sync PBKDF2; rust ≈ parity with node:crypto (1.08×). Keep rust. |
|
|
82
|
+
| `brotliCompress` / `brotliDecompress` | Bun 1.4 has no synchronous brotli; keep rust (async `CompressionStream` is not drop-in). |
|
|
83
|
+
| `validateEmail` / `validateUuid` / `validateIpv4` / `validateIpv6` | `Bun.validators` absent in 1.4; keep rust. Re-check when Bun adds it. |
|
|
84
|
+
| `jsonValid` / `jsonSumIds` / packed parsers / ingress | No Bun sync equivalent for the zero-DOM / zero-copy semantics; keep rust. |
|
|
85
|
+
|
|
86
|
+
### → Add primitives that close gaps (Phase 2)
|
|
87
|
+
| Gap | Plan |
|
|
88
|
+
|---|---|
|
|
89
|
+
| Public XXH3-64 (`rust.xxh3`) | **Done** — exposed; measured Bun.hash.xxHash3 4.15× faster → classify `not-competitive`, prefer Bun under Bun. |
|
|
90
|
+
| bcrypt | **Done** — `rust.passwordHashBcrypt`/`passwordVerifyBcrypt`; hash parity, verify rust 1.49×. |
|
|
91
|
+
| PBKDF2 | **Done** — `rust.pbkdf2Sha256`; parity with node:crypto, only sync option in Bun. |
|
|
92
|
+
| UUIDv7 | `Bun.randomUUIDv7` already wins — **do not** build a rust UUIDv7; use Bun. |
|
|
93
|
+
|
|
94
|
+
## Summary
|
|
95
|
+
|
|
96
|
+
**The "don't reinvent the wheel" takeaway:** for non-cryptographic hashing,
|
|
97
|
+
random tokens, and gzip, Bun 1.4's built-ins are already faster than any
|
|
98
|
+
FFI-crossing Rust op — `@flux/native` should prefer them. Rust retains a clear,
|
|
99
|
+
defensible moat exactly where it matters: **argon2id password hashing
|
|
100
|
+
(≈1.9× vs Bun)**, the **zero-DOM / zero-copy parsers and JSON paths** (no Bun
|
|
101
|
+
sync equivalent), and **brotli** (no Bun sync API).
|
|
102
|
+
|
|
103
|
+
## Re-run
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
bun run build # release addon
|
|
107
|
+
bun run check # writes latest.json; grep the "vs Bun" lines
|
|
108
|
+
```
|
package/index.ts
CHANGED
|
@@ -1,8 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
// index.ts — castrum package entry (barrel).
|
|
2
|
+
//
|
|
3
|
+
// Re-exports the full public API: the flat `rust.*` FFI surface
|
|
4
|
+
// (src/rust-ffi), the runtime selection hints (src/selection), the
|
|
5
|
+
// performance-proven registry (src/shared/proven), and the higher-order
|
|
6
|
+
// loader (src/loader). See AGENTS.md for the layout map.
|
|
2
7
|
|
|
3
|
-
export *
|
|
8
|
+
export * from './src/rust-ffi'
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
// Native-vs-JS selection (OWNED HERE, not by consumers): `opImpl(op)` is the
|
|
11
|
+
// benchmark-driven recommendation for which operations should use the Rust
|
|
12
|
+
// addon vs a pure-JS implementation. Consumers read it once at load time and
|
|
13
|
+
// bind each op to a fixed impl. Source: rust/selection.rs (audited by
|
|
14
|
+
// `bun run check:selection`). See src/selection.ts.
|
|
15
|
+
export { opImpl, isNativeOp, opDecision } from './src/selection'
|
|
16
|
+
export type { OpImpl, OpDecision } from './src/selection'
|
|
17
|
+
|
|
18
|
+
// Performance-proven surface: only `rust.*` functions that beat their JS
|
|
19
|
+
// baseline in benchmarks. See src/shared/proven.ts for the registry.
|
|
20
|
+
export {
|
|
21
|
+
proven,
|
|
22
|
+
PROVEN_SURFACE,
|
|
23
|
+
provenStatus,
|
|
24
|
+
isProven,
|
|
25
|
+
provenSurface,
|
|
26
|
+
provenSummary,
|
|
27
|
+
} from './src/rust-ffi/proven'
|
|
28
|
+
export type { ProvenClient, ProvenKey } from './src/rust-ffi/proven'
|
|
29
|
+
export type { PerformanceStatus, ProvenEntry } from './src/shared/proven'
|
|
30
|
+
|
|
31
|
+
export { encoder, decoder } from './src/shared/bytes'
|
|
32
|
+
// Bounded EWMA adaptive-estimate utility (drives BufferPool adaptive sizing
|
|
33
|
+
// and reusable runtime self-optimization decisions).
|
|
34
|
+
export { AdaptiveEstimate } from './src/shared/adaptive'
|
|
35
|
+
export type { AdaptiveEstimateOptions } from './src/shared/adaptive'
|
|
36
|
+
// UUIDv7 generation — delegates to Bun.randomUUIDv7, crypto.randomUUID on Node.
|
|
37
|
+
export { uuidv7 } from './src/shared/uuid'
|
|
38
|
+
// Zero-dep metrics registry (counters/gauges/histograms + Prometheus render).
|
|
39
|
+
export { createMetrics, DEFAULT_BUCKETS } from './src/shared/metrics'
|
|
40
|
+
export type { MetricsRegistry, Counter, Gauge, Histogram } from './src/shared/metrics'
|
|
6
41
|
|
|
7
42
|
export {
|
|
8
43
|
packBatch,
|
|
@@ -12,13 +47,21 @@ export {
|
|
|
12
47
|
pairsToObject,
|
|
13
48
|
parseQueryString,
|
|
14
49
|
parseCookieHeader,
|
|
50
|
+
parseFormBody,
|
|
15
51
|
unpackBitset,
|
|
16
52
|
unpackByteResults,
|
|
17
53
|
unpackI64ArrayAsBigInt,
|
|
18
54
|
unpackU32Array,
|
|
19
|
-
} from
|
|
20
|
-
export type { Pair, ParsedHttpRequestPacked } from
|
|
55
|
+
} from './src/shared/packed'
|
|
56
|
+
export type { Pair, ParsedHttpRequestPacked } from './src/shared/packed'
|
|
57
|
+
|
|
58
|
+
export * from './src/ingress'
|
|
59
|
+
|
|
60
|
+
// Framework-agnostic integration helpers: run the ingress pipeline as a
|
|
61
|
+
// request stage (createPipeline), RFC 6455 upgrade (createWebSocketUpgrade),
|
|
62
|
+
// and SSE framing (sseResponse). See docs/INGRESS.md.
|
|
63
|
+
export * from './src/integration'
|
|
21
64
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export * from
|
|
65
|
+
// Higher-order loader (HFC) over the curated op set: createLoader / loader
|
|
66
|
+
// dispatch single items, packed batches, and load() coalescing. See README.
|
|
67
|
+
export * from './src/loader'
|