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
@@ -0,0 +1,474 @@
1
+ // src/native/ffi/types.ts — bun:ffi C-ABI type surface (pure types).
2
+ //
3
+ // The bound-function contract (`BunFFI`), the transport selection
4
+ // (`FfiMode`), and the raw `(ptr,len)` symbol signatures (`Raw2..Raw10`)
5
+ // used by the transport core. No runtime code — erased at compile time.
6
+
7
+ /**
8
+ * The bound bun:ffi C-ABI surface. Each member is a dlopen'd `castrum_*`
9
+ * symbol wrapped so inputs are `(ptr,len)` byte slices and text outputs are
10
+ * returned as JS strings via JSC's native transfer (cstring clone) — zero
11
+ * `TextEncoder`/`TextDecoder` on the Bun path. `*Into` variants write into a
12
+ * caller buffer and return the byte count; variable-size ops return the exact
13
+ * required size on a too-small buffer so the transport grows once and retries
14
+ * exactly once.
15
+ */
16
+ export interface BunFFI {
17
+ crc32(input: Uint8Array): number
18
+ fnv1a64(input: Uint8Array): bigint
19
+ xxh3(input: Uint8Array): bigint
20
+ jsonValid(input: Uint8Array): boolean
21
+ /** UTF-8 validity probe (native replacement for a fatal `TextDecoder`). */
22
+ utf8Valid(input: Uint8Array): boolean
23
+ /** Lowercase-hex encode → string (native transfer, zero encode). */
24
+ hexEncode(input: Uint8Array): string
25
+ /** Lowercase-hex encode into `output`; returns bytes written. */
26
+ hexEncodeInto(input: Uint8Array, output: Uint8Array): number
27
+ /** RFC 3986 percent-encode → string. */
28
+ urlEncode(input: Uint8Array): string
29
+ /** RFC 3986 percent-encode into `output`; returns bytes written. */
30
+ urlEncodeInto(input: Uint8Array, output: Uint8Array): number
31
+
32
+ // ── Validators (u8 → boolean) — `cstring` ARG ────────────────────
33
+ // The input crosses as a `cstring` ARG: the engine transcodes the JS string
34
+ // to a call-scoped NUL-terminated UTF-8 buffer in-engine, so the JS side does
35
+ // ZERO `encoder.encode` work and Rust borrows via `CStr::from_ptr`.
36
+ validateEmail(input: string): boolean
37
+ validateUuid(input: string): boolean
38
+ validateIpv4(input: string): boolean
39
+ validateIpv6(input: string): boolean
40
+ /** Sum of `id` fields across a JSON array → bigint (throws on non-array input). */
41
+ jsonSumIds(input: Uint8Array): bigint
42
+
43
+ // ── Constant-time verify (u8 → boolean) ───────────────────────────
44
+ hmacSha256Verify(key: Uint8Array, data: Uint8Array, signature: Uint8Array): boolean
45
+ csrfVerify(token: Uint8Array, secret: Uint8Array): boolean
46
+ passwordVerify(password: Uint8Array, phc: Uint8Array): boolean
47
+ /** `phc` is a `cstring` ARG (the engine transcodes the JS PHC string in-engine). */
48
+ passwordVerifyBcrypt(password: Uint8Array, phc: string): boolean
49
+
50
+ // ── Decoders (throw on malformed input — napi `Result` parity) ──
51
+ /** Hex-decode into a fresh buffer (size `input.length / 2`). */
52
+ hexDecode(input: Uint8Array): Uint8Array
53
+ /** Hex-decode into `output`; returns bytes written (throws if too small). */
54
+ hexDecodeInto(input: Uint8Array, output: Uint8Array): number
55
+ /** Percent-decode into a fresh buffer (size `input.length`). */
56
+ urlDecode(input: Uint8Array): Uint8Array
57
+ /** Percent-decode into `output`; returns bytes written (throws if too small). */
58
+ urlDecodeInto(input: Uint8Array, output: Uint8Array): number
59
+ /** base64-decode into a fresh buffer (size `ceil(len/4)*3`). */
60
+ base64Decode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array
61
+ /** base64-decode into `output`; returns bytes written (throws if too small). */
62
+ base64DecodeInto(
63
+ input: Uint8Array,
64
+ output: Uint8Array,
65
+ urlSafe?: boolean,
66
+ padding?: boolean,
67
+ ): number
68
+
69
+ // ── Fixed-size output writers ─────────────────────────────────────
70
+ /** RFC 6455 Sec-WebSocket-Accept → string (cstring return; `key` is a `cstring` ARG). */
71
+ wsAcceptKey(key: string): string
72
+ /** RFC 6455 Sec-WebSocket-Accept into `output`; returns bytes written. */
73
+ wsAcceptKeyInto(key: Uint8Array, output: Uint8Array): number
74
+ /** crc32 ETag → string (10 strong / 12 weak chars, cstring return). */
75
+ etag(data: Uint8Array, weak?: boolean): string
76
+ /** crc32 ETag into `output`; returns bytes written. */
77
+ etagInto(data: Uint8Array, output: Uint8Array, weak?: boolean): number
78
+ /** Evaluate a precompiled `ConditionalRequest` (opaque `inner` handle from its
79
+ * napi `inner_ptr()`) — true → 304. `null` header args = absent (flags encode
80
+ * presence so a present-but-empty header is distinct). A null (0) handle → false. */
81
+ conditionalIsNotModified(
82
+ inner: number,
83
+ ifNoneMatch: Uint8Array | null,
84
+ ifModifiedSince: Uint8Array | null,
85
+ ): boolean
86
+ /** Precompiled `MediaTypeMatcher` wildcard match (opaque `inner` handle). */
87
+ mediaTypeMatcherMatches(inner: number, actual: Uint8Array): boolean
88
+ /** Precompiled `AcceptNegotiator` → best supported encoding (opaque `inner`
89
+ * handle); `null` = identity. */
90
+ acceptNegotiatorNegotiate(inner: number, header: Uint8Array): string | null
91
+ /** Precompiled `JwtSigner` → compact token from pre-serialized claim JSON
92
+ * (opaque `inner` handle); throws on invalid claims / null handle. */
93
+ jwtSignerSign(inner: number, claimsJson: Uint8Array, nowSeconds: number): Uint8Array
94
+ /** Precompiled `JwtSigner` → claims JSON bytes (opaque `inner` handle);
95
+ * `null` = invalid signature / expired / malformed. */
96
+ jwtSignerVerify(inner: number, token: Uint8Array, nowSeconds: number): Uint8Array | null
97
+ /** Compiled `TemplateRenderer` → UTF-8 bytes from a pre-serialized JSON
98
+ * context (opaque `inner` handle); throws on invalid context / render error. */
99
+ templateRender(inner: number, contextJson: Uint8Array): Uint8Array
100
+ /** Compiled `SchemaValidator` → whether a document is valid (opaque `inner` handle). */
101
+ schemaValidatorValidate(inner: number, doc: Uint8Array): boolean
102
+ /** `RateLimiter` string-key check → { allowed, remaining, resetMs } (opaque
103
+ * `inner` handle; `key` is a `cstring` ARG); throws on a null handle. */
104
+ rateLimiterCheck(inner: number, key: string, nowMs: number): RateLimiterVerdict
105
+ /** `RateLimiter` pre-hashed key check → { allowed, remaining, resetMs }. */
106
+ rateLimiterCheckKey(inner: number, key: number, nowMs: number): RateLimiterVerdict
107
+ /** `byteLen` random bytes → `byteLen * 2` hex-char string (cstring return). */
108
+ randomToken(byteLen: number): string
109
+ /** base64-encode → string. */
110
+ base64Encode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): string
111
+ /** base64-encode into `output`; returns bytes written. */
112
+ base64EncodeInto(
113
+ input: Uint8Array,
114
+ output: Uint8Array,
115
+ urlSafe?: boolean,
116
+ padding?: boolean,
117
+ ): number
118
+ /** HMAC-SHA256 → 64-char lowercase-hex string. */
119
+ hmacSha256(key: Uint8Array, data: Uint8Array): string
120
+ /** HMAC-SHA256 hex into `output`; returns bytes written (throws if too small). */
121
+ hmacSha256Into(key: Uint8Array, data: Uint8Array, output: Uint8Array): number
122
+ /** Sign cookie `value` as `value.<64-hex>` → string (cstring return). */
123
+ signCookie(value: Uint8Array, secret: Uint8Array): string
124
+ /** Sign cookie into `output`; returns bytes written (throws if too small). */
125
+ signCookieInto(value: Uint8Array, secret: Uint8Array, output: Uint8Array): number
126
+ /** Verify a signed cookie → value string, or `null` on bad signature. */
127
+ verifyCookie(signed: Uint8Array, secret: Uint8Array): string | null
128
+ /** Verify a signed cookie into `output`; returns value length or `null` on bad signature. */
129
+ verifyCookieInto(signed: Uint8Array, secret: Uint8Array, output: Uint8Array): number | null
130
+ /** CSRF token (129 chars: 64 rnd-hex + '.' + 64 sig-hex) → string. */
131
+ csrfToken(secret: Uint8Array): string
132
+ /** CSRF token into `output`; returns bytes written (throws if too small). */
133
+ csrfTokenInto(secret: Uint8Array, output: Uint8Array): number
134
+ /** Argon2id PHC hash → string (m/t/p/out_len params). */
135
+ passwordHash(
136
+ password: Uint8Array,
137
+ salt: Uint8Array,
138
+ mCost: number,
139
+ tCost: number,
140
+ pCost: number,
141
+ outLen: number,
142
+ ): string
143
+ /** bcrypt `$2b$` PHC → string (cost clamped 4..=31). */
144
+ passwordHashBcrypt(password: Uint8Array, cost: number): string
145
+ /** PBKDF2-HMAC-SHA256 → `dkLen` bytes (rounds, dkLen clamped). */
146
+ pbkdf2Sha256(password: Uint8Array, salt: Uint8Array, rounds: number, dkLen: number): Uint8Array
147
+ /** AEAD encrypt (0 = AES-256-GCM, 1 = ChaCha20-Poly1305) → ct+tag. */
148
+ aeadEncrypt(
149
+ key: Uint8Array,
150
+ nonce: Uint8Array,
151
+ plaintext: Uint8Array,
152
+ algorithm?: number,
153
+ ): Uint8Array
154
+ /** AEAD encrypt into `output` (ct + 16-byte tag); returns bytes written. */
155
+ aeadEncryptInto(
156
+ key: Uint8Array,
157
+ nonce: Uint8Array,
158
+ plaintext: Uint8Array,
159
+ output: Uint8Array,
160
+ algorithm?: number,
161
+ ): number
162
+ /** AEAD decrypt → plaintext, or `null` on auth failure. */
163
+ aeadDecrypt(
164
+ key: Uint8Array,
165
+ nonce: Uint8Array,
166
+ ciphertext: Uint8Array,
167
+ algorithm?: number,
168
+ ): Uint8Array | null
169
+
170
+ // ── Frame / patch / compression (variable-size → grow-retry) ─────
171
+ /** RFC 6455 frame encode into a fresh buffer. */
172
+ wsFrameEncode(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean): Uint8Array
173
+ /** RFC 6455 frame encode into `output`; returns bytes written (throws if too small). */
174
+ wsFrameEncodeInto(
175
+ opcode: number,
176
+ payload: Uint8Array,
177
+ mask: boolean,
178
+ fin: boolean,
179
+ output: Uint8Array,
180
+ ): number
181
+ /** RFC 6902 JSON patch → JSON text string. */
182
+ jsonPatch(doc: Uint8Array, patch: Uint8Array): string
183
+ /** gzip-compress into a fresh buffer (level clamped 0..=9, default 6). */
184
+ gzipCompress(data: Uint8Array, level?: number): Uint8Array
185
+ /** gzip-compress into `output`; returns bytes written (throws if too small). */
186
+ gzipCompressInto(data: Uint8Array, output: Uint8Array, level?: number): number
187
+ /** gzip-decompress into a fresh buffer (capped by `maxDecompressed`). */
188
+ gzipDecompress(data: Uint8Array, maxDecompressed?: number): Uint8Array
189
+ /** gzip-decompress into `output`; returns bytes written (throws if too small). */
190
+ gzipDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number): number
191
+ /** brotli-compress into a fresh buffer (quality clamped 0..=11, default 5). */
192
+ brotliCompress(data: Uint8Array, quality?: number): Uint8Array
193
+ /** brotli-compress into `output`; returns bytes written (throws if too small). */
194
+ brotliCompressInto(data: Uint8Array, output: Uint8Array, quality?: number): number
195
+ /** brotli-decompress into a fresh buffer (capped by `maxDecompressed`). */
196
+ brotliDecompress(data: Uint8Array, maxDecompressed?: number): Uint8Array
197
+ /** brotli-decompress into `output`; returns bytes written (throws if too small). */
198
+ brotliDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number): number
199
+
200
+ // ── Packed parsers (into caller buffers) ─────────────────────────
201
+ /** HTTP request parse → packed output into `output`; returns bytes written. */
202
+ httpParseRequestPackedInto(input: Uint8Array, output: Uint8Array): number
203
+ /** Query string parse → packed output into `output`; returns bytes written. */
204
+ queryParsePackedInto(input: Uint8Array, output: Uint8Array): number
205
+ /** Cookie header parse → packed output into `output`; returns bytes written. */
206
+ cookieParsePackedInto(input: Uint8Array, output: Uint8Array): number
207
+
208
+ // ── Excluded-surface additions (packed / opaque-handle) ─────────
209
+ /** Sign JWT (HS256) from pre-serialized claim JSON (ttl<=0 → no iat/exp) → string. */
210
+ jwtSignBytes(claimsJson: Uint8Array, secret: Uint8Array, ttl: number, now: number): string
211
+ /** Sign JWT into `output`; returns bytes written (throws if too small). */
212
+ jwtSignBytesInto(
213
+ claimsJson: Uint8Array,
214
+ secret: Uint8Array,
215
+ ttl: number,
216
+ now: number,
217
+ output: Uint8Array,
218
+ ): number
219
+ /** Decode a WS frame into packed `[flags][opcode][u32 len][payload]`; null on malformed. */
220
+ wsFrameDecodePacked(data: Uint8Array): Uint8Array | null
221
+ /**
222
+ * Decode a WS frame into `output` (sized ≥ `data.length + 6`); returns bytes
223
+ * written, null on malformed. Pooled sibling of `wsFrameDecodePacked`.
224
+ */
225
+ wsFrameDecodePackedInto(data: Uint8Array, output: Uint8Array): number | null
226
+ /** Parse multipart/form-data into the packed parts layout. */
227
+ multipartParsePacked(body: Uint8Array, boundary: Uint8Array): Uint8Array
228
+ /** Parse multipart/form-data into `output`; returns bytes written (throws if too small). */
229
+ multipartParsePackedInto(body: Uint8Array, boundary: Uint8Array, output: Uint8Array): number
230
+ /** Parse x-www-form-urlencoded into packed pairs into `output`. */
231
+ formParsePackedInto(input: Uint8Array, output: Uint8Array): number
232
+ /**
233
+ * Verify an HS256 JWT → claims JSON text (cstring return; `null` on invalid
234
+ * signature / expired / malformed). `nowSeconds` is an i64 C arg.
235
+ */
236
+ jwtVerify(token: Uint8Array, secret: Uint8Array, nowSeconds: number): string | null
237
+ /**
238
+ * Parse JSON → packed token stream bytes (structural, no re-parse). Layout:
239
+ * `[u32 strCount]{[u32 len][utf8]}... [u32 treeLen][tree]` — a deduplicated
240
+ * string table followed by a start/end-marker value tree:
241
+ * `0=null 1=false 2=true 3=number(f64 LE) 4=string(u32 table idx)
242
+ * 5=array start 6=object start 7=array end 8=object end
243
+ * 9=object key(u32 table idx)` (object body `6,(9,keyIdx,value)*,8`).
244
+ * Throws on invalid JSON / too-small output. The scalar wrapper decodes the
245
+ * table once and assembles the value — no second parse.
246
+ */
247
+ jsonParsePacked(input: Uint8Array): Uint8Array
248
+ /**
249
+ * Parse a Content-Type header → packed verdict bytes:
250
+ * `[u32 mediaTypeLen][mediaType][u32 charsetLen (0xFFFFFFFF = none)][charset]
251
+ * [u32 boundaryLen][boundary][u32 paramCount]{[u32 keyLen][key][u32 valLen][val]}`.
252
+ * Throws on invalid media type / too-small output.
253
+ */
254
+ parseMediaType(input: Uint8Array): Uint8Array
255
+ /** Parse an IMF-fixdate → unix seconds, or `null` on malformed (packed
256
+ * `[u8 ok][i64 secs LE]` output, decoded here). */
257
+ parseHttpDate(input: Uint8Array): bigint | null
258
+ /**
259
+ * Parse an Accept-Encoding header → packed verdict bytes:
260
+ * `[u32 count]{[u32 encLen][enc][f32 q][u32 order]}` (empty header → count 0).
261
+ * Throws on too-small output.
262
+ */
263
+ parseAcceptEncoding(input: Uint8Array): Uint8Array
264
+ /** Percent-encode a query from packed pairs (the `packPairs` layout) → string,
265
+ * keys SORTED (matches napi `BTreeMap` ordering); `null` on malformed input. */
266
+ urlEncodeQuery(input: Uint8Array): string | null
267
+ /** RFC 3986 URL resolution (base + reference) → string; `null` on non-UTF-8. */
268
+ urlResolve(base: Uint8Array, reference: Uint8Array): string | null
269
+ /** Resolve a reference against a `UrlBuilder`'s PRECOMPILED base (opaque
270
+ * `inner` handle from its napi `inner_ptr()`) → bytes; throws on a null
271
+ * handle / non-UTF-8 reference / too-small output. */
272
+ urlBuilderResolve(inner: number, reference: Uint8Array): Uint8Array
273
+ /** Extension → MIME type → string (unknown → `application/octet-stream`; `ext` is a `cstring` ARG). */
274
+ mimeFromExtension(ext: string): string | null
275
+ /**
276
+ * 29-byte HTTP-date (`Sun, 06 Nov 1994 08:49:37 GMT`) into `output`; returns
277
+ * bytes written (throws on too-small / out-of-range year). FFI sibling of the
278
+ * napi `httpDateInto` — kills the napi crossing on the hot path.
279
+ */
280
+ httpDateInto(secs: number, output: Uint8Array): number
281
+ /** 29-byte HTTP-date → string (allocating sibling of httpDateInto). */
282
+ httpDate(secs?: number): string
283
+ /**
284
+ * Encode one SSE event → bytes (fresh buffer). event/id null = line omitted
285
+ * (a present-but-empty string emits the line, matching napi `Option`).
286
+ * FFI sibling of the napi `sse_encode_event` — kills the napi crossing.
287
+ */
288
+ sseEncodeEvent(
289
+ event: string | null,
290
+ data: Uint8Array,
291
+ id: string | null,
292
+ retry: number | null,
293
+ ): Uint8Array
294
+ /**
295
+ * Encode one SSE event into `output` (sized ≥ `data.length + 64`); returns
296
+ * bytes written (throws if too small / invalid UTF-8). Pooled sibling of
297
+ * `sseEncodeEvent` — removes the per-call output-buffer alloc.
298
+ */
299
+ sseEncodeEventInto(
300
+ event: string | null,
301
+ data: Uint8Array,
302
+ id: string | null,
303
+ retry: number | null,
304
+ output: Uint8Array,
305
+ ): number
306
+ /** Random hex token into `output`; returns bytes written (throws if too small). */
307
+ randomTokenInto(byteLen: number, output: Uint8Array): number
308
+ /**
309
+ * Run the ingress pipeline on a packed frame via the opaque inner handle from
310
+ * `Ingress.ingressInnerPtr()` (valid only while the instance is alive — the
311
+ * caller must hold it). Returns bytes written; throws on error / too-small.
312
+ */
313
+ ingressHandlePacked(
314
+ inner: number,
315
+ input: Uint8Array,
316
+ body: Uint8Array | null,
317
+ output: Uint8Array,
318
+ ): number
319
+ /**
320
+ * Run the ingress pipeline from raw request components via the opaque inner
321
+ * handle (valid only while the instance is alive). `url`/`ip` are JS strings
322
+ * passed as `bun:ffi` `cstring` ARGs — the engine transcodes them to
323
+ * call-scoped NUL-terminated UTF-8 buffers in-engine, so the JS side skips
324
+ * the frame assembly + `Buffer.write` encode for the URL/IP. `rid`/`headers`
325
+ * are the packed byte slices; `body` is the request body (or null); `output`
326
+ * receives the packed decision. Same wire format as {@link ingressHandlePacked}.
327
+ * Returns bytes written; throws on error / too-small.
328
+ */
329
+ ingressHandleComponents(
330
+ inner: number,
331
+ methodKind: number,
332
+ url: string,
333
+ ip: string,
334
+ rid: Uint8Array,
335
+ headers: Uint8Array,
336
+ body: Uint8Array | null,
337
+ output: Uint8Array,
338
+ ): number
339
+ /**
340
+ * Write the ingress binary-layout constants (38 × u32 LE — `rust/ffi.rs`
341
+ * `IngressLayout`, numeric source `rust/ingress/output.rs`) into `output`;
342
+ * returns bytes written. Lets `src/ingress/constants.ts` read the layout via
343
+ * bun:ffi on Bun so importing the package does NOT dlopen the napi addon.
344
+ */
345
+ ingressLayout(out: Uint8Array): number
346
+
347
+ // ── Per-route native stack (`castrum_route_*`) ──────────────────
348
+ /**
349
+ * Compile a route descriptor (the `@ignex/native` route-wire v3 format:
350
+ * magic `ROUT`, version 3, limits, stage tags, draft-07 body schema) into an
351
+ * opaque handle. Returns the handle (`0` = invalid descriptor / compile
352
+ * failure / panic — throws). The handle is owned by the caller and must be
353
+ * released with {@link routeDestroy}.
354
+ */
355
+ routeCompile(descriptor: Uint8Array): number
356
+ /**
357
+ * Run a compiled route on one request frame, writing the packed verdict
358
+ * (`[flags u32][errorCode u32]` + optional query/cookie pair sections) into
359
+ * `output`. Returns bytes written (`0` = real error — throws; `> output.length`
360
+ * = the EXACT required size — the caller allocates once and retries, per the
361
+ * growExact convention).
362
+ */
363
+ routeRun(handle: number, frame: Uint8Array, output: Uint8Array): number
364
+ /** Destroy a compiled route handle (frees the native `Box<NativeRoute>`). */
365
+ routeDestroy(handle: number): void
366
+ }
367
+
368
+ /**
369
+ * bun:ffi transport selection (from `CASTRUM_FFI_MODE`; legacy `RUST_FFI_MODE`
370
+ * accepted):
371
+ *
372
+ * - `auto` (default): use bun:ffi on Bun, silently fall back to napi when the
373
+ * bind or self-test fails (and always on Node).
374
+ * - `ffi`: force bun:ffi on Bun — throw a clear error if it can't bind or the
375
+ * self-test fails (use in benches/CI that MUST run ffi).
376
+ * - `napi`: never bind — every call goes through the napi addon (the fallback;
377
+ * useful for exercising the fallback path on Bun).
378
+ */
379
+ export type FfiMode = 'auto' | 'ffi' | 'napi'
380
+
381
+ /** Unpacked rate-limit verdict (from the packed
382
+ * `[u8 allowed][u32 remaining LE][i64 reset_ms LE]` C-ABI output). */
383
+ export interface RateLimiterVerdict {
384
+ allowed: boolean
385
+ remaining: number
386
+ resetMs: number
387
+ }
388
+
389
+ /** Raw 2-arg C-ABI symbol signature (`(ptr,len)` pairs → scalar result). */
390
+ export type Raw2 = (a: unknown, b: unknown) => number | bigint
391
+ /** Raw 3-arg C-ABI symbol signature. */
392
+ export type Raw3 = (a: unknown, b: unknown, c: unknown) => number | bigint
393
+ /** Raw 4-arg C-ABI symbol signature. */
394
+ export type Raw4 = (a: unknown, b: unknown, c: unknown, d: unknown) => number | bigint
395
+ /** Raw 5-arg C-ABI symbol signature. */
396
+ export type Raw5 = (a: unknown, b: unknown, c: unknown, d: unknown, e: unknown) => number | bigint
397
+ /** Raw 6-arg C-ABI symbol signature. */
398
+ export type Raw6 = (
399
+ a: unknown,
400
+ b: unknown,
401
+ c: unknown,
402
+ d: unknown,
403
+ e: unknown,
404
+ f: unknown,
405
+ ) => number | bigint
406
+ /** Raw 7-arg C-ABI symbol signature. */
407
+ export type Raw7 = (
408
+ a: unknown,
409
+ b: unknown,
410
+ c: unknown,
411
+ d: unknown,
412
+ e: unknown,
413
+ f: unknown,
414
+ g: unknown,
415
+ ) => number | bigint
416
+ /** Raw 8-arg C-ABI symbol signature. */
417
+ export type Raw8 = (
418
+ a: unknown,
419
+ b: unknown,
420
+ c: unknown,
421
+ d: unknown,
422
+ e: unknown,
423
+ f: unknown,
424
+ g: unknown,
425
+ h: unknown,
426
+ ) => number | bigint
427
+ /** Raw 9-arg C-ABI symbol signature. */
428
+ export type Raw9 = (
429
+ a: unknown,
430
+ b: unknown,
431
+ c: unknown,
432
+ d: unknown,
433
+ e: unknown,
434
+ f: unknown,
435
+ g: unknown,
436
+ h: unknown,
437
+ i: unknown,
438
+ ) => number | bigint
439
+ /** Raw 10-arg C-ABI symbol signature. */
440
+ export type Raw10 = (
441
+ a: unknown,
442
+ b: unknown,
443
+ c: unknown,
444
+ d: unknown,
445
+ e: unknown,
446
+ f: unknown,
447
+ g: unknown,
448
+ h: unknown,
449
+ i: unknown,
450
+ j: unknown,
451
+ ) => number | bigint
452
+
453
+ /** Raw 12-arg C-ABI symbol signature (`castrum_ingress_handle_components`). */
454
+ export type Raw12 = (
455
+ a: unknown,
456
+ b: unknown,
457
+ c: unknown,
458
+ d: unknown,
459
+ e: unknown,
460
+ f: unknown,
461
+ g: unknown,
462
+ h: unknown,
463
+ i: unknown,
464
+ j: unknown,
465
+ k: unknown,
466
+ l: unknown,
467
+ ) => number | bigint
468
+
469
+ /** A raw C-ABI symbol whose return type is `cstring` — the engine clones the
470
+ * null-terminated string at return (`null` = C NULL, i.e. a failure sentinel).
471
+ * Used by the fixed-size ASCII writers (ws_accept_key, etag, random_token,
472
+ * sign/verify_cookie, csrf_token, jwt_sign_bytes).
473
+ */
474
+ export type RawCStr = (...a: unknown[]) => string | null