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/docs/API.md ADDED
@@ -0,0 +1,337 @@
1
+ # castrum — API Reference
2
+
3
+ The complete public API, verified against the current source. For the friendly
4
+ overview and quick start, see the [README](../README.md); for the HTTP pipeline
5
+ deep-dive, see [`INGRESS.md`](./INGRESS.md).
6
+
7
+ Everything is **synchronous**. All byte-level functions accept `Uint8Array`;
8
+ `rust.text.*` provides string-in/string-out ergonomics.
9
+
10
+ ---
11
+
12
+ ## Runtime return-type divergence (Bun vs Node)
13
+
14
+ On **Bun** the `bun:ffi` C-ABI transport returns text results as **JS strings**
15
+ via JSC's native transfer (cstring → `new CString(ptr, 0, len)`), with **zero
16
+ `TextEncoder`/`TextDecoder`** on the hot path. So `rust.urlEncode`,
17
+ `rust.base64Encode`, `rust.hexEncode`, `rust.hmacSha256`, `rust.etag`,
18
+ `rust.wsAcceptKey`, `rust.randomToken`, `rust.signCookie`, `rust.verifyCookie`
19
+ (`string | null`), `rust.csrfToken`, `rust.passwordHash`,
20
+ `rust.passwordHashBcrypt`, `rust.jwtSignBytes`, `rust.jwtVerify`
21
+ (`string | null`), `rust.jsonPatch`, `rust.httpDate`, `rust.mimeFromExtension`,
22
+ `rust.urlResolve`, `rust.urlEncodeQuery`, and `rust.acceptNegotiatorNegotiate`
23
+ (`string | null` — the `null` is the C `NULL` sentinel) return **`string`** on Bun. On **Node** (napi fallback) the same
24
+ functions return **`Uint8Array`**. The TypeScript surface types these as
25
+ `Uint8Array | string`; normalize with `toBytes(value)` / `toText(value)` (from
26
+ `src/shared/bytes`). The `*Into`/pooled variants always return bytes on both
27
+ runtimes, and binary ops (`gzip*`, `brotli*`, `aead*`, `pbkdf2Sha256`,
28
+ `wsFrame*`, packed parsers, `batch.*`/`packed.*`) return `Uint8Array` on both.
29
+
30
+ ---
31
+
32
+ ## Core exports
33
+
34
+ | Export | Description |
35
+ |--------|-------------|
36
+ | `rust` | The default, shared native client — every Rust utility, flat and complete. |
37
+ | `createRust(options?)` | Build an isolated client (own caches / rayon settings). |
38
+ | `proven`, `PROVEN_SELECTION`, `provenImpl`, `provenStatus`, `isProven`, `provenSurface`, `provenSummary` | The full `rust.*` surface + the baked (pure-data) proven-selection registry of benchmark winners (`native` / `js` / `bun`). |
39
+ | `opImpl`, `isNativeOp`, `opDecision` | Native-vs-JS selection hints (benchmark-driven). |
40
+ | `loader`, `createLoader` | Higher-order loader over the batch surface. |
41
+ | `encoder`, `decoder` | Codec-backed UTF-8 helpers (Bun: `Bun.ArrayBufferSink` / `CString`; Node: `TextEncoder`/`TextDecoder`). `encode`/`decode` accept the `Uint8Array | string` union and normalize. |
42
+ | `uuidv7()` | UUIDv7 — `Bun.randomUUIDv7` on Bun, `crypto.randomUUID` on Node. |
43
+ | `AdaptiveEstimate`, `AdaptiveEstimateOptions` | Bounded EWMA adaptive-estimate utility. |
44
+ | `createMetrics`, `DEFAULT_BUCKETS` | Zero-dependency metrics registry (counters/gauges/histograms + Prometheus text). |
45
+ | `packBatch`, `packPairs`, `readPairsPacked`, `readHttpPacked`, `pairsToObject`, `parseQueryString`, `parseCookieHeader`, `parseFormBody`, `unpackBitset`, `unpackByteResults`, `unpackI64ArrayAsBigInt`, `unpackU32Array` | Packed-wire + high-level parsing utilities. |
46
+ | Ingress: `createIngress`, `createIngressSync`, `createIngressFast`, `createIngressHandler`, `createIngressServer`, `createIngressServerNode`, `createIngressRouter`, route factories, metrics/health/trace helpers | See [`INGRESS.md`](./INGRESS.md) and [`INGRESS-ROUTER.md`](./INGRESS-ROUTER.md). |
47
+ | Integration: `createPipeline`, `createWebSocketUpgrade`, `sseResponse` | Framework-agnostic helpers — see [`INGRESS.md`](./INGRESS.md) §Framework Integration. |
48
+
49
+ ---
50
+
51
+ ## The `rust` client
52
+
53
+ `rust` is a single flat object exposing **every** native utility. Text-oriented
54
+ operations also have ergonomic string variants under `rust.text`.
55
+
56
+ | Namespace | Description |
57
+ |-----------|-------------|
58
+ | `rust.crc32(...)`, `rust.jsonValid(...)`, … | Scalar utilities (bytes in → typed out) |
59
+ | `rust.text.mimeFromExtension(".js")`, … | String ergonomics (string in → string/bool out) |
60
+ | `rust.batch.jsonValid(docs)`, … | High-throughput batch (arrays → unpacked results) |
61
+ | `rust.packed.jsonValidBatchPacked(p)`, … | Raw packed low-level variants + metadata |
62
+ | `rust.configure({ ... })` | Override defaults on the shared instance |
63
+
64
+ `rust.transport()` returns `"ffi"` / `"napi"` (the resolved transport);
65
+ `rust.ffiActive()` reports whether the bun:ffi transport is live.
66
+
67
+ ### Scalar utilities
68
+
69
+ | Function | Returns |
70
+ |----------|---------|
71
+ | `crc32(input)` | `number` — CRC32 checksum |
72
+ | `fnv1a64(input)` | `bigint` — FNV-1a 64-bit hash |
73
+ | `xxh3(input)` | `bigint` — XXH3-64 (non-crypto hash) |
74
+ | `hmacSha256(key, data)` | `Uint8Array` — HMAC-SHA256 signature |
75
+ | `hmacSha256Verify(key, data, sig)` | `boolean` |
76
+ | `jsonValid(input)` | `boolean` — JSON validity check (zero-DOM) |
77
+ | `jsonParse(input)` | `unknown` — JSON → JS value (native sonic-rs DOM; throws on invalid) |
78
+ | `jsonSumIds(input)` | `bigint` — sum of numeric `id` fields |
79
+ | `jsonPatch(doc, patch)` | `Uint8Array` — RFC 6902 result |
80
+ | `createSchemaValidator(schema)` | `SchemaValidator` — compile a JSON Schema (draft-07); `.validate(doc): boolean`, `.validateDetailed(doc): SchemaError[]`, `.validateFirstError(doc): SchemaError | null`, `.validateBatchPackedCount(packed): number`, … |
81
+ | `mimeFromExtension(ext)` | `Uint8Array` — MIME type |
82
+ | `randomToken(byteLen)` | `Uint8Array` — CSPRNG token |
83
+ | `urlEncode(input)` / `urlDecode(input)` | `Uint8Array` — percent encode/decode |
84
+ | `urlDecodeBytes(input)` | `Uint8Array` — strict %-decode (no UTF-8 check) |
85
+ | `validateEmail/Uuid/Ipv4/Ipv6(input)` | `boolean` |
86
+ | `wsAcceptKey(key)` | `Uint8Array` — WebSocket accept key |
87
+ | `passwordHashBcrypt(password, salt?, cost?)` | `Uint8Array` — bcrypt `$2b$` PHC string |
88
+ | `passwordVerifyBcrypt(password, phc)` | `boolean` |
89
+ | `pbkdf2Sha256(password, salt, iterations, dkLen?)` | `Uint8Array` — PBKDF2-HMAC-SHA256 |
90
+ | `gzipCompress(data)` / `gzipDecompress(data, maxDecompressed?)` | `Uint8Array` — gzip (decompress capped at 64 MiB by default) |
91
+ | `brotliCompress(data)` / `brotliDecompress(data, maxDecompressed?)` | `Uint8Array` — brotli |
92
+
93
+ ### Framework actions (HTTP semantics + security)
94
+
95
+ | Function | Returns |
96
+ |----------|---------|
97
+ | `formParsePacked(body)` | `Uint8Array` — packed x-www-form-urlencoded pairs |
98
+ | `createFormParser(capacity?)` | `FormParser` — reusable-buffer form parser (`.parse`, `.parseInto`) |
99
+ | `parseMediaType(header)` | `{ mediaType, charset, boundary, params }` — Content-Type parse |
100
+ | `createMediaTypeParser()` | `MediaTypeParser` — `.parse`, `.matches(actual, expected)` (wildcards) |
101
+ | `createMediaTypeMatcher(expected)` | `MediaTypeMatcher` — `.matches(actual)` (expected precompiled once) |
102
+ | `etag(data, weak?)` | `Uint8Array` — strong/weak ETag (crc32-based) |
103
+ | `httpDate(secs?)` / `parseHttpDate(input)` | `Uint8Array` / `bigint | null` — IMF-fixdate format/parse |
104
+ | `createConditionalRequest(etag, lastModifiedSecs?)` | `ConditionalRequest` — `.isNotModified(ifNoneMatch, ifModifiedSince)` → 304 |
105
+ | `parseAcceptEncoding(header)` | `{ encoding, q, order }[]` |
106
+ | `createAcceptNegotiator(supported[])` | `AcceptNegotiator` — `.negotiate(header)` → best encoding |
107
+ | `base64Encode/Decode(input, urlSafe?, padding?)`, `base64UrlEncode/Decode(input)` | `Uint8Array` |
108
+ | `createBase64Codec(urlSafe?, padding?)` | `Base64Codec` — `.encode`, `.decode` |
109
+ | `hexEncode(input)` / `hexDecode(input)` | `Uint8Array` — lowercase hex |
110
+ | `signCookie(value, secret)` / `verifyCookie(signed, secret)` | `Uint8Array` / `Uint8Array | null` — signed cookies |
111
+ | `createCookieSigner(secret)` | `CookieSigner` — `.sign`, `.verify` (HMAC key compiled once) |
112
+ | `csrfToken(secret)` / `csrfVerify(token, secret)` | `Uint8Array` / `boolean` |
113
+ | `createCsrfProtector(secret)` | `CsrfProtector` — `.create`, `.verify` |
114
+ | `urlResolve(base, reference)` | `Uint8Array` — RFC 3986 resolution |
115
+ | `urlEncodeQuery(params)` | `Uint8Array` — percent-encoded query (sorted keys) |
116
+ | `createUrlBuilder(base)` | `UrlBuilder` — `.resolve(reference)` (base parsed once) |
117
+ | `createJwtSigner(secret, ttlSeconds?)` | `JwtSigner` — `.sign(claims, now)`, `.verify(token, now)` (HS256 key + ttl compiled once) |
118
+ | `createAeadCipher(key, algorithm?)` | `AeadCipher` — `.encrypt(nonce, pt)`, `.decrypt(nonce, ct)` (key compiled once) |
119
+ | `createArgon2Hasher(options?)` | `Argon2Hasher` — `.hash(password, salt)`, `.verify(password, phc)` (params compiled once) |
120
+ | `httpParseRequestPacked(bytes)` | packed HTTP request parse (low-level) |
121
+ | `queryParsePacked(query)` / `cookieParsePacked(header)` / `formParsePacked(body)` | packed pair parsing (low-level) |
122
+
123
+ > `passwordHash` / `passwordVerify` (argon2id) and `passwordHashBcrypt` /
124
+ > `passwordVerifyBcrypt` are also exposed as scalar helpers; the compiled-once
125
+ > `createArgon2Hasher` is preferred when hashing repeatedly.
126
+
127
+ ### String ergonomics (`rust.text`)
128
+
129
+ ```ts
130
+ rust.text.mimeFromExtension(".js"); // "text/javascript"
131
+ rust.text.mimeFromExtension("html"); // "text/html"
132
+ rust.text.urlEncode("a b"); // "a%20b"
133
+ rust.text.urlDecode("a%20b"); // "a b"
134
+ rust.text.wsAcceptKey(keyBase64); // base64 accept key
135
+ rust.text.validateEmail("a@b.co"); // true
136
+ ```
137
+
138
+ ### Batch operations (`rust.batch`)
139
+
140
+ All batch helpers accept `Uint8Array[]` and return unpacked, ready-to-use results:
141
+
142
+ ```ts
143
+ const docs = [encoder.encode('{"id":1}'), encoder.encode('{"id":2}')];
144
+
145
+ rust.batch.jsonValid(docs); // Uint8Array bitset (1 per doc)
146
+ rust.batch.crc32(docs); // Uint32Array
147
+ rust.batch.jsonSumIds(docs); // BigInt64Array
148
+ rust.batch.queryParse(docs); // Uint8Array[]
149
+ rust.batch.formParse(docs); // Uint8Array[] (packed pairs per doc)
150
+ rust.batch.signCookie(docs, secret); // Uint8Array[] (signed)
151
+ rust.batch.verifyCookie(docs, secret); // Uint8Array bitset (valid?)
152
+ rust.batch.csrfVerify(tokens, secret); // Uint8Array bitset (valid?)
153
+ rust.batch.schemaValidate(validator, docs); // bitset
154
+
155
+ // Hashing / url / mime / ws / base64url
156
+ rust.batch.fnv1a64(docs); // BigUint64Array (unsigned hashes)
157
+ rust.batch.etag(docs); // Uint8Array[]
158
+ rust.batch.urlEncode(docs); // Uint8Array[]
159
+ rust.batch.urlDecode(docs); // Uint8Array[]
160
+ rust.batch.base64UrlEncode(docs); // Uint8Array[]
161
+ rust.batch.wsAcceptKey(keys); // Uint8Array[]
162
+ rust.batch.mimeFromExtension(exts); // Uint8Array[]
163
+
164
+ // Backend-framework batches (zipped lists / shared args)
165
+ rust.batch.passwordVerify(pwds, phcs); // Uint8Array bitset (zipped)
166
+ rust.batch.urlResolve(bases, refs); // Uint8Array[] (zipped)
167
+ rust.batch.jwtSign(claimDocs, secret); // Uint8Array[] (sign N JSON claim docs)
168
+ rust.batch.jwtVerify(tokens, secret, now); // Uint8Array bitset
169
+ rust.batch.sseEncode(items, "event"); // Uint8Array[]
170
+ rust.batch.wsFrameEncode(items, 1, false, true); // Uint8Array[]
171
+ rust.batch.wsFrameDecode(frames); // Uint8Array[] (payloads)
172
+ rust.batch.multipartParse(bodies, boundary); // MultipartPart[][]
173
+ ```
174
+
175
+ Raw packed + metadata variants (advanced): `rust.packed.*` mirrors the native
176
+ functions 1:1 (`jsonValidBatchPacked`, `jsonValidBatchCountPacked`,
177
+ `queryParseBatchTotalLenPacked`, …).
178
+
179
+ ---
180
+
181
+ ## Higher-order data loader (`loader` / `createLoader`)
182
+
183
+ `loader` is a callable higher-order function over the whole batch surface. It
184
+ pre-binds an op so repeated calls skip registry dispatch, and it routes small
185
+ vs. large workloads automatically: a single item → one scalar native call, a
186
+ bulk → ONE packed batch call, and `load()` coalesces N same-tick calls into one
187
+ packed call (DataLoader-style) with a bounded LRU cache.
188
+
189
+ ```ts
190
+ import { loader } from "castrum";
191
+
192
+ const isEmail = loader("validateEmail");
193
+ isEmail(emailBytes); // scalar → boolean
194
+ isEmail([a, b, c]); // bulk → Uint8Array bitset (one packed call)
195
+ await isEmail.load(a); // coalesced + cached (auto batch/strategy)
196
+
197
+ // Covered op families (44 ops):
198
+ // hash crc32, fnv1a64
199
+ // json jsonValid, jsonSumIds, jsonPatch*, schemaValidate
200
+ // validate email, uuid, ipv4, ipv6
201
+ // parse query, cookie, form, httpParseRequest, urlResolve*
202
+ // encode hex, base64, base64Url, urlEncode, urlDecode, urlDecodeBytes, etag
203
+ // compress gzip, brotli
204
+ // crypto hmacSha256, hmacSha256Verify*, signCookie, verifyCookie, csrfVerify,
205
+ // passwordHash, passwordVerify*, aeadEncrypt, aeadDecrypt, jwtSign, jwtVerify
206
+ // web wsAcceptKey, wsFrameEncode, sseEncode, mimeFromExtension
207
+ // (* = paired: per-item companion array, e.g. loader.run("jsonPatch", docs, patches))
208
+
209
+ // Paired ops take a companion array:
210
+ loader.run("jsonPatch", docs, patches); // bulk
211
+ loader.run("jsonPatch", doc, patch); // single
212
+
213
+ // verifyCookie / jwtVerify are boolean-validity ops (valid/invalid);
214
+ // use rust.verifyCookie / rust.jwtVerify for the decoded value.
215
+
216
+ // Bind a schema validator for repeated single/bulk validation + count:
217
+ const schema = loader.schema(rust.createSchemaValidator(schemaBytes));
218
+ schema(doc); // boolean
219
+ schema(docs); // Uint8Array bitset
220
+ schema.count(docs); // number of valid docs
221
+
222
+ // Fine-grained control: createLoader({ adaptive, batchMin, maxCacheKeys, ... })
223
+ ```
224
+
225
+ ---
226
+
227
+ ## The `proven` selection surface
228
+
229
+ `proven` is literally the same object as `rust` (`export const proven = rust`) —
230
+ nothing is filtered. Alongside it, the pure-data registry `PROVEN_SELECTION`
231
+ (`src/shared/proven.ts`) states which implementation is the benchmark-proven
232
+ winner for every op: `native` (the addon), `js` (pure TS), or `bun` (a Bun
233
+ built-in that is delegated under Bun). The winners are **baked** from
234
+ measurements (`src/selection.json` + `docs/bun-builtins-decision-matrix.md`),
235
+ and `test/unit/shared/proven.test.ts` verifies each winner is actually wired
236
+ (`opImpl` agrees, `builtins.has` matches, native/js entries match the addon's
237
+ embedded `selection.json`) — so the registry cannot silently drift. There is no
238
+ live benchmark gate (the old `check:proven` / `check:annotate` friction is gone).
239
+
240
+ - Statuses: `proven` (decisive win) / `parity` (borderline) / `unmeasured` (no
241
+ direct ratio — pinned by policy). `provenImpl(op)` returns the baked winner.
242
+ - The full surface is unchanged — `rust.jsonParse`, `rust.createSchemaValidator`,
243
+ etc. remain available for completeness; they just aren't advertised as wins.
244
+ - Classifications are based on the **release build on the shipped baseline CPU**
245
+ (published artifacts are baseline — not the local SIMD `build:perf`).
246
+
247
+ ```ts
248
+ import { proven, PROVEN_SELECTION, provenImpl } from "castrum";
249
+
250
+ proven.fnv1a64(bytes); // native — Rust wins ~12x vs the JS baseline
251
+ provenImpl("crc32"); // "bun" — Bun.hash.crc32 wins under Bun
252
+ provenImpl("validateEmail"); // "js" — pure-TS wins
253
+ ```
254
+
255
+ ```bash
256
+ bun test test/unit/shared/proven.test.ts # proves every baked winner is wired
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Native-vs-JS selection surface
262
+
263
+ For framework consumers that want to bind each operation to a fixed
264
+ implementation at load time (instead of calling `rust.*` directly):
265
+
266
+ ```ts
267
+ import { opImpl, isNativeOp, opDecision } from "castrum";
268
+
269
+ opImpl("gzipCompress"); // "native" | "js" | null
270
+ isNativeOp("crc32"); // boolean
271
+ opDecision("fnv1a64"); // { impl, note? } | null
272
+ ```
273
+
274
+ - Source of truth: `rust/selection.rs`, which embeds the benchmark-generated
275
+ `src/selection.json` (produced by `scripts/select-native.ts --write`, audited
276
+ by `--check` in CI).
277
+ - Under Bun, ops where the Bun built-in beats the Rust addon (gzip, crc32, xxh3,
278
+ HMAC, random tokens) are selected as `"js"` (a JS path that delegates to the
279
+ Bun built-in); under Node the base benchmark decision stands.
280
+ - Consumers read `opImpl(op)` **once at startup** and bind each op to a fixed
281
+ implementation — they do not swap native↔js per call.
282
+
283
+ ---
284
+
285
+ ## Configuration
286
+
287
+ Defaults are selected automatically, and can be overridden:
288
+
289
+ ```ts
290
+ // Defaults:
291
+ // rayonThreads = max(1, hardwareConcurrency - 1)
292
+ // (env override: CASTRUM_RAYON_THREADS / RUST_RAYON_THREADS)
293
+ // mimeCache = true
294
+ // hmacCache = true
295
+
296
+ // Override per-instance options on the shared instance:
297
+ rust.configure({ mimeCache: false, hmacCache: false });
298
+
299
+ // Or create an isolated instance with custom defaults:
300
+ import { createRust } from "castrum";
301
+ const myRust = createRust({ mimeCache: false });
302
+ ```
303
+
304
+ > **Thread pool note**: the rayon pool is process-wide and initialized **once**
305
+ > (native `OnceLock`) — the first initialization wins. Because importing the
306
+ > module initializes it with the default, set
307
+ > `CASTRUM_RAYON_THREADS` / `RUST_RAYON_THREADS` (or call
308
+ > `rust.configure({ rayonThreads })` / `createRust({ rayonThreads })` before any
309
+ > other pool use) to tune it. `mimeCache` / `hmacCache` are per-instance and can
310
+ > be toggled any time.
311
+
312
+ ---
313
+
314
+ ## Shared utilities
315
+
316
+ ```ts
317
+ import {
318
+ encoder, decoder, // TextEncoder/TextDecoder singletons
319
+ packBatch, packPairs, // batch/pair packing
320
+ unpackBitset, unpackByteResults, unpackI64ArrayAsBigInt, unpackU32Array,
321
+ readPairsPacked, pairsToObject, readHttpPacked,
322
+ parseQueryString, parseCookieHeader, parseFormBody,
323
+ uuidv7, createMetrics,
324
+ } from "castrum";
325
+
326
+ parseQueryString("a=1&b=2&tag=a&tag=b"); // { a: "1", b: "2", tag: ["a", "b"] }
327
+ parseCookieHeader("session=abc; theme=dark"); // { session: "abc", theme: "dark" }
328
+ ```
329
+
330
+ > **Environment variables**: see [`ENVIRONMENT.md`](./ENVIRONMENT.md) for the
331
+ > full reference of `INGRESS_*`, `CASTRUM_*`, and `RUST_*` runtime variables.
332
+
333
+ ## Ingress & integration
334
+
335
+ The HTTP pipeline (options, route factories, `BakedIngressResult`, server
336
+ builders, observability, and the framework integration helpers) has its own
337
+ dedicated reference: [`INGRESS.md`](./INGRESS.md).