castrum 0.7.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -368
  3. package/castrum.linux-x64-gnu.node +0 -0
  4. package/dist/index.d.ts +2763 -0
  5. package/dist/index.js +8707 -0
  6. package/docs/API.md +337 -0
  7. package/docs/ARCHITECTURE.md +507 -0
  8. package/docs/BENCHMARKS.md +343 -0
  9. package/docs/CASE_STUDY.md +240 -0
  10. package/docs/ENVIRONMENT.md +81 -0
  11. package/docs/FFI_BUN_GUIDE.md +327 -0
  12. package/docs/GETTING_STARTED.md +192 -0
  13. package/docs/INGRESS-ROUTER.md +81 -0
  14. package/docs/INGRESS.md +746 -0
  15. package/docs/NATIVE-ROUTE.md +53 -0
  16. package/docs/REPO_MAP.md +279 -0
  17. package/docs/adr/0001-two-wire-formats.md +39 -0
  18. package/docs/adr/0002-zero-dom-fast-schema.md +38 -0
  19. package/docs/adr/0003-bun-builtins-delegation.md +43 -0
  20. package/docs/bun-builtins-decision-matrix.md +115 -0
  21. package/index.ts +44 -8
  22. package/package.json +98 -33
  23. package/src/ingress/body.ts +289 -0
  24. package/src/ingress/constants.ts +220 -48
  25. package/src/ingress/context.ts +136 -0
  26. package/src/ingress/decode/baked-result.ts +121 -0
  27. package/src/ingress/decode/fast-result.ts +160 -0
  28. package/src/ingress/decode/packed-sections.ts +56 -0
  29. package/src/ingress/decode/result-base.ts +172 -0
  30. package/src/ingress/errors.ts +72 -0
  31. package/src/ingress/fast.ts +152 -1021
  32. package/src/ingress/handlers.ts +639 -1001
  33. package/src/ingress/headers/baked-templates.ts +102 -0
  34. package/src/ingress/headers/cors.ts +44 -0
  35. package/src/ingress/headers/fast-templates.ts +238 -0
  36. package/src/ingress/headers/hsts.ts +83 -0
  37. package/src/ingress/health.ts +68 -0
  38. package/src/ingress/index.ts +142 -405
  39. package/src/ingress/metrics.ts +132 -0
  40. package/src/ingress/options.ts +197 -0
  41. package/src/ingress/packing/gather-raw-headers.ts +139 -0
  42. package/src/ingress/packing/header-packing.ts +75 -0
  43. package/src/ingress/packing/input-packer.ts +164 -0
  44. package/src/ingress/packing/scratch.ts +50 -0
  45. package/src/ingress/packing/select-headers.ts +102 -0
  46. package/src/ingress/response/error-bodies.ts +75 -0
  47. package/src/ingress/response/terminal.ts +59 -0
  48. package/src/ingress/router.ts +237 -0
  49. package/src/ingress/routes/common.ts +79 -0
  50. package/src/ingress/routes/delete.ts +14 -0
  51. package/src/ingress/routes/echo.ts +107 -0
  52. package/src/ingress/routes/fallback.ts +23 -0
  53. package/src/ingress/routes/head.ts +27 -0
  54. package/src/ingress/routes/index.ts +11 -0
  55. package/src/ingress/routes/json-write.ts +129 -0
  56. package/src/ingress/routes/options.ts +26 -0
  57. package/src/ingress/routes/read.ts +35 -0
  58. package/src/ingress/routes/responder.ts +159 -0
  59. package/src/ingress/server-node.ts +376 -0
  60. package/src/ingress/server.ts +475 -0
  61. package/src/ingress/shared.ts +132 -0
  62. package/src/ingress/status.ts +97 -0
  63. package/src/ingress/types.ts +191 -0
  64. package/src/integration/batch.ts +58 -0
  65. package/src/integration/index.ts +29 -0
  66. package/src/integration/pipeline.ts +220 -0
  67. package/src/integration/streaming.ts +78 -0
  68. package/src/integration/websocket.ts +93 -0
  69. package/src/loader/batch.ts +137 -0
  70. package/src/loader/cost.ts +105 -0
  71. package/src/loader/index.ts +745 -0
  72. package/src/loader/ops.ts +657 -0
  73. package/src/loader/state.ts +84 -0
  74. package/src/loader/types.ts +145 -0
  75. package/src/native/ffi/constants.ts +58 -0
  76. package/src/native/ffi/selftest.ts +729 -0
  77. package/src/native/ffi/types.ts +474 -0
  78. package/src/native/ffi.ts +1775 -0
  79. package/src/native/index.ts +8 -207
  80. package/src/native/loader.ts +240 -0
  81. package/src/native/types/instances.ts +274 -0
  82. package/src/native/types.ts +401 -0
  83. package/src/runtime/builtins.ts +131 -0
  84. package/src/runtime/codec.ts +147 -0
  85. package/src/runtime/detect.ts +36 -0
  86. package/src/runtime/index.ts +28 -0
  87. package/src/runtime/native.ts +35 -0
  88. package/src/runtime/server.ts +31 -0
  89. package/src/runtime/transport.ts +64 -0
  90. package/src/runtime/types.ts +173 -0
  91. package/src/runtime/uuid.ts +45 -0
  92. package/src/runtime/websocket.ts +30 -0
  93. package/src/rust-ffi/addon.ts +10 -0
  94. package/src/rust-ffi/batch/build.ts +155 -0
  95. package/src/rust-ffi/batch/index.ts +8 -0
  96. package/src/rust-ffi/batch/types.ts +92 -0
  97. package/src/rust-ffi/client.ts +87 -0
  98. package/src/rust-ffi/context.ts +193 -0
  99. package/src/rust-ffi/index.ts +51 -541
  100. package/src/rust-ffi/into.ts +21 -0
  101. package/src/rust-ffi/options.ts +113 -0
  102. package/src/rust-ffi/packed.ts +253 -0
  103. package/src/rust-ffi/proven.ts +28 -0
  104. package/src/rust-ffi/scalar/crypto.ts +346 -0
  105. package/src/rust-ffi/scalar/factories.ts +459 -0
  106. package/src/rust-ffi/scalar/hashing.ts +74 -0
  107. package/src/rust-ffi/scalar/http.ts +337 -0
  108. package/src/rust-ffi/scalar/index.ts +31 -0
  109. package/src/rust-ffi/scalar/interface.ts +325 -0
  110. package/src/rust-ffi/scalar/json-packed.ts +175 -0
  111. package/src/rust-ffi/scalar/json.ts +61 -0
  112. package/src/rust-ffi/scalar/payload.ts +241 -0
  113. package/src/rust-ffi/text.ts +121 -0
  114. package/src/selection.json +194 -0
  115. package/src/selection.ts +73 -0
  116. package/src/shared/adaptive.ts +67 -0
  117. package/src/shared/buffer-pool.ts +264 -0
  118. package/src/shared/bytes.ts +108 -9
  119. package/src/shared/codec.ts +78 -0
  120. package/src/shared/env.ts +20 -0
  121. package/src/shared/log.ts +75 -0
  122. package/src/shared/metrics.ts +247 -0
  123. package/src/shared/packed/index.ts +10 -0
  124. package/src/shared/packed/parsers.ts +31 -0
  125. package/src/shared/packed/schema.ts +23 -0
  126. package/src/shared/packed/wire.ts +504 -0
  127. package/src/shared/proven.ts +289 -0
  128. package/src/shared/request-id.ts +67 -0
  129. package/src/shared/response.ts +104 -0
  130. package/src/shared/runtime.ts +29 -0
  131. package/src/shared/trace.ts +81 -0
  132. package/src/shared/uuid.ts +15 -0
  133. package/Cargo.toml +0 -73
  134. package/bench.ts +0 -3
  135. package/rust/batch.rs +0 -195
  136. package/rust/cookie_parser.rs +0 -70
  137. package/rust/cors.rs +0 -263
  138. package/rust/hashing.rs +0 -164
  139. package/rust/headers.rs +0 -234
  140. package/rust/hmac_sha256.rs +0 -113
  141. package/rust/http_parser.rs +0 -132
  142. package/rust/ingress.rs +0 -616
  143. package/rust/ingress_constants.rs +0 -89
  144. package/rust/ip_trust.rs +0 -185
  145. package/rust/json_ops.rs +0 -66
  146. package/rust/json_patch_ops.rs +0 -25
  147. package/rust/json_schema.rs +0 -151
  148. package/rust/json_ser.rs +0 -429
  149. package/rust/lib.rs +0 -48
  150. package/rust/method.rs +0 -141
  151. package/rust/mime_lookup.rs +0 -76
  152. package/rust/output.rs +0 -238
  153. package/rust/proxy.rs +0 -90
  154. package/rust/query_parser.rs +0 -141
  155. package/rust/random_token.rs +0 -52
  156. package/rust/rate_limit.rs +0 -203
  157. package/rust/terminal.rs +0 -95
  158. package/rust/unit_tests.rs +0 -843
  159. package/rust/url_codec.rs +0 -133
  160. package/rust/util.rs +0 -537
  161. package/rust/validation.rs +0 -107
  162. package/rust/websocket.rs +0 -23
  163. package/src/baseline/index.ts +0 -12
  164. package/src/baseline/tasks/cookie.ts +0 -22
  165. package/src/baseline/tasks/hashing.ts +0 -18
  166. package/src/baseline/tasks/hmac.ts +0 -38
  167. package/src/baseline/tasks/http.ts +0 -62
  168. package/src/baseline/tasks/json-patch.ts +0 -12
  169. package/src/baseline/tasks/json.ts +0 -50
  170. package/src/baseline/tasks/mime.ts +0 -5
  171. package/src/baseline/tasks/query.ts +0 -14
  172. package/src/baseline/tasks/token.ts +0 -15
  173. package/src/baseline/tasks/url.ts +0 -11
  174. package/src/baseline/tasks/validation.ts +0 -19
  175. package/src/baseline/tasks/websocket.ts +0 -14
  176. package/src/bench/assert.ts +0 -70
  177. package/src/bench/checks.ts +0 -227
  178. package/src/bench/checksum.ts +0 -27
  179. package/src/bench/comparisons.ts +0 -70
  180. package/src/bench/concurrent-worker.ts +0 -135
  181. package/src/bench/fixtures.ts +0 -233
  182. package/src/bench/index.ts +0 -2
  183. package/src/bench/measure.ts +0 -177
  184. package/src/bench/now.ts +0 -3
  185. package/src/bench/report.ts +0 -79
  186. package/src/bench/run.ts +0 -57
  187. package/src/bench/tasks/complex.ts +0 -232
  188. package/src/bench/tasks/concurrent.ts +0 -213
  189. package/src/bench/tasks/cookie.ts +0 -44
  190. package/src/bench/tasks/hashing.ts +0 -33
  191. package/src/bench/tasks/hmac.ts +0 -36
  192. package/src/bench/tasks/http.ts +0 -50
  193. package/src/bench/tasks/index.ts +0 -52
  194. package/src/bench/tasks/json-patch.ts +0 -21
  195. package/src/bench/tasks/json.ts +0 -33
  196. package/src/bench/tasks/mime.ts +0 -21
  197. package/src/bench/tasks/query.ts +0 -44
  198. package/src/bench/tasks/stress.ts +0 -142
  199. package/src/bench/tasks/token.ts +0 -20
  200. package/src/bench/tasks/url.ts +0 -34
  201. package/src/bench/tasks/validation.ts +0 -57
  202. package/src/bench/tasks/websocket.ts +0 -21
  203. package/src/bench/types.ts +0 -60
  204. package/src/data/json-rows.ts +0 -33
  205. package/src/ingress/packed-input.ts +0 -59
  206. package/src/shared/json.ts +0 -18
  207. package/src/shared/packed.ts +0 -316
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 castrum contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,262 +1,40 @@
1
1
  # castrum
2
2
 
3
- **Enterprise-Grade Bun + Rust FFI Runtime Benchmark Package**
3
+ **Rust-accelerated primitives and a production-grade HTTP pipeline for Bun — and Node.js.**
4
4
 
5
- A high-performance Bun backend framework with Rust-accelerated FFI functions, providing production-grade HTTP ingress pipeline, validation, hashing, JSON processing, and more — all with benchmark-grade performance.
5
+ castrum is a hybrid TypeScript + Rust package. A single Rust addon does the
6
+ performance-critical work — hashing, JSON validation, parsing, crypto,
7
+ compression, and a full HTTP request pipeline — while TypeScript gives you a
8
+ small, flat API. Bun talks to the addon through `bun:ffi` (a ~10–20 ns C-ABI
9
+ call); Node.js falls back to NAPI automatically.
6
10
 
7
- ---
11
+ ## Why castrum
8
12
 
9
- ## Architecture Overview
13
+ - **Native speed without the plumbing** — call `rust.crc32()`, `rust.jsonValid()`,
14
+ `rust.validateEmail()`, … directly from TypeScript. No WASM, no subprocess.
15
+ - **A real HTTP server in a few lines** — `createIngressServer` wires CORS, rate
16
+ limiting, body-size guards, JSON-schema validation, and security headers for you.
17
+ - **Zero-copy hot paths** — request data travels in packed binary buffers shared
18
+ with Rust, so there's no serialization and no per-request allocation.
19
+ - **Honest performance data** — the CPU benchmark (`bun run check`) races every
20
+ `rust.*` op against a pure-JS baseline and writes a machine-readable report, so
21
+ you always know what to reach for.
22
+ - **Bun first, Node ready** — the same API runs on Node ≥ 20.3 via a compiled ESM
23
+ entry.
10
24
 
11
- ```
12
- ┌─────────────────────────────────────────────────────────────────┐
13
- │ TypeScript (Bun) Layer │
14
- │ │
15
- │ index.ts (entry point) │
16
- │ ├── src/rust-ffi/ Raw Rust FFI bindings │
17
- │ ├── src/native/ NAPI native addon loader │
18
- │ ├── src/ingress/ High-performance HTTP ingress pipeline │
19
- │ ├── src/baseline/ JS baseline (benchmark reference) │
20
- │ ├── src/data/ Data serialization utilities │
21
- │ └── src/shared/ Shared constants and helpers │
22
- │ │
23
- ├─────────────────── NAPI Bridge (napi-rs) ──────────────────────┤
24
- │ │
25
- │ Rust (cdylib) Layer │
26
- │ │
27
- │ rust/lib.rs │
28
- │ ├── Core Pipeline: ingress, cors, rate_limit, ip_trust │
29
- │ ├── Parsers: http_parser, query_parser, cookie_parser │
30
- │ ├── Validation: validation (email/uuid/IP) │
31
- │ ├── JSON: json_ops, json_patch_ops, json_schema, │
32
- │ │ json_ser │
33
- │ ├── Crypto: hashing, hmac_sha256, random_token │
34
- │ ├── Data: url_codec, mime_lookup, websocket │
35
- │ ├── Batch: batch │
36
- │ ├── Utilities: util, method, headers, output, terminal │
37
- └─────────────────────────────────────────────────────────────────┘
38
- ```
39
-
40
- ## Quick Start
41
-
42
- ```bash
43
- # Install dependencies
44
- bun install
45
-
46
- # Build Rust native addon (release)
47
- bun run build
48
-
49
- # Run benchmarks
50
- bun bench.ts
51
-
52
- # Run tests
53
- bun test
54
- ```
25
+ > **Want the full story?** See the [case study](./docs/CASE_STUDY.md) for how
26
+ > castrum was built and measured — and where native speed did (and didn't) pay off.
55
27
 
56
- ## Build
28
+ ## Quick start
57
29
 
58
- ```bash
59
- # Production build
60
- bun run build
61
-
62
- # Debug build
63
- bun run build:debug
64
- ```
65
-
66
- ## Benchmarks
30
+ ### 1. Install
67
31
 
68
32
  ```bash
69
- # Full benchmark suite
70
- bun bench.ts
71
-
72
- # HTTP server benchmarks
73
- bun run bench:http
74
-
75
- # Compare with specific frameworks
76
- bun run bench:http:elysia # vs Elysia
77
- bun run bench:http:ingress # vs Ingress (native)
78
- bun run bench:http:bun-only # vs raw Bun
79
-
80
- # Load patterns
81
- bun run bench:http:smoke # smoke test
82
- bun run bench:http:stress # stress test
83
- bun run bench:http:heavy # heavy JSON
84
- bun run bench:http:crud # CRUD operations
85
- bun run bench:http:soak # soak (endurance) test
86
- bun run bench:http:storm # storm (burst) test
87
- bun run bench:http:boundary # boundary conditions
88
- ```
89
-
90
- ## API Reference
91
-
92
- ### Core Exports
93
-
94
- ```ts
95
- import { rust, native, rustBatch } from "castrum";
96
-
97
- // `rust` — All Rust FFI native implementations (flat, complete API)
98
- // `native` — JavaScript/Bun baseline implementations for benchmarking
99
- // `rustBatch` — Back-compat alias === rust.batch
100
- ```
101
-
102
- ### Rust Utility API
103
-
104
- `rust` is a single flat object exposing **every** native Rust utility with
105
- optimized defaults baked in. Text-oriented operations also have ergonomic
106
- string variants under `rust.text`.
107
-
108
- | Namespace | Description |
109
- |-----------|-------------|
110
- | `rust.crc32(...)`, `rust.jsonValid(...)`, … | Scalar utilities (bytes in → typed out) |
111
- | `rust.text.mimeFromExtension(".js")`, … | String ergonomics (string in → string/bool out) |
112
- | `rust.batch.jsonValid(docs)`, … | High-throughput batch (arrays → unpacked results) |
113
- | `rust.packed.jsonValidBatchPacked(p)`, … | Raw packed low-level variants + metadata |
114
- | `rust.configure({ ... })` | Override defaults on the shared instance |
115
-
116
- #### Scalar utilities
117
-
118
- | Function | Returns |
119
- |----------|---------|
120
- | `crc32(input)` | `number` — CRC32 checksum |
121
- | `fnv1a64(input)` | `bigint` — FNV-1a 64-bit hash |
122
- | `hmacSha256(key, data)` | `Uint8Array` — HMAC-SHA256 signature |
123
- | `hmacSha256Verify(key, data, sig)` | `boolean` |
124
- | `jsonValid(input)` | `boolean` — JSON validity check |
125
- | `jsonSumIds(input)` | `bigint` — sum of numeric `id` fields |
126
- | `jsonPatch(doc, patch)` | `Uint8Array` — RFC 6902 result |
127
- | `mimeFromExtension(ext)` | `Uint8Array` — MIME type |
128
- | `randomToken(byteLen)` | `Uint8Array` — CSPRNG token |
129
- | `urlEncode(input)` / `urlDecode(input)` | `Uint8Array` — percent encode/decode |
130
- | `urlDecodeBytes(input)` | `Uint8Array` — strict %-decode (no UTF-8 check) |
131
- | `validateEmail/Uuid/Ipv4/Ipv6(input)` | `boolean` |
132
- | `wsAcceptKey(key)` | `Uint8Array` — WebSocket accept key |
133
-
134
- Low-level / packed variants: `rust.httpParseRequestPacked`, `rust.queryParsePacked`,
135
- `rust.cookieParsePacked`.
136
-
137
- #### String ergonomics (`rust.text`)
138
-
139
- ```ts
140
- rust.text.mimeFromExtension(".js"); // "text/javascript"
141
- rust.text.mimeFromExtension("html"); // "text/html"
142
- rust.text.urlEncode("a b"); // "a%20b"
143
- rust.text.urlDecode("a%20b"); // "a b"
144
- rust.text.wsAcceptKey(keyBase64); // base64 accept key
145
- rust.text.validateEmail("a@b.co"); // true
146
- ```
147
-
148
- #### Batch operations (`rust.batch`)
149
-
150
- All batch helpers accept `Uint8Array[]` and return unpacked, ready-to-use results:
151
-
152
- ```ts
153
- const docs = [encoder.encode('{"id":1}'), encoder.encode('{"id":2}')];
154
-
155
- rust.batch.jsonValid(docs); // Uint8Array bitset (1 per doc)
156
- rust.batch.crc32(docs); // Uint32Array
157
- rust.batch.jsonSumIds(docs); // BigInt64Array
158
- rust.batch.queryParse(docs); // Uint8Array[]
159
- rust.batch.schemaValidate(validator, docs); // bitset
160
- ```
161
-
162
- Raw packed + metadata variants (advanced): `rust.packed.*` mirrors the native
163
- functions 1:1 (`jsonValidBatchPacked`, `jsonValidBatchCountPacked`,
164
- `queryParseBatchTotalLenPacked`, …).
165
-
166
- #### Configuring defaults
167
-
168
- Defaults are selected automatically, and can be overridden:
169
-
170
- ```ts
171
- // Defaults:
172
- // rayonThreads = max(1, hardwareConcurrency - 1)
173
- // (env override: CASTRUM_RAYON_THREADS / RUST_RAYON_THREADS)
174
- // mimeCache = true
175
- // hmacCache = true
176
-
177
- // Override per-instance options on the shared instance:
178
- rust.configure({ mimeCache: false, hmacCache: false });
179
-
180
- // Or create an isolated instance with custom defaults:
181
- import { createRust } from "castrum";
182
- const myRust = createRust({ mimeCache: false });
183
- ```
184
-
185
- > **Thread pool note**: the rayon pool is process-wide and initialized **once**
186
- > (native `OnceLock`) — the first initialization wins. Because importing the
187
- > module initializes it with the default, set
188
- > `CASTRUM_RAYON_THREADS` / `RUST_RAYON_THREADS` (or call
189
- > `rust.configure({ rayonThreads })` / `createRust({ rayonThreads })` before any
190
- > other pool use) to tune it. `mimeCache` / `hmacCache` are per-instance and can
191
- > be toggled any time.
192
-
193
- ### Sub-modules
194
-
195
- | Import | Description |
196
- |--------|-------------|
197
- | `import { encoder, decoder } from "castrum"` | Text encode/decode utilities |
198
- | `import { packBatch, packPairs, unpackBitset, unpackByteResults, unpackI64ArrayAsBigInt, unpackU32Array } from "castrum"` | Batch/pair packing + unpacking utilities |
199
- | `import { readPairsPacked, pairsToObject, readHttpPacked } from "castrum"` | Decode packed pairs / parsed HTTP buffers |
200
- | `import { parseQueryString, parseCookieHeader } from "castrum"` | High-level string parsers (no hand-packing) |
201
- | `import { jsonRowsBytes, createJsonRows } from "castrum"` | JSON row serialization |
202
- | `import { createIngress, createIngressSync, createIngressFast } from "castrum"` | HTTP ingress pipeline (low-level) |
203
- | `import { createIngressHandler, createIngressServer, readHandler, jsonWriteHandler, echoHandler, fallbackHandler } from "castrum"` | **Pre-baked** ingress handlers — route factories + Bun.serve builder |
204
-
205
- ```ts
206
- import { parseQueryString, parseCookieHeader } from "castrum";
207
-
208
- parseQueryString("a=1&b=2&tag=a&tag=b"); // { a: "1", b: "2", tag: ["a", "b"] }
209
- parseCookieHeader("session=abc; theme=dark"); // { session: "abc", theme: "dark" }
210
- ```
211
-
212
- > **Environment variables**: See [docs/ENVIRONMENT.md](./docs/ENVIRONMENT.md) for the
213
- > full reference of `INGRESS_*`, `CASTRUM_*` and `RUST_*` runtime variables.
214
-
215
- ### Ingress Pipeline
216
-
217
- The ingress system is a high-performance request processing pipeline that handles:
218
-
219
- - **Method routing** — GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS
220
- - **CORS evaluation** — Origin validation, preflight handling
221
- - **Rate limiting** — Token-bucket per IP with configurable window
222
- - **IP trust & proxy** — X-Forwarded-For, X-Real-IP resolution
223
- - **HTTPS detection** — X-Forwarded-Proto based
224
- - **Cookie parsing** — Structured JSON output
225
- - **Query parsing** — Key-value pair extraction
226
- - **JSON body validation** — Syntax + optional schema validation
227
- - **Body size guarding** — Configurable max body size
228
- - **Security headers** — HSTS, CSP, X-Frame-Options, etc.
229
-
230
- ```ts
231
- // Fast synchronous handler (zero allocations per call)
232
- import { createIngressFast } from "castrum";
233
-
234
- const handler = createIngressFast({
235
- cors: { allowOrigin: ["https://example.com"] },
236
- rateLimit: { limit: 100, windowMs: 60_000 },
237
- parseCookies: true,
238
- parseQuery: true,
239
- });
240
-
241
- // Inside a Bun server:
242
- Bun.serve({
243
- port: 3000,
244
- async fetch(req) {
245
- return handler.run(req, req.headers.get("x-forwarded-for"), null, "", (result) => {
246
- if (result.ok) {
247
- return new Response("OK", { status: 200 });
248
- }
249
- return new Response("Error", { status: result.status });
250
- });
251
- },
252
- });
33
+ bun install
34
+ bun run build # compile the Rust addon (release)
253
35
  ```
254
36
 
255
- #### Pre-baked handlers (recommended for servers)
256
-
257
- `src/ingress/handlers.ts` provides ready-to-use route handlers and a `Bun.serve`
258
- builder so **any system can consume the optimized ingress pipeline in a few
259
- lines** — no need to hand-build responses or manage header templates:
37
+ ### 2. Start a server
260
38
 
261
39
  ```ts
262
40
  import { createIngressHandler, createIngressServer } from "castrum";
@@ -268,165 +46,173 @@ const ingress = createIngressHandler({
268
46
  rateLimit: { limit: 1000, windowMs: 60_000 },
269
47
  });
270
48
 
271
- createIngressServer({
49
+ const server = createIngressServer({
272
50
  port: 3000,
273
51
  routes: {
274
- "/health": { read: ingress }, // GET + HEAD
275
- "/api/users": { read: ingress, write: ingress }, // + POST/PUT/PATCH
276
- "/api/echo": { echo: ingress }, // POST echo
52
+ "/health": { read: ingress }, // GET + HEAD
53
+ "/api/users": { read: ingress, write: ingress }, // + POST/PUT/PATCH (+OPTIONS)
54
+ "/api/echo": { echo: ingress }, // POST echo
277
55
  },
278
56
  });
279
- ```
280
57
 
281
- The route factories (`readHandler`, `headHandler`, `jsonWriteHandler`,
282
- `echoHandler`, `fallbackHandler`) are runtime-agnostic — use them with any
283
- fetch-style server. The benchmark server (`bench/servers/ingress-server.ts`)
284
- is built entirely from these and re-exports them. See
285
- [docs/INGRESS.md](./docs/INGRESS.md) for the full pre-baked API reference.
58
+ console.log("listening on", server.port);
59
+ ```
286
60
 
61
+ Run it with `bun`, then `curl http://localhost:3000/health`. A complete
62
+ walkthrough lives in [`examples/basic-server.ts`](examples/basic-server.ts) and
63
+ the tutorial in [`docs/GETTING_STARTED.md`](./docs/GETTING_STARTED.md).
287
64
 
288
- ---
65
+ ### 3. Call native primitives
289
66
 
290
- ## Project Structure
67
+ ```ts
68
+ import { rust, encoder } from "castrum";
291
69
 
292
- ```
293
- castrum/
294
- ├── index.ts # Entry point / public API
295
- ├── AGENTS.md # AI agent instructions (commands, constraints, gotchas)
296
- ├── bench.ts # Benchmark runner
297
- ├── build.rs # NAPI build script
298
- ├── Cargo.toml # Rust dependencies & build config
299
- ├── package.json # Node/Bun package config
300
- ├── tsconfig.json # TypeScript config
301
- │
302
- ├── rust/ # Rust source (cdylib)
303
- │ ├── lib.rs # Crate root, module declarations
304
- │ ├── ingress.rs # NAPI ingress class
305
- │ ├── cors.rs # CORS engine
306
- │ ├── rate_limit.rs # Keyed rate limiter
307
- │ ├── ip_trust.rs # IP trust & proxy resolution
308
- │ ├── http_parser.rs # HTTP request parser
309
- │ ├── query_parser.rs # Query string parser
310
- │ ├── cookie_parser.rs # Cookie header parser
311
- │ ├── validation.rs # Email/UUID/IP validation
312
- │ ├── hashing.rs # FNV-1a & fast hashing
313
- │ ├── hmac_sha256.rs # HMAC-SHA256
314
- │ ├── json_ops.rs # JSON operations
315
- │ ├── json_patch_ops.rs # JSON Patch operations
316
- │ ├── json_schema.rs # JSON Schema validation
317
- │ ├── json_ser.rs # JSON serialization utilities
318
- │ ├── url_codec.rs # URL encode/decode
319
- │ ├── mime_lookup.rs # MIME type lookup
320
- │ ├── websocket.rs # WebSocket utilities
321
- │ ├── random_token.rs # Secure random token generation
322
- │ ├── batch.rs # Batch processing
323
- │ ├── method.rs # HTTP method enum
324
- │ ├── headers.rs # Header packing
325
- │ ├── output.rs # Output buffer layout
326
- │ ├── terminal.rs # Terminal response writers
327
- │ ├── proxy.rs # Proxy detection utilities
328
- │ ├── util.rs # Core utilities
329
- │ └── ingress_constants.rs # NAPI-exported constants
330
- │
331
- ├── src/ # TypeScript source
332
- │ ├── ingress/ # Ingress pipeline (TS layer)
333
- │ │ ├── index.ts # Public API + async factory
334
- │ │ ├── fast.ts # Fast synchronous handler (packed input)
335
- │ │ ├── handlers.ts # PRE-BAKED handlers + createIngressServer
336
- │ │ ├── constants.ts # Layout constants (from Rust)
337
- │ │ └── packed-input.ts # Input buffer packing
338
- │ ├── native/ # Native addon loader
339
- │ │ └── index.ts # Module loading + type definitions
340
- │ ├── rust-ffi/ # Rust FFI bindings
341
- │ │ └── index.ts # Flat, complete FFI API
342
- │ ├── baseline/ # JS baseline implementations
343
- │ │ ├── index.ts # Aggregator
344
- │ │ └── tasks/ # Individual baseline implementations
345
- │ ├── bench/ # Benchmark framework
346
- │ ├── data/ # Data serialization
347
- │ └── shared/ # Shared utilities
348
- │
349
- ├── bench/ # HTTP benchmark servers
350
- │ ├── servers/ # bun-server.ts, elysia-server.ts, ingress-server.ts (+ shared.ts)
351
- │ ├── load.ts # HTTP load generator & scenarios
352
- │ └── run-bench.ts # Benchmark runner
353
- │
354
- ├── test/ # Tests (see CONTRIBUTING.md → Testing Requirements)
355
- ├── docs/ # Documentation
356
- └── scripts/ # Build/utility scripts
70
+ rust.crc32(encoder.encode("hello")); // number
71
+ rust.jsonValid(encoder.encode('{"a":1}')); // boolean (zero-DOM)
72
+ rust.validateEmail(encoder.encode("a@b.co")); // boolean
73
+ rust.text.mimeFromExtension(".js"); // "text/javascript"
74
+ rust.batch.jsonValid([docA, docB]); // Uint8Array bitset (one packed call)
357
75
  ```
358
76
 
359
- ---
77
+ Everything is synchronous. For repeated calls, the higher-order
78
+ [`loader`](#api-at-a-glance) batches and caches for you automatically.
360
79
 
361
- ## Performance Characteristics
80
+ ## Node.js support
362
81
 
363
- All Rust FFI functions are designed for **zero-allocation** paths where possible:
82
+ Bun is the primary target — it resolves the `bun` export condition to raw
83
+ `index.ts` (zero startup cost). Node.js ≥ 20.3 resolves the `node`/`default`
84
+ condition to a compiled ESM bundle (`dist/`, built with `bun run build:js`). It's
85
+ the same cdylib either way: Bun uses `bun:ffi`, Node uses the NAPI fallback, so
86
+ `rust.*` calls behave identically on both runtimes.
364
87
 
365
- - **Ingress pipeline**: Single output buffer written by Rust, read by JS — minimal copies
366
- - **Validation functions**: Pure CPU-bound, 10-100x faster than JS equivalents
367
- - **Hashing**: SIMD-optimized crates under the hood
368
- - **Batch operations**: Packed binary format minimizes NAPI boundary crossings
369
- - **JSON parsing**: Uses `sonic-rs` (Rust) — one of the fastest JSON parsers available
88
+ ```ts
89
+ // Node (ESM) — the same pre-baked handlers over node:http
90
+ import { createIngressServerNode } from "castrum";
91
+ const srv = createIngressServerNode({ port: 3000, routes: { "/health": { read: ingress } } });
92
+ await srv.ready; // resolves once listening
93
+ ```
370
94
 
371
- ---
95
+ The public `createIngressServer` is runtime-adaptive — it picks `Bun.serve` on
96
+ Bun and `node:http` on Node; `createIngressServerNode` stays available for pinned
97
+ Node users with the same route handlers. `rust.transport()` /
98
+ `rust.ffiActive()` report which transport is live, and `CASTRUM_FFI_MODE` forces
99
+ one (see [`docs/ENVIRONMENT.md`](./docs/ENVIRONMENT.md)).
372
100
 
373
- ## Testing
101
+ ## How it works
374
102
 
375
- ```bash
376
- # Run all tests
377
- bun test
103
+ TypeScript is the API; a Rust cdylib (`rust/`, one crate) is the engine. Bun
104
+ `dlopen`s the addon and JIT-calls its `extern "C"` exports — that's `bun:ffi`,
105
+ the primary transport (~10–20 ns per crossing). On Node, or if the bind-time
106
+ ffi self-test fails, the same addon loads through napi-rs (NAPI). Request data is
107
+ packed into shared, length-prefixed buffers so hot paths allocate nothing per
108
+ request. See [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) for the full deep
109
+ dive.
378
110
 
379
- # Run specific test file
380
- bun test test/unit/ingress/fast.test.ts
111
+ ## Benchmarks
381
112
 
382
- # Run tests with coverage
383
- bun test --coverage
384
- ```
113
+ Two suites ship with the repo:
385
114
 
386
- ---
115
+ - **CPU benchmark** — `bun run check` races every `rust.*` op against a pure-JS
116
+ baseline (and Bun's built-ins) and writes a machine-readable report to
117
+ `bench/results/cpu/`.
118
+ - **HTTP benchmark** — `bun run bench:http` compares raw `Bun.serve`, Elysia,
119
+ and the castrum ingress server across ~20 load scenarios (smoke, stress,
120
+ soak, spike, heavy JSON, slowloris, …).
387
121
 
388
- ## Publishing
122
+ ```bash
123
+ bun run check # CPU benchmark + correctness (writes latest.json)
124
+ bun run bench:http # HTTP benchmark, all servers
125
+ bun run bench:http:smoke # fast HTTP sanity — the CI wire-format guard
126
+ ```
127
+
128
+ For scenario details, how to read the reports, and the benchmark-specific env
129
+ controls, see [`docs/BENCHMARKS.md`](./docs/BENCHMARKS.md). The keep/delegate
130
+ decisions for ops where Bun's built-ins win (gzip, crc32, xxh3, `randomUUIDv7`,
131
+ …) live in [`docs/bun-builtins-decision-matrix.md`](./docs/bun-builtins-decision-matrix.md).
132
+
133
+ ## API at a glance
134
+
135
+ The complete, verified reference lives in [`docs/API.md`](./docs/API.md). This
136
+ is the map:
137
+
138
+ | Export | What it is |
139
+ |--------|-----------|
140
+ | `rust` | The flat native surface: hashing, JSON, validation, URL/cookie/query/form parsing, MIME, WebSocket/SSE, crypto (HMAC, JWT, AEAD, argon2, bcrypt, PBKDF2), signed cookies, CSRF, gzip/brotli, and more. |
141
+ | `rust.text.*` | String-in / string-out ergonomics over `rust.*`. |
142
+ | `rust.batch.*` | Array-of-bytes → native results (bitsets, typed arrays, byte arrays) in one packed call. |
143
+ | `rust.packed.*` | Raw packed-wire low-level variants + metadata. |
144
+ | `loader` / `createLoader` | Higher-order loader: pre-bound ops, automatic scalar-vs-bulk dispatch, DataLoader-style coalescing, LRU cache. |
145
+ | `opImpl` / `isNativeOp` / `opDecision` | Benchmark-driven native-vs-JS selection hints for framework consumers. |
146
+ | `createIngressHandler` + route factories | Pre-baked HTTP pipeline: CORS, rate limit, body guard, schema validation, security headers. |
147
+ | `createIngressServer` / `createIngressServerNode` | Server builders — the runtime adapter picks `Bun.serve` on Bun and `node:http` on Node. |
148
+ | `createIngress` / `createIngressFast` / `createIngressSync` | Lower-level ingress entry points. |
149
+ | `createPipeline` / `createWebSocketUpgrade` / `sseResponse` | Framework-agnostic integration helpers. |
150
+ | `createMetrics` / `createIngressMetrics` / `livenessHandler` / `readinessHandler` / trace helpers | Zero-dependency observability. |
151
+ | `uuidv7` / `AdaptiveEstimate` / packing & parsing utilities | Shared utilities (`encoder`, `decoder`, `packBatch`, `parseQueryString`, …). |
389
152
 
390
- The package ships **all platform native addons in a single tarball**, so a release
391
- needs every `napi.targets` platform artifact present (see `scripts/prepublish.mjs`).
153
+ ```ts
154
+ import { rust, loader } from "castrum";
392
155
 
393
- ### Automatic (recommended)
156
+ rust.jsonValid(bytes); // scalar boolean
157
+ const isEmail = loader("validateEmail");
158
+ isEmail([a, b, c]); // one packed batch call
159
+ ```
394
160
 
395
- CI builds every platform addon and publishes to npm when you push a `v*` tag.
396
- Bump `version` in `package.json` (and `Cargo.toml`), add a `CHANGELOG.md` entry,
397
- then:
161
+ > **The selection surface** — framework consumers that want to bind each op to
162
+ > a fixed implementation at load time use `opImpl(op)` / `isNativeOp(op)` /
163
+ > `opDecision(op)` (source of truth: `rust/selection.rs` + `src/selection.json`,
164
+ > audited by CI). They read it once at startup — they don't swap native↔js per
165
+ > call. See [`docs/API.md`](./docs/API.md) and
166
+ > [`docs/bun-builtins-decision-matrix.md`](./docs/bun-builtins-decision-matrix.md).
167
+
168
+ ## Testing
398
169
 
399
170
  ```bash
400
- git tag v0.6.0 && git push origin v0.6.0
171
+ bun test # TypeScript unit tests (~540)
172
+ bun run test:rust # Rust unit tests (cargo test)
173
+ bun run typecheck # TypeScript typecheck
174
+ bun run test:node # Node.js integration tests (run `bun run build:js` first)
175
+ bun run bench:http:smoke # HTTP smoke — the CI wire-format guard
401
176
  ```
402
177
 
403
- ### Manual
178
+ For coverage floors, the typecheck/test configs, and the full test matrix, see
179
+ [`docs/REPO_MAP.md`](./docs/REPO_MAP.md).
180
+
181
+ ## Publishing
404
182
 
405
- To build and publish a release by hand (no CI needed; npm logged in or `NPM_TOKEN`
406
- exported):
183
+ The package ships **all platform addons in a single tarball**. Push a `v*` tag
184
+ and CI builds + publishes every platform to npm; or use `bun run publish:manual`
185
+ for a quick single-platform release:
407
186
 
408
187
  ```bash
409
- bun run publish:manual # build + publish current version
410
- bun run publish:manual -- --increment minor # bump version + tag + push + publish
411
- bun run publish:manual:dry # print the plan, change nothing
188
+ bun run publish:manual -- --increment minor # bump + tag + build + publish
189
+ bun run publish:manual:dry # print the plan, change nothing
412
190
  ```
413
191
 
414
- `publish:manual` bumps the version (`--increment`), creates + pushes the `v<version>`
415
- git tag via `bun pm version` (keeping `Cargo.toml`, `Cargo.lock`, and `CHANGELOG.md`
416
- in sync), builds the host addon locally, and runs `npm publish --access public`.
192
+ Run `bun run check:version` before tagging (it verifies `package.json`,
193
+ `Cargo.toml`, and `CHANGELOG.md` agree). See [`docs/REPO_MAP.md`](./docs/REPO_MAP.md)
194
+ §6 for the full flow.
417
195
 
418
- > **Platform note:** because only the current platform can be built locally, the
419
- > manual publish ships a **single-platform tarball** (it sets
420
- > `CASTRUM_PUBLISH_ALLOW_PARTIAL=1` so `prepublishOnly` doesn't fail on the missing
421
- > platforms). For a full multi-platform tarball, push a `v*` tag and let the CI
422
- > workflow build + publish.
196
+ ## Documentation
423
197
 
424
- ---
198
+ | Doc | Read it for |
199
+ |-----|-------------|
200
+ | [`docs/GETTING_STARTED.md`](./docs/GETTING_STARTED.md) | First-time walkthrough: install → server → bench → test. |
201
+ | [`docs/API.md`](./docs/API.md) | The complete public API reference. |
202
+ | [`docs/INGRESS.md`](./docs/INGRESS.md) | The ingress pipeline: options, route factories, servers, framework integration. |
203
+ | [`docs/CASE_STUDY.md`](./docs/CASE_STUDY.md) | Data-driven case study: how castrum was built and measured. |
204
+ | [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) | Internals: modules, data flow, wire layout, memory, concurrency. |
205
+ | [`docs/BENCHMARKS.md`](./docs/BENCHMARKS.md) | Benchmark scenarios + how to interpret reports. |
206
+ | [`docs/ENVIRONMENT.md`](./docs/ENVIRONMENT.md) | Every `CASTRUM_*` / `INGRESS_*` env var. |
207
+ | [`docs/REPO_MAP.md`](./docs/REPO_MAP.md) | Where everything lives + how to build/test/publish. |
208
+ | [`docs/bun-builtins-decision-matrix.md`](./docs/bun-builtins-decision-matrix.md) | Bun built-in vs castrum keep/delegate decisions. |
209
+ | [`examples/`](examples/README.md) | Runnable examples. |
425
210
 
426
211
  ## License
427
212
 
428
- MIT
213
+ MIT — see [LICENSE](./LICENSE).
429
214
 
430
215
  ## Contributing
431
216
 
432
- Please see [CONTRIBUTING.md](./CONTRIBUTING.md).
217
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) and [`AGENTS.md`](./AGENTS.md) (AI-agent
218
+ guidance) for contribution and testing requirements.
Binary file