castrum 0.9.2 → 0.9.4

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 (184) hide show
  1. package/Cargo.lock +2259 -0
  2. package/Cargo.toml +138 -0
  3. package/README.md +6 -0
  4. package/build.rs +5 -0
  5. package/castrum.linux-x64-gnu.node +0 -0
  6. package/dist/index.d.ts +1348 -1295
  7. package/dist/index.js +4307 -3776
  8. package/docs/API.md +1 -1
  9. package/docs/ARCHITECTURE.md +3 -2
  10. package/docs/CASE_STUDY.md +2 -2
  11. package/docs/ENVIRONMENT.md +16 -0
  12. package/docs/FFI_BUN_GUIDE.md +89 -6
  13. package/docs/REPO_MAP.md +2 -2
  14. package/index.ts +19 -25
  15. package/package.json +9 -1
  16. package/rust/crypto/aead.rs +378 -0
  17. package/rust/crypto/argon2.rs +265 -0
  18. package/rust/crypto/base64.rs +420 -0
  19. package/rust/crypto/bcrypt.rs +89 -0
  20. package/rust/crypto/cookie_sign.rs +169 -0
  21. package/rust/crypto/csrf.rs +106 -0
  22. package/rust/crypto/ed25519.rs +215 -0
  23. package/rust/crypto/hashing.rs +140 -0
  24. package/rust/crypto/hmac_sha256.rs +88 -0
  25. package/rust/crypto/jwt/api.rs +286 -0
  26. package/rust/crypto/jwt/mod.rs +22 -0
  27. package/rust/crypto/jwt/tests.rs +312 -0
  28. package/rust/crypto/jwt/token.rs +360 -0
  29. package/rust/crypto/mod.rs +27 -0
  30. package/rust/crypto/pbkdf2.rs +96 -0
  31. package/rust/crypto/random_token.rs +85 -0
  32. package/rust/crypto/session.rs +222 -0
  33. package/rust/ffi/crypto.rs +708 -0
  34. package/rust/ffi/hashing.rs +285 -0
  35. package/rust/ffi/http.rs +752 -0
  36. package/rust/ffi/ingress.rs +303 -0
  37. package/rust/ffi/json.rs +517 -0
  38. package/rust/ffi/jwt.rs +419 -0
  39. package/rust/ffi/metrics.rs +373 -0
  40. package/rust/ffi/mod.rs +68 -0
  41. package/rust/ffi/payload.rs +275 -0
  42. package/rust/ffi/probe.rs +60 -0
  43. package/rust/ffi/rate_limit.rs +77 -0
  44. package/rust/ffi/route.rs +85 -0
  45. package/rust/ffi/tests.rs +2333 -0
  46. package/rust/ffi/util.rs +152 -0
  47. package/rust/ffi/validators.rs +123 -0
  48. package/rust/http/accept.rs +606 -0
  49. package/rust/http/cookie_parser.rs +140 -0
  50. package/rust/http/etag.rs +289 -0
  51. package/rust/http/form.rs +132 -0
  52. package/rust/http/headers.rs +267 -0
  53. package/rust/http/http_date.rs +286 -0
  54. package/rust/http/http_parser.rs +252 -0
  55. package/rust/http/media_type.rs +355 -0
  56. package/rust/http/method.rs +200 -0
  57. package/rust/http/mime_lookup.rs +130 -0
  58. package/rust/http/mod.rs +32 -0
  59. package/rust/http/multipart.rs +685 -0
  60. package/rust/http/query_parser.rs +180 -0
  61. package/rust/http/url_codec.rs +296 -0
  62. package/rust/http/url_join.rs +324 -0
  63. package/rust/ingress/api.rs +355 -0
  64. package/rust/ingress/cors.rs +403 -0
  65. package/rust/ingress/ingress_constants.rs +209 -0
  66. package/rust/ingress/ip_trust.rs +312 -0
  67. package/rust/ingress/mod.rs +48 -0
  68. package/rust/ingress/native_route.rs +779 -0
  69. package/rust/ingress/options.rs +176 -0
  70. package/rust/ingress/output.rs +406 -0
  71. package/rust/ingress/packed.rs +191 -0
  72. package/rust/ingress/pipeline.rs +534 -0
  73. package/rust/ingress/proxy.rs +138 -0
  74. package/rust/ingress/rate_limit.rs +538 -0
  75. package/rust/ingress/terminal.rs +114 -0
  76. package/rust/ingress/tests.rs +566 -0
  77. package/rust/ingress/time.rs +70 -0
  78. package/rust/json/fast_schema/capture.rs +336 -0
  79. package/rust/json/fast_schema/compile.rs +556 -0
  80. package/rust/json/fast_schema/cursor.rs +368 -0
  81. package/rust/json/fast_schema/email.rs +282 -0
  82. package/rust/json/fast_schema/errors.rs +247 -0
  83. package/rust/json/fast_schema/mod.rs +57 -0
  84. package/rust/json/fast_schema/tests.rs +1143 -0
  85. package/rust/json/fast_schema/types.rs +140 -0
  86. package/rust/json/fast_schema/validate.rs +931 -0
  87. package/rust/json/json_ops.rs +121 -0
  88. package/rust/json/json_schema.rs +837 -0
  89. package/rust/json/json_ser.rs +881 -0
  90. package/rust/json/mod.rs +19 -0
  91. package/rust/json/napi_marshal.rs +318 -0
  92. package/rust/json/patch/api.rs +60 -0
  93. package/rust/json/patch/engine.rs +153 -0
  94. package/rust/json/patch/mod.rs +47 -0
  95. package/rust/json/patch/ops.rs +270 -0
  96. package/rust/json/patch/pointer.rs +49 -0
  97. package/rust/json/patch/tests.rs +499 -0
  98. package/rust/lib.rs +72 -0
  99. package/rust/metrics/api.rs +148 -0
  100. package/rust/metrics/mod.rs +21 -0
  101. package/rust/metrics/registry.rs +961 -0
  102. package/rust/panic_safety.rs +40 -0
  103. package/rust/payload/compress.rs +512 -0
  104. package/rust/payload/mod.rs +13 -0
  105. package/rust/payload/sse.rs +214 -0
  106. package/rust/payload/template.rs +253 -0
  107. package/rust/payload/websocket.rs +69 -0
  108. package/rust/payload/ws_frames.rs +354 -0
  109. package/rust/proptest_suite.rs +139 -0
  110. package/rust/selection.rs +95 -0
  111. package/rust/src/bin/native-bench.rs +495 -0
  112. package/rust/test_support.rs +71 -0
  113. package/rust/util/batch/api.rs +266 -0
  114. package/rust/util/batch/core.rs +473 -0
  115. package/rust/util/batch/mod.rs +23 -0
  116. package/rust/util/batch/tests.rs +445 -0
  117. package/rust/util/batch_core.rs +184 -0
  118. package/rust/util/bytes.rs +407 -0
  119. package/rust/util/mod.rs +34 -0
  120. package/rust/util/packed.rs +473 -0
  121. package/rust/util/text.rs +130 -0
  122. package/rust/util/threadpool.rs +128 -0
  123. package/rust/util/validation.rs +419 -0
  124. package/rust-toolchain.toml +17 -0
  125. package/scripts/postinstall.mjs +199 -0
  126. package/src/ingress/constants.ts +1 -1
  127. package/src/ingress/context.ts +2 -2
  128. package/src/ingress/decode/baked-result.ts +13 -13
  129. package/src/ingress/decode/fast-result.ts +17 -17
  130. package/src/ingress/errors.ts +7 -7
  131. package/src/ingress/headers/baked-templates.ts +1 -1
  132. package/src/ingress/headers/fast-templates.ts +3 -3
  133. package/src/ingress/index.ts +1 -1
  134. package/src/ingress/native-route.ts +9 -5
  135. package/src/ingress/options.ts +1 -1
  136. package/src/ingress/packing/gather-raw-headers.ts +1 -1
  137. package/src/ingress/packing/route-wire.ts +1 -5
  138. package/src/ingress/response/error-bodies.ts +5 -5
  139. package/src/ingress/response/terminal.ts +1 -1
  140. package/src/ingress/routes/echo.ts +4 -4
  141. package/src/ingress/routes/fallback.ts +2 -2
  142. package/src/ingress/routes/head.ts +2 -2
  143. package/src/ingress/routes/native.ts +2 -1
  144. package/src/ingress/routes/options.ts +1 -1
  145. package/src/ingress/routes/read.ts +2 -2
  146. package/src/ingress/server.ts +10 -6
  147. package/src/ingress/status.ts +7 -7
  148. package/src/integration/batch.ts +1 -1
  149. package/src/integration/index.ts +9 -9
  150. package/src/integration/pipeline.ts +6 -6
  151. package/src/integration/websocket.ts +2 -2
  152. package/src/loader/index.ts +1 -5
  153. package/src/loader/ops.ts +1 -1
  154. package/src/native/ffi/build/codecs.ts +104 -8
  155. package/src/native/ffi/build/compress.ts +14 -10
  156. package/src/native/ffi/build/instances.ts +40 -3
  157. package/src/native/ffi/build/metrics.ts +224 -0
  158. package/src/native/ffi/build/parse.ts +59 -23
  159. package/src/native/ffi/build/util.ts +14 -1
  160. package/src/native/ffi/build.ts +2 -0
  161. package/src/native/ffi/selftest.ts +10 -3
  162. package/src/native/ffi/types.ts +111 -9
  163. package/src/native/ffi.ts +87 -1
  164. package/src/native/index.ts +1 -1
  165. package/src/native/loader.ts +2 -2
  166. package/src/native/types/instances.ts +34 -0
  167. package/src/native/types.ts +11 -0
  168. package/src/runtime/codec.ts +53 -9
  169. package/src/runtime/types.ts +7 -0
  170. package/src/rust-ffi/batch/build.ts +16 -9
  171. package/src/rust-ffi/index.ts +7 -6
  172. package/src/rust-ffi/options.ts +1 -3
  173. package/src/rust-ffi/proven.ts +3 -3
  174. package/src/rust-ffi/scalar/crypto.ts +1 -1
  175. package/src/rust-ffi/scalar/factories.ts +73 -0
  176. package/src/rust-ffi/scalar/hashing.ts +1 -1
  177. package/src/rust-ffi/scalar/http.ts +17 -11
  178. package/src/rust-ffi/scalar/index.ts +2 -0
  179. package/src/rust-ffi/scalar/interface.ts +20 -0
  180. package/src/rust-ffi/scalar/payload.ts +11 -3
  181. package/src/rust-ffi/scalar/validation.ts +121 -0
  182. package/src/selection.ts +1 -1
  183. package/src/shared/bytes.ts +5 -4
  184. package/src/shared/codec.ts +18 -3
package/dist/index.d.ts CHANGED
@@ -1,285 +1,150 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
- /** A single JSON Schema validation error (fast path + DOM fallback). */
4
- export interface SchemaError {
5
- /** RFC 6901 JSON pointer to the failing instance value ("" = root). */
6
- instancePath: string;
7
- /** JSON pointer into the schema at the failing keyword. */
8
- schemaPath: string;
9
- /** The failing keyword (e.g. "type", "pattern", "required"). */
10
- keyword: string;
11
- /** Human-readable failure message. */
12
- message: string;
13
- }
14
- /** The native `SchemaValidator` class instance. */
15
- export interface SchemaValidatorInstance {
16
- /** Validate a single JSON document. */
17
- validate(input: Uint8Array): boolean;
18
- /** Opaque handle (bun:ffi fast path) — `castrum_schema_validator_validate`. */
19
- innerPtr?(): bigint;
20
- /** Validate a single JSON document, returning detailed errors (empty = valid). */
21
- validateDetailed(input: Uint8Array): SchemaError[];
22
- /** Validate a single JSON document, returning only the first error (null = valid). */
23
- validateFirstError(input: Uint8Array): SchemaError | null;
24
- validateBatchPackedCount(packed: Uint8Array): number;
25
- validateBatchPackedBitset(packed: Uint8Array): Uint8Array;
26
- /**
27
- * One-pass validate + extract: validate `input` against the schema and
28
- * capture scalar values / array lengths at `paths` during the same native
29
- * walk (no `JSON.parse`, no DOM). For "derive" routes (response built from a
30
- * handful of body fields) this replaces `JSON.parse` + Ajv on the happy path
31
- * and rejects invalid bodies with zero DOM/GC.
32
- *
33
- * `paths` are RFC 6901 JSON pointers of OBJECT KEYS; a trailing `/-`
34
- * captures the ARRAY LENGTH at that path (e.g. `"/totalCents"`,
35
- * `"/lineItems/-"`). Array-index steps are not supported.
36
- */
37
- derive(input: Uint8Array, paths: string[]): JsonDeriveResult;
38
- }
39
- /** A single derived value captured during one-pass validation. */
40
- export interface JsonDeriveValue {
41
- /** `"int" | "number" | "string" | "bool" | "null"`. */
42
- kind: string;
43
- int: number | null;
44
- number: number | null;
45
- text: string | null;
46
- boolean: boolean | null;
47
- }
48
- /** Result of a one-pass `validate + derive`. */
49
- export interface JsonDeriveResult {
50
- /** `true` when the document is schema-valid; `false` → caller rejects. */
51
- ok: boolean;
52
- /** One entry per requested path (`null` = path absent from the document). */
53
- values: Array<JsonDeriveValue | null>;
54
- }
55
- /** The native `HmacSigner` class instance. */
56
- export interface HmacSignerInstance {
57
- sign(data: Uint8Array): Uint8Array;
58
- verify(data: Uint8Array, sig: Uint8Array): boolean;
59
- }
60
- /** The native `FormParser` class instance (reusable output buffer). */
61
- export interface FormParserInstance {
62
- /** Parse an x-www-form-urlencoded body into packed pairs (reuses the buffer). */
63
- parse(input: Uint8Array): Uint8Array;
64
- /** Zero-alloc parse into a caller-provided output buffer. */
65
- parseInto(input: Uint8Array, output: Uint8Array): number;
66
- }
67
- /** A parsed Content-Type / media type. */
68
- export interface MediaTypeResult {
69
- /** Lowercased `type/subtype`. */
70
- mediaType: string;
71
- charset: string | null;
72
- boundary: string | null;
73
- params: Record<string, string>;
74
- }
75
- /** The native `MediaTypeParser` class instance. */
76
- export interface MediaTypeParserInstance {
77
- parse(input: Uint8Array): MediaTypeResult;
78
- }
79
- /** The native `ConditionalRequest` class instance (per-resource 304 checks). */
80
- export interface ConditionalRequestInstance {
81
- isNotModified(ifNoneMatch: Uint8Array | null, ifModifiedSince: Uint8Array | null): boolean;
82
- /** Opaque handle to the precompiled state (present when the bun:ffi fast path
83
- * is active) — `castrum_conditional_is_not_modified` in rust/ffi.rs. */
84
- innerPtr?(): bigint;
3
+ /** Route descriptor magic (`"ROUT"` LE). Must match `ROUTE_DESC_MAGIC` in Rust. */
4
+ export declare const ROUTE_DESC_MAGIC = 1380930900;
5
+ /** Wire version — bump on ANY descriptor/frame/result layout change. */
6
+ export declare const ROUTE_DESC_VERSION = 3;
7
+ /** Descriptor stage tags (the ordered pipeline a route instance runs). */
8
+ export declare const ROUTE_STAGE: {
9
+ readonly parseQuery: 0;
10
+ readonly parseCookies: 1;
11
+ readonly validateQuery: 2;
12
+ readonly validateCookies: 3;
13
+ readonly validateBody: 4;
14
+ readonly requireJsonBody: 5;
15
+ };
16
+ /** A descriptor stage tag (`ROUTE_STAGE` values, must match Rust). */
17
+ export type RouteStageTag = (typeof ROUTE_STAGE)[keyof typeof ROUTE_STAGE];
18
+ /** Descriptor part tags (`RoutePartKind`): the schema-bearing request parts. */
19
+ export declare const ROUTE_PART: {
20
+ readonly body: 3;
21
+ };
22
+ /** Result flag bits (`ROUTE_RESULT_FLAG_*` in Rust). */
23
+ export declare const ROUTE_FLAG: {
24
+ readonly OK: number;
25
+ readonly BODY_VALID_JSON: number;
26
+ readonly QUERY_VALID: number;
27
+ readonly COOKIE_VALID: number;
28
+ readonly BODY_VALID: number;
29
+ };
30
+ /** Frame flag: the body section is present (bit 0 of the frame flags word). */
31
+ export declare const ROUTE_FRAME_FLAG_HAS_BODY: number;
32
+ /** Size limits a route descriptor carries. */
33
+ export interface RouteWireLimits {
34
+ maxBodyBytes: number;
35
+ maxQueryBytes: number;
36
+ maxCookieBytes: number;
37
+ maxPairs: number;
85
38
  }
86
- /** One parsed Accept-Encoding preference. */
87
- export interface EncodingPrefResult {
88
- encoding: string;
89
- q: number;
90
- order: number;
39
+ /** A schema part carried by the descriptor (part tag + draft-07 JSON bytes). */
40
+ export interface RouteWireSchema {
41
+ part: number;
42
+ bytes: Uint8Array;
91
43
  }
92
44
  /**
93
- * The native `AcceptNegotiator` class instance (supported encodings compiled
94
- * once). Best supported encoding for a header, or `null` for identity.
45
+ * Encode a route plan into the descriptor wire.
46
+ *
47
+ * Layout: `[magic u32][version u32][maxBody u32][maxQuery u32][maxCookie u32]
48
+ * [maxPairs u32][stageCount u32][stages u8…][schemaCount u32]
49
+ * { [part u8][len u32][schema] }…`
95
50
  */
96
- export interface AcceptNegotiatorInstance {
97
- /** Best supported encoding for `header`, or null for identity. */
98
- negotiate(header: Uint8Array): string | null;
99
- /** Best supported encoding for `header` with SERVER-preference tie-breaking
100
- * (RFC 7231 server semantics — the supported list's order breaks q-value
101
- * ties), or null for identity. On the napi fallback this is the projected
102
- * `negotiate_server_preference` method. */
103
- negotiateServerPreference(header: Uint8Array): string | null;
104
- /** Opaque handle (bun:ffi fast path) — `castrum_accept_negotiator_negotiate`. */
105
- innerPtr?(): bigint;
51
+ export declare function encodeRouteDescriptor(pipeline: readonly RouteStageTag[], schemas: readonly RouteWireSchema[], limits: RouteWireLimits): Uint8Array;
52
+ /**
53
+ * Encode a request frame: `[flags u32][qLen][query][cLen][cookie]([bLen][body])`.
54
+ * The `flags` word currently only carries `ROUTE_FRAME_FLAG_HAS_BODY`.
55
+ */
56
+ export declare function packRouteFrame(query: string, cookie: string, body: Uint8Array | null): Uint8Array;
57
+ /** A decoded `[name, value]` pair from a result section. */
58
+ export type RouteWirePair = [
59
+ string,
60
+ string
61
+ ];
62
+ /** The decoded route result: the verdict header + optional pair sections. */
63
+ export interface RouteWireResult {
64
+ /** `ROUTE_FLAG_*` bits from the result header. */
65
+ flags: number;
66
+ /** `0` = ok; `400` = not-JSON under requireJsonBody; `422` = schema fail. */
67
+ errorCode: number;
68
+ /** Decoded query pairs (present iff the plan compiled `parseQuery`). */
69
+ query: RouteWirePair[];
70
+ /** Decoded cookie pairs (present iff the plan compiled `parseCookies`). */
71
+ cookie: RouteWirePair[];
106
72
  }
107
- /** The native `Base64Codec` class instance (config compiled once). */
108
- export interface Base64CodecInstance {
109
- encode(input: Uint8Array): Uint8Array;
110
- decode(input: Uint8Array): Uint8Array;
73
+ /**
74
+ * Decode the result wire: `[flags u32][errorCode u32]` + a query pair section
75
+ * iff `query` and a cookie pair section iff `cookie` (the caller knows its own
76
+ * plan). Sections are `[count u32] { [nameLen u32][name][valueLen u32][value] }`.
77
+ */
78
+ export declare function decodeRouteResult(buf: Uint8Array, opts: {
79
+ query: boolean;
80
+ cookie: boolean;
81
+ }): RouteWireResult;
82
+ /** Plan for a compiled native route (the stage/limit/schema surface). */
83
+ export interface NativeRoutePlan {
84
+ /** Parse + lenient-decode the query string into pairs (stage 0). */
85
+ parseQuery?: boolean;
86
+ /** Parse + lenient-decode the Cookie header into pairs (stage 1). */
87
+ parseCookies?: boolean;
88
+ /** Reject a missing / non-JSON body with 400 (stage 5, first-failure-wins). */
89
+ requireJsonBody?: boolean;
90
+ /** Validate the body against `schema` with 422 on failure (stage 4). */
91
+ validateBody?: boolean;
92
+ /** Draft-07 JSON schema bytes for the body (`validateBody`). */
93
+ schema?: Uint8Array;
94
+ /** Max body bytes before `requireJsonBody`/`validateBody` fail (default 2 MiB). */
95
+ maxBodyBytes?: number;
96
+ /** Max query bytes before the parse VALID bit clears (default 8192). */
97
+ maxQueryBytes?: number;
98
+ /** Max cookie bytes before the parse VALID bit clears (default 8192). */
99
+ maxCookieBytes?: number;
100
+ /** Max pairs per section (0 = unlimited; default 0). */
101
+ maxPairs?: number;
111
102
  }
112
- /** Result of a native rate-limit check for one key at a point in time. */
113
- export interface RateCheckResult {
114
- allowed: boolean;
115
- remaining: number;
116
- resetMs: number;
103
+ /** A compiled per-route native stack (route-wire v3). */
104
+ export interface NativeRoute {
105
+ /** Whether the plan compiled `parseQuery` (the result carries a query section). */
106
+ readonly parseQuery: boolean;
107
+ /** Whether the plan compiled `parseCookies` (the result carries a cookie section). */
108
+ readonly parseCookies: boolean;
109
+ /**
110
+ * Run one request frame through the compiled stack and return the decoded
111
+ * verdict. The frame is `[flags u32][qLen][query][cLen][cookie]([bLen][body])`
112
+ * — build it with {@link packRouteFrame}. Reuses one growable output buffer
113
+ * (the needed-size convention: `0` = real error → throws; `> out.length` =
114
+ * exact required size → retry once).
115
+ */
116
+ runFrame(frame: Uint8Array): RouteWireResult;
117
+ /** Convenience: pack a `(query, cookie, body)` frame then {@link runFrame}. */
118
+ run(query: string, cookie: string, body: Uint8Array | null): RouteWireResult;
119
+ /** Free the native handle (idempotent; safe to call once at shutdown). */
120
+ destroy(): void;
117
121
  }
118
- /** The native `RateLimiter` class instance (sharded fixed-window state). */
119
- export interface RateLimiterInstance {
120
- check(key: string, nowMs: number): RateCheckResult;
121
- checkKey(key: number, nowMs: number): RateCheckResult;
122
- /** Opaque handle (bun:ffi fast path) — `castrum_rate_limiter_check*`. */
123
- innerPtr?(): bigint;
124
- }
125
- /** The native `CookieSigner` class instance (HMAC key compiled once). */
126
- export interface CookieSignerInstance {
127
- sign(value: Uint8Array): Uint8Array;
128
- /** Returns the signed value without its signature, or null on invalid. */
129
- verify(signed: Uint8Array): Uint8Array | null;
130
- }
131
- /** The native `CsrfProtector` class instance (HMAC key compiled once). */
132
- export interface CsrfProtectorInstance {
133
- create(): Uint8Array;
134
- verify(token: Uint8Array): boolean;
135
- }
136
- /** The native `UrlBuilder` class instance (base parsed once). */
137
- export interface UrlBuilderInstance {
138
- resolve(reference: Uint8Array): Uint8Array;
139
- /** Opaque handle to the precompiled base (present when the bun:ffi fast path
140
- * is active) — `castrum_url_builder_resolve` in rust/ffi.rs. */
141
- innerPtr?(): bigint;
142
- }
143
- /** Options for `passwordHash` (argon2id). */
144
- export interface PasswordHashOptions {
145
- mCost?: number;
146
- tCost?: number;
147
- pCost?: number;
148
- outLen?: number;
149
- }
150
- /** A parsed multipart/form-data part. */
151
- export interface MultipartPart {
152
- name: string;
153
- filename: string | null;
154
- contentType: string | null;
155
- data: Uint8Array;
156
- }
157
- /** A decoded RFC 6455 websocket frame. */
158
- export interface WsFrame {
159
- fin: boolean;
160
- opcode: number;
161
- payload: Uint8Array;
162
- }
163
- /** The native `TemplateRenderer` class instance. */
164
- export interface TemplateRendererInstance {
165
- render(context: unknown): Uint8Array;
166
- /** Render from pre-serialized JSON context bytes (no napi Value marshal). */
167
- renderBytes(contextJson: Uint8Array): Uint8Array;
168
- /** Parallel batch render: packed contexts in → packed rendered out. */
169
- renderBatchPacked(data: Uint8Array): Uint8Array;
170
- /** Opaque handle (bun:ffi fast path) — `castrum_template_render`. */
171
- innerPtr?(): bigint;
172
- }
173
- /** The native `MediaTypeMatcher` class instance (expected precompiled once). */
174
- export interface MediaTypeMatcherInstance {
175
- /** Wildcard match against the precompiled expected type. */
176
- matches(actual: Uint8Array): boolean;
177
- /** Opaque handle (bun:ffi fast path) — `castrum_media_type_matcher_matches`. */
178
- innerPtr?(): bigint;
179
- }
180
- /** The native `JwtSigner` class instance (HS256 key + ttl compiled once). */
181
- export interface JwtSignerInstance {
182
- sign(claims: unknown, nowSeconds: number): Uint8Array;
183
- /** Sign from pre-serialized claim JSON bytes (no napi Value marshal). */
184
- signBytes(claimsJson: Uint8Array, nowSeconds: number): Uint8Array;
185
- verify(token: Uint8Array, nowSeconds: number): unknown;
186
- /** Opaque handle (bun:ffi fast path) — `castrum_jwt_signer_*`. */
187
- innerPtr?(): bigint;
188
- }
189
- /** The native `AeadCipher` class instance (algorithm + key compiled once). */
190
- export interface AeadCipherInstance {
191
- encrypt(nonce: Uint8Array, plaintext: Uint8Array): Uint8Array;
192
- decrypt(nonce: Uint8Array, ciphertext: Uint8Array): Uint8Array | null;
193
- }
194
- /** The native `Argon2Hasher` class instance (params compiled once). */
195
- export interface Argon2HasherInstance {
196
- hash(password: Uint8Array, salt: Uint8Array): Uint8Array;
197
- verify(password: Uint8Array, phc: Uint8Array): boolean;
198
- }
199
- /** Parse a query string (`a=1&b=2`) into an object via the native parser. */
200
- export declare function parseQueryString(query: string): Record<string, string | string[]>;
201
- /** Parse a cookie header (`a=1; b=2`) into an object via the native parser. */
202
- export declare function parseCookieHeader(header: string): Record<string, string | string[]>;
203
- /** Parse an application/x-www-form-urlencoded body into a key/value object. */
204
- export declare function parseFormBody(body: Uint8Array): Record<string, string | string[]>;
205
- /** Native JSON-Schema validator handle (see `createSchemaValidator`). */
206
- export type SchemaValidator = SchemaValidatorInstance;
207
- /**
208
- * Unpack a packed u32 array: `[u32 count]{[u32 value]}` → `Uint32Array`.
209
- * @throws {RangeError} when the buffer is shorter than `count` × 4 bytes.
210
- */
211
- export declare function unpackU32Array(bytes: Uint8Array): Uint32Array;
212
- /**
213
- * Unpack a packed bitset: `[u32 count]{bit per item}` → one 0/1 byte per item.
214
- * @throws {RangeError} when the buffer is shorter than `count` bits.
215
- */
216
- export declare function unpackBitset(bytes: Uint8Array): Uint8Array;
217
- /**
218
- * Unpack a packed i64 array as SIGNED `BigInt64Array`.
219
- * @throws {RangeError} when the buffer is shorter than `count` × 8 bytes.
220
- */
221
- export declare function unpackI64ArrayAsBigInt(bytes: Uint8Array): BigInt64Array;
222
- /**
223
- * Unpack a packed byte-results buffer: `[u32 count]{[u32 len][bytes]}` → one
224
- * `Uint8Array` slice per item. The slices reference `bytes`'s backing store
225
- * (zero-copy) — copy them if held beyond the buffer's lifetime.
226
- */
227
- export declare function unpackByteResults(bytes: Uint8Array): Uint8Array[];
228
- /** A decoded `[name, value]` pair from a packed buffer. */
229
- export type Pair = [
230
- string,
231
- string
232
- ];
233
- /**
234
- * Decode a packed `[u32 count][u32 keyLen][key][u32 valLen][val]...` buffer
235
- * into `[name, value]` string pairs (see `packPairs` for the layout).
236
- */
237
- export declare function readPairsPacked(bytes: Uint8Array): Pair[];
238
- /**
239
- * Fold `[name, value]` pairs into an object; repeated names collect into a
240
- * string array (query-string/cookie semantics).
241
- */
242
- export declare function pairsToObject(pairs: Pair[]): Record<string, string | string[]>;
243
- /** A decoded HTTP request from the native packed parser. */
244
- export interface ParsedHttpRequestPacked {
245
- method: string;
246
- path: string;
247
- version: string;
248
- headers: Record<string, string>;
249
- }
250
- /**
251
- * Decode a native `httpParseRequestPacked` buffer into a plain request object
252
- * (zero-alloc on the Rust side; this builds the JS strings once).
253
- */
254
- export declare function readHttpPacked(bytes: Uint8Array): ParsedHttpRequestPacked;
255
- /**
256
- * Build a packed batch buffer. Returns an OWNED buffer (safe to hold across
257
- * later calls) — this is the public entry point. Hot internal paths that can
258
- * guarantee synchronous consumption should use `withPackScratch` instead.
259
- */
260
- export declare function packBatch(items: Uint8Array[]): Uint8Array;
261
- /**
262
- * Pack `[name, value]` string pairs into the native `[u32 count]...` layout
263
- * that `readPairsPacked` / the batch validators consume.
264
- */
265
- export declare function packPairs(pairs: Array<[
266
- string,
267
- string
268
- ]>): Uint8Array;
269
- /** Bounds-checked section offsets for one ingress output buffer. */
270
- export interface PackedSectionLayout {
271
- /** Cookie-JSON length clamped to the buffer (0 if none / out of range). */
272
- safeCookiesLen: number;
273
- /** Byte offset where the query-JSON section begins. */
274
- queryStart: number;
275
- /** Query-JSON length clamped to the buffer (0 if none / out of range). */
276
- safeQueryLen: number;
277
- /** Byte offset where the body-JSON section begins. */
278
- bodyJsonStart: number;
279
- /** Body-JSON length clamped to the buffer (0 if none / out of range). */
280
- safeBodyJsonLen: number;
281
- /** Whether any declared section length overran the buffer. */
282
- truncated: boolean;
122
+ /**
123
+ * Compile a per-route native stack from a plan. Returns a compiled route whose
124
+ * `run`/`runFrame` perform the parse + verdict in ONE native call.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const route = createNativeRoute({ parseQuery: true, parseCookies: true })
129
+ * const result = route.run('page=1&limit=20', 'session=abc', null)
130
+ * // result.flags & ROUTE_FLAG.OK → parse succeeded; result.query = [['page','1'],…]
131
+ * ```
132
+ */
133
+ export declare function createNativeRoute(plan?: NativeRoutePlan): NativeRoute;
134
+ /** Bounds-checked section offsets for one ingress output buffer. */
135
+ export interface PackedSectionLayout {
136
+ /** Cookie-JSON length clamped to the buffer (0 if none / out of range). */
137
+ safeCookiesLen: number;
138
+ /** Byte offset where the query-JSON section begins. */
139
+ queryStart: number;
140
+ /** Query-JSON length clamped to the buffer (0 if none / out of range). */
141
+ safeQueryLen: number;
142
+ /** Byte offset where the body-JSON section begins. */
143
+ bodyJsonStart: number;
144
+ /** Body-JSON length clamped to the buffer (0 if none / out of range). */
145
+ safeBodyJsonLen: number;
146
+ /** Whether any declared section length overran the buffer. */
147
+ truncated: boolean;
283
148
  }
284
149
  declare abstract class IngressResultBase {
285
150
  /** Raw native verdict byte (0 = accepted). */
@@ -356,6 +221,23 @@ export declare class BakedIngressResult extends IngressResultBase {
356
221
  setInternalError(): void;
357
222
  bodyJson(copy: boolean): Uint8Array;
358
223
  }
224
+ /** Zero-alloc, lazy-decoding view over a native Ingress output buffer. */
225
+ export declare class FastIngressResult extends IngressResultBase {
226
+ status: number;
227
+ verdict: number;
228
+ errorCode: number;
229
+ flags: number;
230
+ https: boolean;
231
+ hasCookies: boolean;
232
+ hasQuery: boolean;
233
+ rateLimit: number;
234
+ requestId: string;
235
+ refresh(buf: Uint8Array, body: Uint8Array, requestId: string): void;
236
+ invalidate(): void;
237
+ setInternalError(requestId: string): void;
238
+ /** Bounds-checked body-JSON section, or an empty view when absent. */
239
+ bodyJson(): Uint8Array;
240
+ }
359
241
  /** User-facing CORS configuration. */
360
242
  export interface CorsOptions {
361
243
  allowOrigin?: string[];
@@ -389,23 +271,6 @@ export interface SecurityHeadersOptions {
389
271
  corp?: string;
390
272
  xssProtection?: string;
391
273
  }
392
- /** Zero-alloc, lazy-decoding view over a native Ingress output buffer. */
393
- export declare class FastIngressResult extends IngressResultBase {
394
- status: number;
395
- verdict: number;
396
- errorCode: number;
397
- flags: number;
398
- https: boolean;
399
- hasCookies: boolean;
400
- hasQuery: boolean;
401
- rateLimit: number;
402
- requestId: string;
403
- refresh(buf: Uint8Array, body: Uint8Array, requestId: string): void;
404
- invalidate(): void;
405
- setInternalError(requestId: string): void;
406
- /** Bounds-checked body-JSON section, or an empty view when absent. */
407
- bodyJson(): Uint8Array;
408
- }
409
274
  /** Options accepted by `createIngressFast` / `createIngress`. */
410
275
  export interface IngressFastOptions {
411
276
  trustProxy?: boolean;
@@ -656,141 +521,10 @@ export interface BakedHandlerOptions {
656
521
  */
657
522
  enableLoader?: boolean;
658
523
  }
659
- /** Route descriptor magic (`"ROUT"` LE). Must match `ROUTE_DESC_MAGIC` in Rust. */
660
- export declare const ROUTE_DESC_MAGIC = 1380930900;
661
- /** Wire version — bump on ANY descriptor/frame/result layout change. */
662
- export declare const ROUTE_DESC_VERSION = 3;
663
- /** Descriptor stage tags (the ordered pipeline a route instance runs). */
664
- export declare const ROUTE_STAGE: {
665
- readonly parseQuery: 0;
666
- readonly parseCookies: 1;
667
- readonly validateQuery: 2;
668
- readonly validateCookies: 3;
669
- readonly validateBody: 4;
670
- readonly requireJsonBody: 5;
671
- };
672
- /** A descriptor stage tag (`ROUTE_STAGE` values, must match Rust). */
673
- export type RouteStageTag = (typeof ROUTE_STAGE)[keyof typeof ROUTE_STAGE];
674
- /** Descriptor part tags (`RoutePartKind`): the schema-bearing request parts. */
675
- export declare const ROUTE_PART: {
676
- readonly body: 3;
677
- };
678
- /** Result flag bits (`ROUTE_RESULT_FLAG_*` in Rust). */
679
- export declare const ROUTE_FLAG: {
680
- readonly OK: number;
681
- readonly BODY_VALID_JSON: number;
682
- readonly QUERY_VALID: number;
683
- readonly COOKIE_VALID: number;
684
- readonly BODY_VALID: number;
685
- };
686
- /** Frame flag: the body section is present (bit 0 of the frame flags word). */
687
- export declare const ROUTE_FRAME_FLAG_HAS_BODY: number;
688
- /** Size limits a route descriptor carries. */
689
- export interface RouteWireLimits {
690
- maxBodyBytes: number;
691
- maxQueryBytes: number;
692
- maxCookieBytes: number;
693
- maxPairs: number;
694
- }
695
- /** A schema part carried by the descriptor (part tag + draft-07 JSON bytes). */
696
- export interface RouteWireSchema {
697
- part: number;
698
- bytes: Uint8Array;
699
- }
700
- /**
701
- * Encode a route plan into the descriptor wire.
702
- *
703
- * Layout: `[magic u32][version u32][maxBody u32][maxQuery u32][maxCookie u32]
704
- * [maxPairs u32][stageCount u32][stages u8…][schemaCount u32]
705
- * { [part u8][len u32][schema] }…`
706
- */
707
- export declare function encodeRouteDescriptor(pipeline: readonly RouteStageTag[], schemas: readonly RouteWireSchema[], limits: RouteWireLimits): Uint8Array;
708
- /**
709
- * Encode a request frame: `[flags u32][qLen][query][cLen][cookie]([bLen][body])`.
710
- * The `flags` word currently only carries `ROUTE_FRAME_FLAG_HAS_BODY`.
711
- */
712
- export declare function packRouteFrame(query: string, cookie: string, body: Uint8Array | null): Uint8Array;
713
- /** A decoded `[name, value]` pair from a result section. */
714
- export type RouteWirePair = [
715
- string,
716
- string
717
- ];
718
- /** The decoded route result: the verdict header + optional pair sections. */
719
- export interface RouteWireResult {
720
- /** `ROUTE_FLAG_*` bits from the result header. */
721
- flags: number;
722
- /** `0` = ok; `400` = not-JSON under requireJsonBody; `422` = schema fail. */
723
- errorCode: number;
724
- /** Decoded query pairs (present iff the plan compiled `parseQuery`). */
725
- query: RouteWirePair[];
726
- /** Decoded cookie pairs (present iff the plan compiled `parseCookies`). */
727
- cookie: RouteWirePair[];
728
- }
729
- /**
730
- * Decode the result wire: `[flags u32][errorCode u32]` + a query pair section
731
- * iff `query` and a cookie pair section iff `cookie` (the caller knows its own
732
- * plan). Sections are `[count u32] { [nameLen u32][name][valueLen u32][value] }`.
733
- */
734
- export declare function decodeRouteResult(buf: Uint8Array, opts: {
735
- query: boolean;
736
- cookie: boolean;
737
- }): RouteWireResult;
738
- /** Plan for a compiled native route (the stage/limit/schema surface). */
739
- export interface NativeRoutePlan {
740
- /** Parse + lenient-decode the query string into pairs (stage 0). */
741
- parseQuery?: boolean;
742
- /** Parse + lenient-decode the Cookie header into pairs (stage 1). */
743
- parseCookies?: boolean;
744
- /** Reject a missing / non-JSON body with 400 (stage 5, first-failure-wins). */
745
- requireJsonBody?: boolean;
746
- /** Validate the body against `schema` with 422 on failure (stage 4). */
747
- validateBody?: boolean;
748
- /** Draft-07 JSON schema bytes for the body (`validateBody`). */
749
- schema?: Uint8Array;
750
- /** Max body bytes before `requireJsonBody`/`validateBody` fail (default 2 MiB). */
751
- maxBodyBytes?: number;
752
- /** Max query bytes before the parse VALID bit clears (default 8192). */
753
- maxQueryBytes?: number;
754
- /** Max cookie bytes before the parse VALID bit clears (default 8192). */
755
- maxCookieBytes?: number;
756
- /** Max pairs per section (0 = unlimited; default 0). */
757
- maxPairs?: number;
758
- }
759
- /** A compiled per-route native stack (route-wire v3). */
760
- export interface NativeRoute {
761
- /** Whether the plan compiled `parseQuery` (the result carries a query section). */
762
- readonly parseQuery: boolean;
763
- /** Whether the plan compiled `parseCookies` (the result carries a cookie section). */
764
- readonly parseCookies: boolean;
765
- /**
766
- * Run one request frame through the compiled stack and return the decoded
767
- * verdict. The frame is `[flags u32][qLen][query][cLen][cookie]([bLen][body])`
768
- * — build it with {@link packRouteFrame}. Reuses one growable output buffer
769
- * (the needed-size convention: `0` = real error → throws; `> out.length` =
770
- * exact required size → retry once).
771
- */
772
- runFrame(frame: Uint8Array): RouteWireResult;
773
- /** Convenience: pack a `(query, cookie, body)` frame then {@link runFrame}. */
774
- run(query: string, cookie: string, body: Uint8Array | null): RouteWireResult;
775
- /** Free the native handle (idempotent; safe to call once at shutdown). */
776
- destroy(): void;
777
- }
778
- /**
779
- * Compile a per-route native stack from a plan. Returns a compiled route whose
780
- * `run`/`runFrame` perform the parse + verdict in ONE native call.
781
- *
782
- * @example
783
- * ```ts
784
- * const route = createNativeRoute({ parseQuery: true, parseCookies: true })
785
- * const result = route.run('page=1&limit=20', 'session=abc', null)
786
- * // result.flags & ROUTE_FLAG.OK → parse succeeded; result.query = [['page','1'],…]
787
- * ```
788
- */
789
- export declare function createNativeRoute(plan?: NativeRoutePlan): NativeRoute;
790
- /** A matched dynamic route: the handler map + the extracted path params. */
791
- export interface PathMatch {
792
- methods: Record<string, unknown>;
793
- params: Record<string, string>;
524
+ /** A matched dynamic route: the handler map + the extracted path params. */
525
+ export interface PathMatch {
526
+ methods: Record<string, unknown>;
527
+ params: Record<string, string>;
794
528
  }
795
529
  /** A raw web-standard route handler (`Request` → `Response`), e.g. the
796
530
  * health/metrics probes (`livenessHandler`, `readinessHandler`,
@@ -986,675 +720,265 @@ export interface WebSocketUpgradeResult {
986
720
  * ```
987
721
  */
988
722
  export declare function createWebSocketUpgrade(req: Request, opts?: WebSocketUpgradeOptions): WebSocketUpgradeResult | null;
989
- /** Options accepted by `createRust` / `rust.configure`. */
990
- export interface RustOptions {
723
+ /**
724
+ * The native `MetricsRegistry` class instance — sharded counters / gauges /
725
+ * histograms with a Prometheus text-format render. Series are declared once
726
+ * (`counter`/`gauge`/`histogram` return a reusable id); per-event updates
727
+ * take the id + the label values for that event.
728
+ */
729
+ export interface MetricsRegistryInstance {
730
+ /** Declare a counter family → its series id (idempotent per shape). */
731
+ counter(name: string, labelKeys?: string[]): number;
732
+ /** Declare a gauge family → its series id (idempotent per shape). */
733
+ gauge(name: string, labelKeys?: string[]): number;
734
+ /**
735
+ * Declare a histogram family → its series id. Omitted/empty `buckets`
736
+ * selects the default latency buckets.
737
+ */
738
+ histogram(name: string, labelKeys?: string[], buckets?: number[]): number;
739
+ /** Counter/gauge `+= amount`, or histogram observe `amount`. */
740
+ record(series: number, values?: string[], amount?: number): void;
741
+ /** Gauge assignment (`= value`). */
742
+ gaugeSet(series: number, values: string[] | undefined, value: number): void;
743
+ /** Render the Prometheus text exposition format (deterministic order). */
744
+ render(): string;
991
745
  /**
992
- * Rayon thread pool size.
746
+ * Packed v1 series snapshot bytes (families then series — see the Rust
747
+ * registry docs). bun:ffi-backed wrappers only; napi exposes
748
+ * `MetricsRegistry.snapshot()` directly.
749
+ */
750
+ snapshot?(): Uint8Array;
751
+ /** Number of live series (tests / introspection; napi transport only). */
752
+ seriesCount?(): number;
753
+ /** Free the native handle (bun:ffi-backed wrappers only). */
754
+ destroy?(): void;
755
+ }
756
+ /** A single JSON Schema validation error (fast path + DOM fallback). */
757
+ export interface SchemaError {
758
+ /** RFC 6901 JSON pointer to the failing instance value ("" = root). */
759
+ instancePath: string;
760
+ /** JSON pointer into the schema at the failing keyword. */
761
+ schemaPath: string;
762
+ /** The failing keyword (e.g. "type", "pattern", "required"). */
763
+ keyword: string;
764
+ /** Human-readable failure message. */
765
+ message: string;
766
+ }
767
+ /** The native `SchemaValidator` class instance. */
768
+ export interface SchemaValidatorInstance {
769
+ /** Validate a single JSON document. */
770
+ validate(input: Uint8Array): boolean;
771
+ /** Opaque handle (bun:ffi fast path) — `castrum_schema_validator_validate`. */
772
+ innerPtr?(): bigint;
773
+ /** Validate a single JSON document, returning detailed errors (empty = valid). */
774
+ validateDetailed(input: Uint8Array): SchemaError[];
775
+ /** Validate a single JSON document, returning only the first error (null = valid). */
776
+ validateFirstError(input: Uint8Array): SchemaError | null;
777
+ validateBatchPackedCount(packed: Uint8Array): number;
778
+ validateBatchPackedBitset(packed: Uint8Array): Uint8Array;
779
+ /**
780
+ * One-pass validate + extract: validate `input` against the schema and
781
+ * capture scalar values / array lengths at `paths` during the same native
782
+ * walk (no `JSON.parse`, no DOM). For "derive" routes (response built from a
783
+ * handful of body fields) this replaces `JSON.parse` + Ajv on the happy path
784
+ * and rejects invalid bodies with zero DOM/GC.
993
785
  *
994
- * The rayon pool is process-wide and initialized **once** (native
995
- * `OnceLock`) — the first initialization wins. Importing this module
996
- * initializes the pool with defaults, so this option only takes effect when
997
- * the pool has not yet been created. For reliable control set
998
- * `CASTRUM_RAYON_THREADS` / `RUST_RAYON_THREADS` before import.
999
- * Default: max(1, hardwareConcurrency - 1).
786
+ * `paths` are RFC 6901 JSON pointers of OBJECT KEYS; a trailing `/-`
787
+ * captures the ARRAY LENGTH at that path (e.g. `"/totalCents"`,
788
+ * `"/lineItems/-"`). Array-index steps are not supported.
1000
789
  */
1001
- rayonThreads?: number;
1002
- /** Cache MIME lookups keyed by normalized extension. Default: true. */
1003
- mimeCache?: boolean;
1004
- /** Reuse HMAC signers keyed by key-buffer identity. Default: true. */
1005
- hmacCache?: boolean;
790
+ derive(input: Uint8Array, paths: string[]): JsonDeriveResult;
791
+ }
792
+ /** A single derived value captured during one-pass validation. */
793
+ export interface JsonDeriveValue {
794
+ /** `"int" | "number" | "string" | "bool" | "null"`. */
795
+ kind: string;
796
+ int: number | null;
797
+ number: number | null;
798
+ text: string | null;
799
+ boolean: boolean | null;
800
+ }
801
+ /** Result of a one-pass `validate + derive`. */
802
+ export interface JsonDeriveResult {
803
+ /** `true` when the document is schema-valid; `false` → caller rejects. */
804
+ ok: boolean;
805
+ /** One entry per requested path (`null` = path absent from the document). */
806
+ values: Array<JsonDeriveValue | null>;
807
+ }
808
+ /** The native `HmacSigner` class instance. */
809
+ export interface HmacSignerInstance {
810
+ sign(data: Uint8Array): Uint8Array;
811
+ verify(data: Uint8Array, sig: Uint8Array): boolean;
812
+ }
813
+ /** The native `FormParser` class instance (reusable output buffer). */
814
+ export interface FormParserInstance {
815
+ /** Parse an x-www-form-urlencoded body into packed pairs (reuses the buffer). */
816
+ parse(input: Uint8Array): Uint8Array;
817
+ /** Zero-alloc parse into a caller-provided output buffer. */
818
+ parseInto(input: Uint8Array, output: Uint8Array): number;
819
+ }
820
+ /** A parsed Content-Type / media type. */
821
+ export interface MediaTypeResult {
822
+ /** Lowercased `type/subtype`. */
823
+ mediaType: string;
824
+ charset: string | null;
825
+ boundary: string | null;
826
+ params: Record<string, string>;
827
+ }
828
+ /** The native `MediaTypeParser` class instance. */
829
+ export interface MediaTypeParserInstance {
830
+ parse(input: Uint8Array): MediaTypeResult;
831
+ }
832
+ /** The native `ConditionalRequest` class instance (per-resource 304 checks). */
833
+ export interface ConditionalRequestInstance {
834
+ isNotModified(ifNoneMatch: Uint8Array | null, ifModifiedSince: Uint8Array | null): boolean;
835
+ /** Opaque handle to the precompiled state (present when the bun:ffi fast path
836
+ * is active) — `castrum_conditional_is_not_modified` in rust/ffi.rs. */
837
+ innerPtr?(): bigint;
838
+ }
839
+ /** One parsed Accept-Encoding preference. */
840
+ export interface EncodingPrefResult {
841
+ encoding: string;
842
+ q: number;
843
+ order: number;
1006
844
  }
1007
845
  /**
1008
- * The `rust.batch.*` array-of-bytes batch surface: N items in → one packed
1009
- * native call → bitsets / typed arrays / byte arrays out. Separated from the
1010
- * implementation (`./build.ts`) so type-only consumers don't pull the native
1011
- * resolver.
846
+ * The native `AcceptNegotiator` class instance (supported encodings compiled
847
+ * once). Best supported encoding for a header, or `null` for identity.
1012
848
  */
1013
- export interface RustBatch {
1014
- jsonValid(items: Uint8Array[]): Uint8Array;
1015
- crc32(items: Uint8Array[]): Uint32Array;
1016
- validateEmail(items: Uint8Array[]): Uint8Array;
1017
- validateUuid(items: Uint8Array[]): Uint8Array;
1018
- validateIpv4(items: Uint8Array[]): Uint8Array;
1019
- validateIpv6(items: Uint8Array[]): Uint8Array;
1020
- jsonSumIds(items: Uint8Array[]): BigInt64Array;
1021
- queryParse(items: Uint8Array[]): Uint8Array[];
1022
- cookieParse(items: Uint8Array[]): Uint8Array[];
1023
- httpParseRequest(items: Uint8Array[]): Uint8Array[];
1024
- jsonPatch(docs: Uint8Array[], patches: Uint8Array[]): Uint8Array[];
1025
- hexEncode(items: Uint8Array[]): Uint8Array[];
1026
- hexDecode(items: Uint8Array[]): Uint8Array[];
1027
- base64Encode(items: Uint8Array[], urlSafe?: boolean, padding?: boolean): Uint8Array[];
1028
- base64Decode(items: Uint8Array[], urlSafe?: boolean, padding?: boolean): Uint8Array[];
1029
- formParse(items: Uint8Array[]): Uint8Array[];
1030
- signCookie(items: Uint8Array[], secret: Uint8Array): Uint8Array[];
1031
- verifyCookie(items: Uint8Array[], secret: Uint8Array): Uint8Array;
1032
- csrfVerify(items: Uint8Array[], secret: Uint8Array): Uint8Array;
1033
- schemaValidate(validator: SchemaValidator, items: Uint8Array[]): Uint8Array;
1034
- schemaValidateCount(validator: SchemaValidator, items: Uint8Array[]): number;
1035
- fnv1a64(items: Uint8Array[]): BigUint64Array;
1036
- etag(items: Uint8Array[], weak?: boolean | null): Uint8Array[];
1037
- urlEncode(items: Uint8Array[]): Uint8Array[];
1038
- urlDecode(items: Uint8Array[]): Uint8Array[];
1039
- urlDecodeBytes(items: Uint8Array[]): Uint8Array[];
1040
- mimeFromExtension(items: Uint8Array[]): Uint8Array[];
1041
- wsAcceptKey(items: Uint8Array[]): Uint8Array[];
1042
- base64UrlEncode(items: Uint8Array[]): Uint8Array[];
1043
- base64UrlDecode(items: Uint8Array[]): Uint8Array[];
1044
- passwordHash(passwords: Uint8Array[], salt: Uint8Array, options?: PasswordHashOptions | null): Uint8Array[];
1045
- aeadEncrypt(key: Uint8Array, nonce: Uint8Array, items: Uint8Array[], algorithm?: string | null): Uint8Array[];
1046
- aeadDecrypt(key: Uint8Array, nonce: Uint8Array, items: Uint8Array[], algorithm?: string | null): Uint8Array[];
1047
- gzipCompress(items: Uint8Array[], level?: number | null): Uint8Array[];
1048
- gzipDecompress(items: Uint8Array[], maxDecompressed?: number | null): Uint8Array[];
1049
- brotliCompress(items: Uint8Array[], quality?: number | null): Uint8Array[];
1050
- brotliDecompress(items: Uint8Array[], maxDecompressed?: number | null): Uint8Array[];
1051
- jwtVerify(tokens: Uint8Array[], secret: Uint8Array, nowSeconds: number): Uint8Array;
1052
- hmacSha256(items: Uint8Array[], key: Uint8Array): Uint8Array[];
1053
- hmacSha256Verify(items: Uint8Array[], sigs: Uint8Array[], key: Uint8Array): Uint8Array;
1054
- passwordVerify(items: Uint8Array[], phcs: Uint8Array[]): Uint8Array;
1055
- urlResolve(bases: Uint8Array[], references: Uint8Array[]): Uint8Array[];
1056
- jwtSign(items: Uint8Array[], secret: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number): Uint8Array[];
1057
- sseEncode(items: Uint8Array[], event?: string | null, id?: string | null, retry?: number | null): Uint8Array[];
1058
- wsFrameEncode(items: Uint8Array[], opcode: number, mask: boolean, fin: boolean): Uint8Array[];
1059
- wsFrameDecode(items: Uint8Array[]): Uint8Array[];
1060
- multipartParse(items: Uint8Array[], boundary: Uint8Array): MultipartPart[][];
1061
- templateRender(source: string, contexts: Uint8Array[]): Uint8Array[];
849
+ export interface AcceptNegotiatorInstance {
850
+ /** Best supported encoding for `header`, or null for identity. */
851
+ negotiate(header: Uint8Array): string | null;
852
+ /** Best supported encoding for `header` with SERVER-preference tie-breaking
853
+ * (RFC 7231 server semantics — the supported list's order breaks q-value
854
+ * ties), or null for identity. On the napi fallback this is the projected
855
+ * `negotiate_server_preference` method. */
856
+ negotiateServerPreference(header: Uint8Array): string | null;
857
+ /** Opaque handle (bun:ffi fast path) — `castrum_accept_negotiator_negotiate`. */
858
+ innerPtr?(): bigint;
1062
859
  }
1063
- /** Raw packed-wire FFI namespace. */
1064
- export interface RustPacked {
1065
- /** Packed CRC32 batch: packed `[u8[]]` items → packed u32 results. */
1066
- crc32BatchPacked(input: Uint8Array): Uint8Array;
1067
- /** Packed JSON-validity batch: packed `[u8[]]` items → packed 0/1 bitset. */
1068
- jsonValidBatchPacked(input: Uint8Array): Uint8Array;
1069
- /** Packed email-validator batch: packed `[u8[]]` items → packed 0/1 bitset. */
1070
- validateEmailBatchPacked(input: Uint8Array): Uint8Array;
1071
- /** Packed UUID-validator batch: packed `[u8[]]` items → packed 0/1 bitset. */
1072
- validateUuidBatchPacked(input: Uint8Array): Uint8Array;
1073
- /** Packed IPv4-validator batch: packed `[u8[]]` items → packed 0/1 bitset. */
1074
- validateIpv4BatchPacked(input: Uint8Array): Uint8Array;
1075
- /** Packed IPv6-validator batch: packed `[u8[]]` items → packed 0/1 bitset. */
1076
- validateIpv6BatchPacked(input: Uint8Array): Uint8Array;
1077
- /** Packed JSON-sum batch: packed `[{id}]` docs → packed `[ok u8][sum i64 LE]`. */
1078
- jsonSumBatchPacked(input: Uint8Array): Uint8Array;
1079
- /** Packed query-string parser batch: packed `[u8[]]` queries → packed pair lists. */
1080
- queryParseBatchPacked(input: Uint8Array): Uint8Array;
1081
- /** Packed cookie-header parser batch: packed `[u8[]]` headers → packed pair lists. */
1082
- cookieParseBatchPacked(input: Uint8Array): Uint8Array;
1083
- /** Packed HTTP-request parser batch: packed `[u8[]]` requests → packed request sections. */
1084
- httpParseRequestBatchPacked(input: Uint8Array): Uint8Array;
1085
- /** Batch RFC 6902 JSON Patch (two packed lists, zipped) → packed results. */
1086
- jsonPatchBatchPacked(docs: Uint8Array, patches: Uint8Array): Uint8Array;
1087
- /** Packed FNV-1a 64 batch (i64 per item). */
1088
- fnv1A64BatchPacked(input: Uint8Array): Uint8Array;
1089
- /** Packed ETag batch (10 strong / 12 weak bytes per item). */
1090
- etagBatchPacked(input: Uint8Array, weak?: boolean | null): Uint8Array;
1091
- /** Packed percent-encoding batch: packed `[u8[]]` items → packed hex-percent bytes. */
1092
- urlEncodeBatchPacked(input: Uint8Array): Uint8Array;
1093
- /** Packed percent-decoding batch: packed `[u8[]]` items → packed decoded bytes (string-safe). */
1094
- urlDecodeBatchPacked(input: Uint8Array): Uint8Array;
1095
- /** Packed percent-decoding batch returning raw bytes (`%FF` preserved). */
1096
- urlDecodeBytesBatchPacked(input: Uint8Array): Uint8Array;
1097
- /** Packed MIME-lookup batch: packed `[u8[]]` extensions → packed media-type strings. */
1098
- mimeFromExtensionBatchPacked(input: Uint8Array): Uint8Array;
1099
- /** Packed WebSocket accept-key batch: packed `[u8[]]` keys → packed base64 SHA-1 results. */
1100
- wsAcceptKeyBatchPacked(input: Uint8Array): Uint8Array;
1101
- /** Valid JSON count in a packed JSON batch (0..n; invalid items not counted). */
1102
- jsonValidBatchCountPacked(input: Uint8Array): number;
1103
- /** Valid-email count in a packed email batch. */
1104
- validateEmailBatchCountPacked(input: Uint8Array): number;
1105
- /** Valid-UUID count in a packed UUID batch. */
1106
- validateUuidBatchCountPacked(input: Uint8Array): number;
1107
- /** Valid-IPv4 count in a packed IPv4 batch. */
1108
- validateIpv4BatchCountPacked(input: Uint8Array): number;
1109
- /** Valid-IPv6 count in a packed IPv6 batch. */
1110
- validateIpv6BatchCountPacked(input: Uint8Array): number;
1111
- /** Total `id` sum across a packed JSON batch (valid items only). */
1112
- jsonSumBatchTotalPacked(input: Uint8Array): number;
1113
- /** Total packed-output length (bytes) a query batch will produce. */
1114
- queryParseBatchTotalLenPacked(input: Uint8Array): number;
1115
- /** Total packed-output length (bytes) a cookie batch will produce. */
1116
- cookieParseBatchTotalLenPacked(input: Uint8Array): number;
1117
- /** Total packed-output length (bytes) an HTTP-request batch will produce. */
1118
- httpParseRequestBatchTotalLenPacked(input: Uint8Array): number;
1119
- /** Packed Argon2id-hash batch: packed `[u8[]]` passwords + shared salt → packed PHC strings. */
1120
- passwordHashBatchPacked(input: Uint8Array, salt: Uint8Array, options?: PasswordHashOptions | null): Uint8Array;
1121
- /** Packed password-verify batch (two packed lists, zipped) → bitset. */
1122
- passwordVerifyBatchPacked(passwords: Uint8Array, phcs: Uint8Array): Uint8Array;
1123
- /** Packed URL-resolve batch (two packed lists, zipped) → packed results. */
1124
- urlResolveBatchPacked(bases: Uint8Array, references: Uint8Array): Uint8Array;
1125
- /** Packed AEAD-encrypt batch (packed inputs + shared key/nonce) → packed ciphertexts. */
1126
- aeadEncryptBatchPacked(input: Uint8Array, key: Uint8Array, nonce: Uint8Array, algorithm?: string | null): Uint8Array;
1127
- /** Packed AEAD-decrypt batch (packed inputs + shared key/nonce) → packed plaintexts. */
1128
- aeadDecryptBatchPacked(input: Uint8Array, key: Uint8Array, nonce: Uint8Array, algorithm?: string | null): Uint8Array;
1129
- /** Packed gzip-compress batch: packed `[u8[]]` items → packed gzip streams. */
1130
- gzipCompressBatchPacked(input: Uint8Array, level?: number | null): Uint8Array;
1131
- /** Packed gzip-decompress batch (64 MiB cap per item) → packed plaintexts. */
1132
- gzipDecompressBatchPacked(input: Uint8Array): Uint8Array;
1133
- /** Packed brotli-compress batch: packed `[u8[]]` items → packed brotli streams. */
1134
- brotliCompressBatchPacked(input: Uint8Array, quality?: number | null): Uint8Array;
1135
- /** Packed brotli-decompress batch (64 MiB cap per item) → packed plaintexts. */
1136
- brotliDecompressBatchPacked(input: Uint8Array): Uint8Array;
1137
- /** Packed HS256 JWT-sign batch (packed claims + shared secret) → packed tokens. */
1138
- jwtSignBatchPacked(input: Uint8Array, secret: Uint8Array, ttlSeconds: number | null, nowSeconds: number): Uint8Array;
1139
- /** Packed HS256 JWT-verify batch (packed tokens + shared secret) → packed claims/validity. */
1140
- jwtVerifyBatchPacked(input: Uint8Array, secret: Uint8Array, nowSeconds: number): Uint8Array;
1141
- /** Batch HMAC-SHA256 sign (packed in → hex-signature byte results). */
1142
- hmacSha256BatchPacked(input: Uint8Array, key: Uint8Array): Uint8Array;
1143
- /** Batch HMAC-SHA256 verify (two packed lists: data + hex sigs, zipped) → bitset. */
1144
- hmacSha256VerifyBatchPacked(input: Uint8Array, sigs: Uint8Array, key: Uint8Array): Uint8Array;
1145
- /** Packed multipart/form-data parser batch (packed bodies + shared boundary) → packed parts. */
1146
- multipartParseBatchPacked(input: Uint8Array, boundary: Uint8Array): Uint8Array;
1147
- /** Packed minijinja template batch: packed contexts + shared source → packed renders. */
1148
- templateRenderBatchPacked(input: Uint8Array, source: string): Uint8Array;
1149
- /** Packed SSE-frame batch: packed data + shared event/id/retry → packed frames. */
1150
- sseEncodeBatchPacked(input: Uint8Array, event: string | null, id: string | null, retry: number | null): Uint8Array;
1151
- /** Packed WebSocket-frame encode batch: packed payloads + shared opcode/mask/fin → packed frames. */
1152
- wsFrameEncodeBatchPacked(input: Uint8Array, opcode: number, mask: boolean, fin: boolean): Uint8Array;
1153
- /** Packed WebSocket-frame decode batch: packed frames → packed (fin, opcode, payload) results. */
1154
- wsFrameDecodeBatchPacked(input: Uint8Array): Uint8Array;
860
+ /** The native `Base64Codec` class instance (config compiled once). */
861
+ export interface Base64CodecInstance {
862
+ encode(input: Uint8Array): Uint8Array;
863
+ decode(input: Uint8Array): Uint8Array;
864
+ }
865
+ /** Result of a native rate-limit check for one key at a point in time. */
866
+ export interface RateCheckResult {
867
+ allowed: boolean;
868
+ remaining: number;
869
+ resetMs: number;
870
+ }
871
+ /** The native `RateLimiter` class instance (sharded fixed-window state). */
872
+ export interface RateLimiterInstance {
873
+ check(key: string, nowMs: number): RateCheckResult;
874
+ checkKey(key: number, nowMs: number): RateCheckResult;
875
+ /** Opaque handle (bun:ffi fast path) — `castrum_rate_limiter_check*`. */
876
+ innerPtr?(): bigint;
877
+ }
878
+ /** The native `CookieSigner` class instance (HMAC key compiled once). */
879
+ export interface CookieSignerInstance {
880
+ sign(value: Uint8Array): Uint8Array;
881
+ /** Returns the signed value without its signature, or null on invalid. */
882
+ verify(signed: Uint8Array): Uint8Array | null;
883
+ }
884
+ /** The native `CsrfProtector` class instance (HMAC key compiled once). */
885
+ export interface CsrfProtectorInstance {
886
+ create(): Uint8Array;
887
+ verify(token: Uint8Array): boolean;
888
+ }
889
+ /** The native `UrlBuilder` class instance (base parsed once). */
890
+ export interface UrlBuilderInstance {
891
+ resolve(reference: Uint8Array): Uint8Array;
892
+ /** Opaque handle to the precompiled base (present when the bun:ffi fast path
893
+ * is active) — `castrum_url_builder_resolve` in rust/ffi.rs. */
894
+ innerPtr?(): bigint;
895
+ }
896
+ /** Options for `passwordHash` (argon2id). */
897
+ export interface PasswordHashOptions {
898
+ mCost?: number;
899
+ tCost?: number;
900
+ pCost?: number;
901
+ outLen?: number;
902
+ }
903
+ /** A parsed multipart/form-data part. */
904
+ export interface MultipartPart {
905
+ name: string;
906
+ filename: string | null;
907
+ contentType: string | null;
908
+ data: Uint8Array;
909
+ }
910
+ /** A decoded RFC 6455 websocket frame. */
911
+ export interface WsFrame {
912
+ fin: boolean;
913
+ opcode: number;
914
+ payload: Uint8Array;
915
+ }
916
+ /** The native `TemplateRenderer` class instance. */
917
+ export interface TemplateRendererInstance {
918
+ render(context: unknown): Uint8Array;
919
+ /** Render from pre-serialized JSON context bytes (no napi Value marshal). */
920
+ renderBytes(contextJson: Uint8Array): Uint8Array;
921
+ /** Parallel batch render: packed contexts in → packed rendered out. */
922
+ renderBatchPacked(data: Uint8Array): Uint8Array;
923
+ /** Opaque handle (bun:ffi fast path) — `castrum_template_render`. */
924
+ innerPtr?(): bigint;
925
+ }
926
+ /** The native `MediaTypeMatcher` class instance (expected precompiled once). */
927
+ export interface MediaTypeMatcherInstance {
928
+ /** Wildcard match against the precompiled expected type. */
929
+ matches(actual: Uint8Array): boolean;
930
+ /** Opaque handle (bun:ffi fast path) — `castrum_media_type_matcher_matches`. */
931
+ innerPtr?(): bigint;
932
+ }
933
+ /** The native `JwtSigner` class instance (HS256 key + ttl compiled once). */
934
+ export interface JwtSignerInstance {
935
+ sign(claims: unknown, nowSeconds: number): Uint8Array;
936
+ /** Sign from pre-serialized claim JSON bytes (no napi Value marshal). */
937
+ signBytes(claimsJson: Uint8Array, nowSeconds: number): Uint8Array;
938
+ verify(token: Uint8Array, nowSeconds: number): unknown;
939
+ /** Opaque handle (bun:ffi fast path) — `castrum_jwt_signer_*`. */
940
+ innerPtr?(): bigint;
941
+ }
942
+ /** The native `AeadCipher` class instance (algorithm + key compiled once). */
943
+ export interface AeadCipherInstance {
944
+ encrypt(nonce: Uint8Array, plaintext: Uint8Array): Uint8Array;
945
+ decrypt(nonce: Uint8Array, ciphertext: Uint8Array): Uint8Array | null;
946
+ }
947
+ /** The native `Argon2Hasher` class instance (params compiled once). */
948
+ export interface Argon2HasherInstance {
949
+ hash(password: Uint8Array, salt: Uint8Array): Uint8Array;
950
+ verify(password: Uint8Array, phc: Uint8Array): boolean;
1155
951
  }
1156
952
  /**
1157
- * The flat `rust.*` scalar method surface: bytes in → normalized out. Built by
1158
- * the scalar builders from the native addon + Bun built-in delegations.
953
+ * Create the runtime-appropriate ingress server: `Bun.serve` on Bun, the
954
+ * `node:http` adapter on Node. Selected ONCE at module load — the branch
955
+ * never runs in the hot path.
1159
956
  */
1160
- export interface RustScalar {
1161
- crc32(input: Uint8Array): number;
1162
- fnv1a64(input: Uint8Array): bigint;
1163
- /**
1164
- * XXH3-64 over raw bytes (high-throughput non-cryptographic hash).
1165
- * Compare against `Bun.hash.xxHash3` — see docs/bun-builtins-decision-matrix.md.
1166
- */
1167
- xxh3(input: Uint8Array): bigint;
1168
- hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array | string;
1169
- hmacSha256Verify(key: Uint8Array, data: Uint8Array, sig: Uint8Array | string): boolean;
1170
- jsonValid(input: Uint8Array): boolean;
1171
- /**
1172
- * Parse JSON to a JS value (FFI packed structural parse on Bun — sonic-rs
1173
- * parses ONCE, JS assembles from a token stream with no re-parse; napi
1174
- * sonic-rs DOM fallback). Throws on invalid JSON.
1175
- */
1176
- jsonParse(input: Uint8Array): unknown;
1177
- jsonSumIds(input: Uint8Array): bigint;
1178
- jsonPatch(doc: Uint8Array, patch: Uint8Array): Uint8Array | string;
1179
- mimeFromExtension(ext: Uint8Array): Uint8Array | string;
1180
- randomToken(byteLen: number): Uint8Array | string;
1181
- randomTokenInto(byteLen: number, output: Uint8Array): number;
1182
- urlEncode(input: Uint8Array): Uint8Array | string;
1183
- urlDecode(input: Uint8Array): Uint8Array;
1184
- /**
1185
- * Strict percent-decode without UTF-8 validation.
1186
- */
1187
- urlDecodeBytes(input: Uint8Array): Uint8Array;
1188
- urlEncodeInto(input: Uint8Array, output: Uint8Array): number;
1189
- urlDecodeInto(input: Uint8Array, output: Uint8Array): number;
1190
- validateEmail(input: Uint8Array): boolean;
1191
- validateUuid(input: Uint8Array): boolean;
1192
- validateIpv4(input: Uint8Array): boolean;
1193
- validateIpv6(input: Uint8Array): boolean;
1194
- wsAcceptKey(key: Uint8Array): Uint8Array | string;
1195
- wsAcceptKeyInto(key: Uint8Array, output: Uint8Array): number;
1196
- httpParseRequestPacked(input: Uint8Array): Uint8Array;
1197
- queryParsePacked(input: Uint8Array): Uint8Array;
1198
- cookieParsePacked(input: Uint8Array): Uint8Array;
1199
- httpParseRequestPackedInto(input: Uint8Array, output: Uint8Array): number;
1200
- queryParsePackedInto(input: Uint8Array, output: Uint8Array): number;
1201
- cookieParsePackedInto(input: Uint8Array, output: Uint8Array): number;
1202
- /**
1203
- * Parse an application/x-www-form-urlencoded body into packed pairs.
1204
- */
1205
- formParsePacked(input: Uint8Array): Uint8Array;
1206
- formParsePackedInto(input: Uint8Array, output: Uint8Array): number;
1207
- /**
1208
- * Parse a Content-Type header into a structured media type.
1209
- */
1210
- parseMediaType(input: Uint8Array): MediaTypeResult;
1211
- /**
1212
- * Generate a strong/weak ETag (crc32-based).
1213
- */
1214
- etag(data: Uint8Array, weak?: boolean): Uint8Array | string;
1215
- etagInto(data: Uint8Array, output: Uint8Array, weak?: boolean): number;
1216
- /**
1217
- * Format a unix timestamp as an IMF-fixdate HTTP-date.
1218
- */
1219
- httpDate(secs?: number): Uint8Array | string;
1220
- httpDateInto(secs: number | undefined, output: Uint8Array): number;
1221
- parseHttpDate(input: Uint8Array): bigint | null;
1222
- parseAcceptEncoding(input: Uint8Array): EncodingPrefResult[];
1223
- /**
1224
- * Base64 encode (standard by default; url-safe/padding configurable).
1225
- */
1226
- base64Encode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array | string;
1227
- /**
1228
- * Base64 decode (standard); throws on invalid input.
1229
- */
1230
- base64Decode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array;
1231
- base64EncodeInto(input: Uint8Array, output: Uint8Array, urlSafe?: boolean, padding?: boolean): number;
1232
- base64DecodeInto(input: Uint8Array, output: Uint8Array, urlSafe?: boolean, padding?: boolean): number;
1233
- base64UrlEncode(input: Uint8Array): Uint8Array | string;
1234
- base64UrlDecode(input: Uint8Array): Uint8Array;
1235
- /**
1236
- * Lowercase hex encode.
1237
- */
1238
- hexEncode(input: Uint8Array): Uint8Array | string;
1239
- /**
1240
- * Hex decode; throws on odd length or invalid digits.
1241
- */
1242
- hexDecode(input: Uint8Array): Uint8Array;
1243
- hexEncodeInto(input: Uint8Array, output: Uint8Array): number;
1244
- hexDecodeInto(input: Uint8Array, output: Uint8Array): number;
1245
- hmacSha256Into(key: Uint8Array, data: Uint8Array, output: Uint8Array): number;
1246
- signCookieInto(value: Uint8Array, secret: Uint8Array, output: Uint8Array): number;
1247
- verifyCookieInto(signed: Uint8Array, secret: Uint8Array, output: Uint8Array): number | null;
1248
- csrfTokenInto(secret: Uint8Array, output: Uint8Array): number;
1249
- aeadEncryptInto(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, output: Uint8Array, algorithm?: string | null): number;
1250
- wsFrameEncodeInto(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean, output: Uint8Array): number;
1251
- gzipCompressInto(data: Uint8Array, output: Uint8Array, level?: number | null): number;
1252
- brotliCompressInto(data: Uint8Array, output: Uint8Array, quality?: number | null): number;
1253
- /**
1254
- * Sign a cookie value as `value.signature` (HMAC-SHA256 hex).
1255
- */
1256
- signCookie(value: Uint8Array, secret: Uint8Array): Uint8Array | string;
1257
- /**
1258
- * Verify a signed cookie; returns the value or null.
1259
- */
1260
- verifyCookie(signed: Uint8Array, secret: Uint8Array): Uint8Array | string | null;
1261
- /**
1262
- * Create a CSRF token (32-byte random hex + HMAC signature).
1263
- */
1264
- csrfToken(secret: Uint8Array): Uint8Array | string;
1265
- /**
1266
- * Constant-time verify a CSRF token.
1267
- */
1268
- csrfVerify(token: Uint8Array, secret: Uint8Array): boolean;
1269
- /**
1270
- * Resolve a URL reference against a base (RFC 3986).
1271
- */
1272
- urlResolve(base: Uint8Array, reference: Uint8Array): Uint8Array | string;
1273
- /**
1274
- * Build a percent-encoded query string from params (sorted keys).
1275
- */
1276
- urlEncodeQuery(params: Record<string, string>): Uint8Array | string;
1277
- createSchemaValidator(schema: Uint8Array): SchemaValidatorInstance;
1278
- createHmacSigner(key: Uint8Array): HmacSignerInstance;
1279
- createTemplateRenderer(source: string): TemplateRendererInstance;
1280
- createFormParser(capacity?: number): FormParserInstance;
1281
- createMediaTypeParser(): MediaTypeParserInstance;
1282
- /**
1283
- * Higher-order conditional-request check: per-resource 304 decision (setup once).
1284
- */
1285
- createConditionalRequest(etagValue: Uint8Array, lastModifiedSecs?: number): ConditionalRequestInstance;
1286
- /**
1287
- * Higher-order negotiator: precompiles supported encodings once.
1288
- */
1289
- createAcceptNegotiator(supported: string[]): AcceptNegotiatorInstance;
1290
- createBase64Codec(urlSafe?: boolean, padding?: boolean): Base64CodecInstance;
1291
- createCookieSigner(secret: Uint8Array): CookieSignerInstance;
1292
- createCsrfProtector(secret: Uint8Array): CsrfProtectorInstance;
1293
- createUrlBuilder(base: Uint8Array): UrlBuilderInstance;
1294
- createJwtSigner(secret: Uint8Array, ttlSeconds?: number): JwtSignerInstance;
1295
- createAeadCipher(key: Uint8Array, algorithm?: string): AeadCipherInstance;
1296
- createArgon2Hasher(options?: PasswordHashOptions | null): Argon2HasherInstance;
1297
- createMediaTypeMatcher(expected: Uint8Array): MediaTypeMatcherInstance;
1298
- /**
1299
- * Sharded fixed-window per-key rate limiter. `maxEntries` clamps internally
1300
- * (default 1,048,576). Each instance owns an independent budget.
1301
- * @remarks Scalar per-check cost: ~3-14x SLOWER than the JS Map baseline —
1302
- * prefer the ingress pipeline (one FFI for all stages) for native rate
1303
- * limiting in a request path. [measured 2026-08]
1304
- */
1305
- createRateLimiter(limit: number, windowMs: number, maxEntries?: number | null): RateLimiterInstance;
1306
- initThreadPool(rayonThreads?: number): void;
1307
- rayonNumThreads(): number;
1308
- jwtSign(claims: Record<string, unknown>, secret: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number): Uint8Array;
1309
- /**
1310
- * Sign a JWT from pre-serialized claim JSON bytes — skips the napi
1311
- * `serde_json::Value` marshal of `jwtSign` for callers that already hold the
1312
- * bytes (same injection semantics).
1313
- */
1314
- jwtSignBytes(claimsJson: Uint8Array, secret: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number): Uint8Array | string;
1315
- jwtSignBytesInto(claimsJson: Uint8Array, secret: Uint8Array, output: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number): number;
1316
- jwtVerify(token: Uint8Array, secret: Uint8Array, nowSeconds?: number): unknown;
1317
- passwordHash(password: Uint8Array, salt: Uint8Array, options?: PasswordHashOptions | null): Uint8Array | string;
1318
- passwordVerify(password: Uint8Array, phc: Uint8Array): boolean;
1319
- /**
1320
- * bcrypt password hash → `$2b$` PHC string (cost clamped 4..=31).
1321
- */
1322
- passwordHashBcrypt(password: Uint8Array, cost: number): string;
1323
- /**
1324
- * Verify a password against a bcrypt `$2b$` PHC string.
1325
- */
1326
- passwordVerifyBcrypt(password: Uint8Array, hash: string): boolean;
1327
- /**
1328
- * PBKDF2-HMAC-SHA256 key derivation (dkLen clamped to 1 MiB).
1329
- */
1330
- pbkdf2Sha256(password: Uint8Array, salt: Uint8Array, rounds: number, dkLen: number): Uint8Array;
1331
- aeadEncrypt(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, algorithm?: string | null): Uint8Array;
1332
- aeadDecrypt(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, algorithm?: string | null): Uint8Array | null;
1333
- gzipCompress(data: Uint8Array, level?: number | null): Uint8Array;
1334
- /**
1335
- * @param maxDecompressed Caps decompressed output (default 64 MiB) as a
1336
- * decompression-bomb guard; a larger value errors.
1337
- */
1338
- gzipDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array;
1339
- /**
1340
- * gzip-decompress into `output` (pooled — caller-owned buffer); returns bytes
1341
- * written. Throws when `output` is too small (the exact required size is
1342
- * reported) or the input is invalid / exceeds `maxDecompressed`.
1343
- * @param maxDecompressed Caps decompressed output (default 64 MiB) as a
1344
- * decompression-bomb guard; a larger value errors.
1345
- */
1346
- gzipDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number | null): number;
1347
- brotliCompress(data: Uint8Array, quality?: number | null): Uint8Array;
1348
- /**
1349
- * @param maxDecompressed Caps decompressed output (default 64 MiB) as a
1350
- * decompression-bomb guard; a larger value errors.
1351
- */
1352
- brotliDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array;
1353
- /**
1354
- * brotli-decompress into `output` (pooled — caller-owned buffer); returns
1355
- * bytes written. Throws when `output` is too small (the exact required size
1356
- * is reported) or the input is invalid / exceeds `maxDecompressed`.
1357
- * @param maxDecompressed Caps decompressed output (default 64 MiB) as a
1358
- * decompression-bomb guard; a larger value errors.
1359
- */
1360
- brotliDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number | null): number;
1361
- multipartParse(body: Uint8Array, boundary: Uint8Array): MultipartPart[];
1362
- multipartParsePacked(body: Uint8Array, boundary: Uint8Array): Uint8Array;
1363
- multipartParsePackedInto(body: Uint8Array, boundary: Uint8Array, output: Uint8Array): number;
1364
- wsFrameEncode(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean): Uint8Array;
1365
- wsFrameDecode(data: Uint8Array): WsFrame | null;
1366
- wsFrameDecodePackedInto(data: Uint8Array, output: Uint8Array): number | null;
1367
- sseEncodeEvent(event: string | null, data: Uint8Array, id: string | null, retry: number | null): Uint8Array;
1368
- sseEncodeEventInto(event: string | null, data: Uint8Array, id: string | null, retry: number | null, output: Uint8Array): number;
1369
- }
1370
- /** String-oriented FFI namespace. */
1371
- export interface RustText {
1372
- /**
1373
- * Resolve a lowercase file extension to a MIME type string (e.g. `"html"` →
1374
- * `"text/html"`). Unknown extensions return `"application/octet-stream"`.
1375
- * @param ext the file extension WITHOUT a leading dot, case-insensitive.
1376
- * @returns the MIME type for `ext`.
1377
- */
1378
- mimeFromExtension(ext: string): string;
1379
- /**
1380
- * RFC 3986 percent-encode a string (the `encodeURIComponent` unreserved
1381
- * set). Under Bun this delegates to the native `encodeURIComponent` for
1382
- * ~4x speed with byte-for-byte parity.
1383
- * @param input the string to encode.
1384
- * @returns the percent-encoded string.
1385
- */
1386
- urlEncode(input: string): string;
1387
- /**
1388
- * Percent-decode a string. Throws on malformed `%XX` or invalid UTF-8
1389
- * output (parity with `decodeURIComponent`, which is used under Bun).
1390
- * @param input the percent-encoded string to decode.
1391
- * @returns the decoded string.
1392
- */
1393
- urlDecode(input: string): string;
1394
- /**
1395
- * Compute the RFC 6455 `Sec-WebSocket-Accept` value for a client key.
1396
- * @param key the base64 `Sec-WebSocket-Key` header value.
1397
- * @returns the 28-byte base64 accept value.
1398
- */
1399
- wsAcceptKey(key: string): string;
1400
- /** @param input an email address. @returns whether it is RFC-valid. */
1401
- validateEmail(input: string): boolean;
1402
- /** @param input a UUID string. @returns whether it is a valid (v1-v8) UUID. */
1403
- validateUuid(input: string): boolean;
1404
- /** @param input an IPv4 address. @returns whether it is valid. */
1405
- validateIpv4(input: string): boolean;
1406
- /** @param input an IPv6 address. @returns whether it is valid. */
1407
- validateIpv6(input: string): boolean;
1408
- }
1409
- /** The full Rust FFI client (scalar methods + namespaces + configuration). */
1410
- export interface RustClient extends RustScalar {
1411
- text: RustText;
1412
- batch: RustBatch;
1413
- packed: RustPacked;
1414
- /**
1415
- * Resolve the native transport in use on this runtime: `"ffi"` (bun:ffi —
1416
- * the PRIMARY transport under Bun) or `"napi"` (Node, forced
1417
- * `CASTRUM_FFI_MODE=napi`, or a failed ffi bind-time self-test).
1418
- */
1419
- transport(): "ffi" | "napi";
1420
- /** Whether the bun:ffi transport is live (equivalent to `transport() === "ffi"`). */
1421
- ffiActive(): boolean;
1422
- /**
1423
- * Update defaults on this instance. `mimeCache` / `hmacCache` apply
1424
- * immediately (per-instance). `rayonThreads` is applied only if the
1425
- * process-wide pool is not yet initialized (see {@link RustOptions}).
1426
- */
1427
- configure(options: RustOptions): void;
1428
- }
957
+ export declare function createIngressServer(options: CreateIngressServerOptions): BakedServer;
1429
958
  /**
1430
- * Create an isolated Rust client with the given defaults.
959
+ * Generate the next request ID as 16 lowercase hex bytes (zero-alloc).
1431
960
  *
1432
- * `mimeCache` / `hmacCache` are per-instance. `rayonThreads` only applies
1433
- * before the process-wide rayon pool is initialized (once per process — see
1434
- * {@link RustOptions.rayonThreads}); the pool defaults to the env var or
1435
- * `max(1, hardwareConcurrency - 1)` otherwise.
1436
- */
1437
- export declare function createRust(options?: RustOptions): RustClient;
1438
- /**
1439
- * The default, ready-to-use `RustClient` created with no options (resolves
1440
- * rayon threads from the host automatically). Most consumers just do
1441
- * `import { rust } from "castrum"` and call `rust.<fn>(...)` directly.
961
+ * @returns A shared buffer aliased across calls — decode/copy before the next
962
+ * `generateRequestId()` invocation.
1442
963
  */
1443
- export declare const rust: RustClient;
1444
- /** The benchmark-proven default implementation for an op. */
1445
- export type ProvenImpl = "native" | "js" | "bun";
1446
- /** How decisive the baked winner is. */
1447
- export type ProvenStatus = "proven" | "parity" | "unmeasured";
1448
- /** One entry in the baked proven-selection registry. */
1449
- export interface ProvenEntry {
1450
- /** Selection op name (matches `opImpl(op)` / `src/selection.json` keys). */
1451
- name: string;
1452
- /** The baked default implementation: the benchmark-proven winner. */
1453
- impl: ProvenImpl;
1454
- /** `proven` = decisive win, `parity` = borderline, `unmeasured` = pinned by policy. */
1455
- status: ProvenStatus;
1456
- /** Measured ratio behind the decision (see module doc for the axis). */
1457
- typicalRatio?: number;
1458
- /** Why this classification. */
1459
- note?: string;
964
+ export declare function generateRequestId(): Uint8Array;
965
+ /** A parsed W3C trace context (valid per the spec's length/hex rules). */
966
+ export interface TraceContext {
967
+ /** Version, always `"00"` for accepted headers. */
968
+ version: string;
969
+ /** 32 hex chars — the trace id (shared across the whole trace). */
970
+ traceId: string;
971
+ /** 16 hex chars — the current span id. */
972
+ spanId: string;
973
+ /** The flags byte (bit 0 = sampled). */
974
+ flags: number;
975
+ /** True when the `sampled` (bit 0) flag is set. */
976
+ sampled: boolean;
1460
977
  }
1461
978
  /**
1462
- * Baked proven-winner registry. Covering the FULL selection surface
1463
- * (`src/selection.json` ops + every Bun built-in delegation) so nothing can be
1464
- * silently dropped. Classifications are based on release/perf-build results;
1465
- * `test/unit/shared/proven.test.ts` re-checks them against the LIVE wiring.
1466
- */
1467
- export declare const PROVEN_SELECTION: readonly ProvenEntry[];
1468
- /**
1469
- * The baked {@link ProvenEntry} for `op`, or `undefined` for unknown ops.
1470
- * Pure data — never dlopens the addon.
1471
- */
1472
- export declare function provenEntry(op: string): ProvenEntry | undefined;
1473
- /** The baked default implementation for `op` (`native` / `js` / `bun`), or `null`. */
1474
- export declare function provenImpl(op: string): ProvenImpl | null;
1475
- /** The baked status for `op`, or `null` for unknown ops. */
1476
- export declare function provenStatus(op: string): ProvenStatus | null;
1477
- /** Whether the op's baked winner is a decisive (`proven`) win. */
1478
- export declare function isProven(op: string): boolean;
1479
- /** The full baked registry (same array as {@link PROVEN_SELECTION}). */
1480
- export declare function provenSurface(): readonly ProvenEntry[];
1481
- /** A one-line summary of the baked selection (counts per implementation). */
1482
- export declare function provenSummary(): string;
1483
- /** The full `rust.*` surface — identical to `rust`; the proven wiring is baked (see {@link ../shared/proven}). */
1484
- export type ProvenClient = RustClient;
1485
- /** The full `rust.*` surface, exposed for audit. Same object as `rust`. */
1486
- export declare const proven: ProvenClient;
1487
- /** Recommended implementation for an operation. */
1488
- export type OpImpl = "native" | "js";
1489
- /** A selection decision: recommended implementation + rationale note. */
1490
- export interface OpDecision {
1491
- /** The recommended implementation when the addon is available. */
1492
- readonly impl: OpImpl;
1493
- /** Why this decision (measured ratio / rationale). */
1494
- readonly note?: string;
1495
- }
1496
- /**
1497
- * The benchmark-driven native-vs-JS decision for `op` — runtime-aware.
1498
- *
1499
- * Returns `"native"` when the Rust addon is the recommended implementation,
1500
- * `"js"` when a pure-JS (or Bun built-in) implementation wins, or `null` for
1501
- * unknown ops (or when the addon is unavailable — with no addon there is no
1502
- * "native" choice). Under Bun, ops in the adapter's builtins registry resolve
1503
- * to `"js"` so the consumer's JS path delegates to the faster Bun built-in.
1504
- *
1505
- * The default is the BAKED proven selection (`src/shared/proven.ts`):
1506
- * `native` / `js` directly, `bun` resolves to `"js"` via the builtins check
1507
- * above. Unknown ops (and `bun` entries on a non-Bun runtime, where no
1508
- * built-in exists) fall back to the addon's embedded decision
1509
- * (`rust/selection.rs` ← `src/selection.json`).
1510
- */
1511
- export declare const opImpl: (op: string) => OpImpl | null;
1512
- /** `true` when the Rust addon is the recommended impl for `op`. */
1513
- export declare const isNativeOp: (op: string) => boolean;
1514
- /** The decision record for `op`, or `null` when unknown. */
1515
- export declare const opDecision: (op: string) => OpDecision | null;
1516
- /**
1517
- * Shared UTF-8 encoder (encode / encodeInto), backed by the runtime-native
1518
- * codec (`src/shared/codec.ts`): on Bun the work is done by Bun's native
1519
- * transfer machinery (`Bun.ArrayBufferSink` / a zero-copy `Buffer` view
1520
- * write); under Node it falls back to `TextEncoder`. Reusing a single object
1521
- * avoids allocation overhead in hot paths.
1522
- *
1523
- * `encode` accepts the string-or-bytes union the text-returning `rust.*` ops
1524
- * surface (Bun returns strings, Node returns bytes) and normalizes to bytes —
1525
- * a string is UTF-8 encoded, bytes pass through unchanged.
1526
- */
1527
- export declare const encoder: {
1528
- encode(s: Uint8Array | string): Uint8Array;
1529
- encodeInto(s: string, dest: Uint8Array): {
1530
- read: number;
1531
- written: number;
1532
- };
1533
- };
1534
- /**
1535
- * Shared UTF-8 decoder, backed by the runtime-native codec
1536
- * (`src/shared/codec.ts`): on Bun it uses `new CString(ptr, 0, len)` from
1537
- * `bun:ffi` (JSC-native, exact-length clone); under Node it falls back to
1538
- * `TextDecoder`. Reusing a single object avoids allocation overhead in hot
1539
- * paths.
1540
- *
1541
- * `decode` accepts the string-or-bytes union the text-returning `rust.*` ops
1542
- * surface and normalizes to a string — bytes are UTF-8 decoded, strings pass
1543
- * through unchanged.
1544
- */
1545
- export declare const decoder: {
1546
- decode(bytes: Uint8Array | string): string;
1547
- };
1548
- /** Options for an `AdaptiveEstimate`: EWMA smoothing, bounds, starting value. */ export interface AdaptiveEstimateOptions {
1549
- /**
1550
- * EWMA smoothing factor in (0, 1]. Higher reacts faster to new samples;
1551
- * lower smooths more. Default: 0.25.
1552
- */
1553
- alpha?: number;
1554
- /** Starting estimate. Default 0 (the first sample wins). */
1555
- initial?: number;
1556
- /** Clamp the estimate to at least this. Default 0. */
1557
- min?: number;
1558
- /** Clamp the estimate to at most this (bounds memory / resources). Default Infinity. */
1559
- max?: number;
1560
- }
1561
- /**
1562
- * A bounded exponential-moving-average estimate of an observed quantity.
1563
- *
1564
- * `sample(x)` folds a new observation into the running estimate; `estimate`
1565
- * reads the current value. The estimate is clamped to `[min, max]`, so it is
1566
- * safe to drive resource sizing (buffer pools, batch thresholds) with it —
1567
- * a single outlier cannot blow up the estimate, and it decays toward recent
1568
- * observations.
1569
- */
1570
- export declare class AdaptiveEstimate {
1571
- private readonly alpha;
1572
- private readonly min;
1573
- private readonly max;
1574
- private readonly initial;
1575
- private value;
1576
- constructor(options?: AdaptiveEstimateOptions);
1577
- /** Fold a new observation into the running estimate. */
1578
- sample(x: number): void;
1579
- /** Current estimate (clamped to `[min, max]`). */
1580
- get estimate(): number;
1581
- /** Reset the estimate (back to `initial`). */
1582
- reset(): void;
1583
- }
1584
- /**
1585
- * Generate a UUIDv7 (time-ordered) string, preferring Bun's native built-in.
1586
- * Under Node this falls back to `crypto.randomUUID` (v4).
1587
- *
1588
- * @returns A UUID string.
1589
- */
1590
- export declare const uuidv7: () => string;
1591
- /** Default histogram buckets (Prometheus-style, seconds). */
1592
- export declare const DEFAULT_BUCKETS: readonly number[];
1593
- export type Labels = Readonly<Record<string, string>>;
1594
- /** A single labelled counter value. */
1595
- export interface Counter {
1596
- /** Increment by 1 (or `by`). `labels` must match the label names exactly. */
1597
- inc(labels?: Labels, by?: number): void;
1598
- }
1599
- /** A single labelled gauge value (can go up and down). */
1600
- export interface Gauge {
1601
- inc(labels?: Labels, by?: number): void;
1602
- dec(labels?: Labels, by?: number): void;
1603
- set(labels: Labels | undefined, value: number): void;
1604
- }
1605
- /** A single labelled histogram. */
1606
- export interface Histogram {
1607
- /** Record one observation. `labels` must match the label names exactly. */
1608
- observe(value: number, labels?: Labels): void;
1609
- }
1610
- /** The metrics registry factory. */
1611
- export interface MetricsRegistry {
1612
- /** Create (or fetch) a counter. `help` is only used on first creation. */
1613
- counter(name: string, help: string, labelNames?: readonly string[]): Counter;
1614
- /** Create (or fetch) a gauge. */
1615
- gauge(name: string, help: string, labelNames?: readonly string[]): Gauge;
1616
- /**
1617
- * Create (or fetch) a histogram. `buckets`/`help` only apply on first
1618
- * creation; later calls with the same name return the existing histogram.
1619
- */
1620
- histogram(name: string, help: string, buckets?: readonly number[], labelNames?: readonly string[]): Histogram;
1621
- /** Render the Prometheus text exposition format. */
1622
- render(): string;
1623
- /** Reset all metrics (for tests / interval rotation). */
1624
- reset(): void;
1625
- }
1626
- /** Create a fresh metrics registry. */
1627
- export declare function createMetrics(): MetricsRegistry;
1628
- /**
1629
- * Create the runtime-appropriate ingress server: `Bun.serve` on Bun, the
1630
- * `node:http` adapter on Node. Selected ONCE at module load — the branch
1631
- * never runs in the hot path.
1632
- */
1633
- export declare function createIngressServer(options: CreateIngressServerOptions): BakedServer;
1634
- /**
1635
- * Generate the next request ID as 16 lowercase hex bytes (zero-alloc).
1636
- *
1637
- * @returns A shared buffer aliased across calls — decode/copy before the next
1638
- * `generateRequestId()` invocation.
1639
- */
1640
- export declare function generateRequestId(): Uint8Array;
1641
- /** A parsed W3C trace context (valid per the spec's length/hex rules). */
1642
- export interface TraceContext {
1643
- /** Version, always `"00"` for accepted headers. */
1644
- version: string;
1645
- /** 32 hex chars — the trace id (shared across the whole trace). */
1646
- traceId: string;
1647
- /** 16 hex chars — the current span id. */
1648
- spanId: string;
1649
- /** The flags byte (bit 0 = sampled). */
1650
- flags: number;
1651
- /** True when the `sampled` (bit 0) flag is set. */
1652
- sampled: boolean;
1653
- }
1654
- /**
1655
- * Parse a `traceparent` header value into a {@link TraceContext}, or `null`
1656
- * when malformed (wrong version / length / non-hex). Invalid contexts are
1657
- * ignored per the spec — callers should generate a fresh trace then.
979
+ * Parse a `traceparent` header value into a {@link TraceContext}, or `null`
980
+ * when malformed (wrong version / length / non-hex). Invalid contexts are
981
+ * ignored per the spec — callers should generate a fresh trace then.
1658
982
  */
1659
983
  export declare function parseTraceParent(header: string | null | undefined): TraceContext | null;
1660
984
  /** Random 32-hex trace id (crypto-backed where available). */
@@ -2056,6 +1380,43 @@ export declare function readinessHandler(check?: () => boolean | Promise<boolean
2056
1380
  * (readiness semantics), otherwise always 200.
2057
1381
  */
2058
1382
  export declare function healthHandler(check?: () => boolean | Promise<boolean>): ((_req: Request) => Response) | ((_req: Request) => Promise<Response>);
1383
+ /** Default histogram buckets (Prometheus-style, seconds). */
1384
+ export declare const DEFAULT_BUCKETS: readonly number[];
1385
+ export type Labels = Readonly<Record<string, string>>;
1386
+ /** A single labelled counter value. */
1387
+ export interface Counter {
1388
+ /** Increment by 1 (or `by`). `labels` must match the label names exactly. */
1389
+ inc(labels?: Labels, by?: number): void;
1390
+ }
1391
+ /** A single labelled gauge value (can go up and down). */
1392
+ export interface Gauge {
1393
+ inc(labels?: Labels, by?: number): void;
1394
+ dec(labels?: Labels, by?: number): void;
1395
+ set(labels: Labels | undefined, value: number): void;
1396
+ }
1397
+ /** A single labelled histogram. */
1398
+ export interface Histogram {
1399
+ /** Record one observation. `labels` must match the label names exactly. */
1400
+ observe(value: number, labels?: Labels): void;
1401
+ }
1402
+ /** The metrics registry factory. */
1403
+ export interface MetricsRegistry {
1404
+ /** Create (or fetch) a counter. `help` is only used on first creation. */
1405
+ counter(name: string, help: string, labelNames?: readonly string[]): Counter;
1406
+ /** Create (or fetch) a gauge. */
1407
+ gauge(name: string, help: string, labelNames?: readonly string[]): Gauge;
1408
+ /**
1409
+ * Create (or fetch) a histogram. `buckets`/`help` only apply on first
1410
+ * creation; later calls with the same name return the existing histogram.
1411
+ */
1412
+ histogram(name: string, help: string, buckets?: readonly number[], labelNames?: readonly string[]): Histogram;
1413
+ /** Render the Prometheus text exposition format. */
1414
+ render(): string;
1415
+ /** Reset all metrics (for tests / interval rotation). */
1416
+ reset(): void;
1417
+ }
1418
+ /** Create a fresh metrics registry. */
1419
+ export declare function createMetrics(): MetricsRegistry;
2059
1420
  /** The runtime hooks + registry for ingress metrics. */
2060
1421
  export interface IngressMetrics {
2061
1422
  registry: MetricsRegistry;
@@ -2225,116 +1586,76 @@ export declare function createIngressSync(options?: IngressOptions): SyncIngress
2225
1586
  * @returns an async handler returning a snapshot context
2226
1587
  */
2227
1588
  export declare function createIngress(options?: IngressOptions): IngressHandler;
2228
- /** Readonly snapshot of the ingress decision, safe after `run()` returns. */
2229
- export interface PipelineResult {
2230
- readonly ok: boolean;
2231
- readonly status: number;
2232
- readonly errorCode: number;
2233
- readonly terminal: boolean;
2234
- readonly rateLimited: boolean;
2235
- readonly bodyValidJson: boolean;
2236
- readonly schemaValid: boolean;
2237
- readonly bodyTruncated: boolean;
2238
- readonly requestId: string;
2239
- /** The raw request body the pipeline saw (read-only; aliases the request body). */
2240
- readonly body: Uint8Array;
2241
- /** The ingress metadata JSON body (when `emitMetadataJson` is on), else empty. */
2242
- readonly metadataJson: Uint8Array;
2243
- }
2244
- /** Per-request context threaded into the app's handler. */
2245
- export interface PipelineContext {
2246
- readonly requestId: string;
2247
- readonly ip: string | undefined;
2248
- /** Mutable per-request storage for middleware/user state. */
2249
- readonly locals: Map<string, unknown>;
2250
- }
2251
- /** Result of running `preprocess`: terminal short-circuit or pass-through. */
2252
- export interface PreprocessOutcome {
2253
- /** True when the pipeline short-circuited (error/denied) — serve `response`. */
2254
- readonly terminal: boolean;
2255
- readonly response: Response | null;
2256
- readonly result: PipelineResult | null;
2257
- readonly ctx: PipelineContext;
2258
- }
2259
- /** Options for `createPipeline`. */
2260
- export interface CreatePipelineOptions {
2261
- /** Ingress options (same surface as `createIngressHandler`). */
2262
- options?: IngressHandlerOptions;
2263
- /** Runtime hooks: onRequest/onResponse/onError, logging, pool sizing. */
2264
- runtime?: BakedIngressRuntime;
2265
- /** Body limit for `readBody`/`preprocess`. Default: 1 MiB. */
2266
- maxBodyBytes?: number;
2267
- /** Body-read deadline (ms) for `readBody`/`preprocess`. Default: 30s. */
2268
- bodyTimeoutMs?: number;
2269
- /**
2270
- * When true (default), `preprocess` reads a request body (guarded) so the
2271
- * result carries it. Set false when the app handler owns the body.
2272
- */
2273
- readBody?: boolean;
2274
- /**
2275
- * Optional renderer for the OK (non-terminal) case of `handleRequest`.
2276
- * Defaults to a minimal `{"ok":true,"requestId":...}` JSON body.
2277
- */
2278
- render?: (result: PipelineResult, ctx: PipelineContext) => Response | Promise<Response>;
2279
- }
2280
- /** The composed ingress pipeline: preprocess / handleRequest / readBody. */
2281
- export interface IngressPipeline {
2282
- /** The underlying pre-baked handler (custom route factories / responses). */
2283
- readonly ingress: OptimizedIngressHandler;
2284
- /** Run the pipeline for a middleware/framework. */
2285
- preprocess(req: Request, ip?: string): Promise<PreprocessOutcome>;
2286
- /** Fetch-compatible handler: terminal response or the renderer's response. */
2287
- handleRequest(req: Request, ip?: string): Promise<Response>;
2288
- /** Stream-read a request body with the configured limits. */
2289
- readBody(req: Request, maxBytes?: number, timeoutMs?: number): Promise<Uint8Array>;
2290
- }
1589
+ /** Parse a query string (`a=1&b=2`) into an object via the native parser. */
1590
+ export declare function parseQueryString(query: string): Record<string, string | string[]>;
1591
+ /** Parse a cookie header (`a=1; b=2`) into an object via the native parser. */
1592
+ export declare function parseCookieHeader(header: string): Record<string, string | string[]>;
1593
+ /** Parse an application/x-www-form-urlencoded body into a key/value object. */
1594
+ export declare function parseFormBody(body: Uint8Array): Record<string, string | string[]>;
1595
+ /** Native JSON-Schema validator handle (see `createSchemaValidator`). */
1596
+ export type SchemaValidator = SchemaValidatorInstance;
2291
1597
  /**
2292
- * Create a framework-agnostic ingress pipeline.
2293
- *
2294
- * @example
2295
- * ```ts
2296
- * // Plain Bun.serve fetch handler
2297
- * const pipeline = createPipeline({ options: { parseCookies: true } });
2298
- * Bun.serve({ port: 3000, fetch: (req) => pipeline.handleRequest(req) });
2299
- *
2300
- * // Hono-style middleware: short-circuit on terminal, else handle the app route
2301
- * app.use("*", async (c, next) => {
2302
- * const { terminal, response, ctx } = await pipeline.preprocess(c.req.raw);
2303
- * if (terminal && response) return response;
2304
- * c.set("ingress", ctx); // pass requestId / locals into the app handler
2305
- * await next();
2306
- * });
2307
- * ```
1598
+ * Unpack a packed u32 array: `[u32 count]{[u32 value]}` → `Uint32Array`.
1599
+ * @throws {RangeError} when the buffer is shorter than `count` × 4 bytes.
2308
1600
  */
2309
- export declare function createPipeline(opts?: CreatePipelineOptions): IngressPipeline;
2310
- /** One SSE event to frame via `sseResponse` (event/id/retry/data). */ export interface SseEvent {
2311
- /** Optional `event:` field. */
2312
- event?: string | null;
2313
- /** Optional `id:` field. */
2314
- id?: string | null;
2315
- /** Optional `retry:` field (ms). */
2316
- retry?: number | null;
2317
- /** Event data. */
2318
- data: string | Uint8Array;
1601
+ export declare function unpackU32Array(bytes: Uint8Array): Uint32Array;
1602
+ /**
1603
+ * Unpack a packed bitset: `[u32 count]{bit per item}` → one 0/1 byte per item.
1604
+ * @throws {RangeError} when the buffer is shorter than `count` bits.
1605
+ */
1606
+ export declare function unpackBitset(bytes: Uint8Array): Uint8Array;
1607
+ /**
1608
+ * Unpack a packed i64 array as SIGNED `BigInt64Array`.
1609
+ * @throws {RangeError} when the buffer is shorter than `count` × 8 bytes.
1610
+ */
1611
+ export declare function unpackI64ArrayAsBigInt(bytes: Uint8Array): BigInt64Array;
1612
+ /**
1613
+ * Unpack a packed byte-results buffer: `[u32 count]{[u32 len][bytes]}` → one
1614
+ * `Uint8Array` slice per item. The slices reference `bytes`'s backing store
1615
+ * (zero-copy) — copy them if held beyond the buffer's lifetime.
1616
+ */
1617
+ export declare function unpackByteResults(bytes: Uint8Array): Uint8Array[];
1618
+ /** A decoded `[name, value]` pair from a packed buffer. */
1619
+ export type Pair = [
1620
+ string,
1621
+ string
1622
+ ];
1623
+ /**
1624
+ * Decode a packed `[u32 count][u32 keyLen][key][u32 valLen][val]...` buffer
1625
+ * into `[name, value]` string pairs (see `packPairs` for the layout).
1626
+ */
1627
+ export declare function readPairsPacked(bytes: Uint8Array): Pair[];
1628
+ /**
1629
+ * Fold `[name, value]` pairs into an object; repeated names collect into a
1630
+ * string array (query-string/cookie semantics).
1631
+ */
1632
+ export declare function pairsToObject(pairs: Pair[]): Record<string, string | string[]>;
1633
+ /** A decoded HTTP request from the native packed parser. */
1634
+ export interface ParsedHttpRequestPacked {
1635
+ method: string;
1636
+ path: string;
1637
+ version: string;
1638
+ headers: Record<string, string>;
2319
1639
  }
2320
1640
  /**
2321
- * Build a `text/event-stream` Response from an (async) iterable of events.
2322
- *
2323
- * Each event is framed with the native `rust.sseEncodeEvent` (zero-DOM, byte
2324
- * parity with the FFI primitive), so SSE stays on the fast path:
2325
- *
2326
- * @example
2327
- * ```ts
2328
- * async function* tick() {
2329
- * for (let i = 0; i < 5; i++) {
2330
- * await Bun.sleep(250);
2331
- * yield { event: "tick", data: String(i) };
2332
- * }
2333
- * }
2334
- * return sseResponse(tick(), { headers: { "x-request-id": rid } });
2335
- * ```
1641
+ * Decode a native `httpParseRequestPacked` buffer into a plain request object
1642
+ * (zero-alloc on the Rust side; this builds the JS strings once).
2336
1643
  */
2337
- export declare function sseResponse(events: AsyncIterable<SseEvent> | Iterable<SseEvent>, init?: ResponseInit): Response;
1644
+ export declare function readHttpPacked(bytes: Uint8Array): ParsedHttpRequestPacked;
1645
+ /**
1646
+ * Build a packed batch buffer. Returns an OWNED buffer (safe to hold across
1647
+ * later calls) — this is the public entry point. Hot internal paths that can
1648
+ * guarantee synchronous consumption should use `withPackScratch` instead.
1649
+ */
1650
+ export declare function packBatch(items: Uint8Array[]): Uint8Array;
1651
+ /**
1652
+ * Pack `[name, value]` string pairs into the native `[u32 count]...` layout
1653
+ * that `readPairsPacked` / the batch validators consume.
1654
+ */
1655
+ export declare function packPairs(pairs: Array<[
1656
+ string,
1657
+ string
1658
+ ]>): Uint8Array;
2338
1659
  /** Result kinds supported by the loader (mirrors the packed batch wire shapes). */
2339
1660
  export type LoaderResultKind = "boolean" | "number" | "bigint" | "bytes";
2340
1661
  /** Single-item (scalar) result type for a kind. */
@@ -2860,134 +2181,866 @@ export interface LoaderOptions {
2860
2181
  /** Sample timing every N dispatches (hot-path cost control). Default 32. */
2861
2182
  sampleEvery?: number;
2862
2183
  /**
2863
- * `load()` dispatch strategy. `"auto"` (default) switches each op between
2864
- * `"single"` (low load → direct scalar call, no coalescer) and
2865
- * `"coalesce"` (rising load → one packed batch call) from observed load.
2184
+ * `load()` dispatch strategy. `"auto"` (default) switches each op between
2185
+ * `"single"` (low load → direct scalar call, no coalescer) and
2186
+ * `"coalesce"` (rising load → one packed batch call) from observed load.
2187
+ */
2188
+ loadStrategy?: LoadStrategy;
2189
+ }
2190
+ /** Options for `load()` / `opFn.load()`. */
2191
+ export interface LoadOptions {
2192
+ /**
2193
+ * Explicit cache key. When omitted (and caching is on) the default key is
2194
+ * `fnv1a64(input)` — which costs one extra native call per load.
2195
+ */
2196
+ key?: string;
2197
+ /** Override the loader-wide caching toggle for this call. Default true. */
2198
+ cache?: boolean;
2199
+ }
2200
+ /** Per-op counters exposed on `opFn.stats`. */
2201
+ export interface LoaderOpStats {
2202
+ scalarCalls: number;
2203
+ batchCalls: number;
2204
+ itemsDispatched: number;
2205
+ cachedHits: number;
2206
+ /** Current learned batch threshold for this op. */
2207
+ batchMin: number;
2208
+ /** Current `load()` strategy: "single" (low load) or "coalesce" (bulk). */
2209
+ mode: "single" | "coalesce";
2210
+ }
2211
+ /** Aggregate loader statistics. */
2212
+ export interface LoaderStats {
2213
+ scalarCalls: number;
2214
+ batchCalls: number;
2215
+ itemsDispatched: number;
2216
+ flushes: number;
2217
+ cachedHits: number;
2218
+ cacheSize: number;
2219
+ cacheEvictions: number;
2220
+ }
2221
+ /** A specialized, pre-bound op function returned by `loader(op)`. */
2222
+ export interface LoaderOpFn<K extends LoaderOpName> {
2223
+ /** Single item → scalar result. */
2224
+ (input: Uint8Array, ...rest: LoaderOpArgs[K]): LoaderScalar<K>;
2225
+ /** Bulk items → ONE packed batch call; same shape as `rust.batch.<op>`. */
2226
+ (inputs: Uint8Array[], ...rest: LoaderBulkArgs<K>): LoaderBulk<K>;
2227
+ readonly name: K;
2228
+ readonly stats: LoaderOpStats;
2229
+ clear(): void;
2230
+ /** Sync cache peek (default key `fnv1a64(input)` unless `key` given). */
2231
+ cache(input: Uint8Array, key?: string): LoaderScalar<K> | undefined;
2232
+ /**
2233
+ * Coalesced, cache-aware load. Unavailable (`undefined`) on ops that require
2234
+ * extra arguments (e.g. `hmacSha256`, `signCookie`) — use `run` for those.
2235
+ */
2236
+ load: K extends LoadableName ? LoaderLoadFn<K> : undefined;
2237
+ }
2238
+ export type LoaderLoadFn<K extends LoaderOpName> = (input: Uint8Array, opts?: LoadOptions) => Promise<LoaderScalar<K>>;
2239
+ /** Ops whose extra args are all optional/empty → they support `load()`. */
2240
+ export type LoadableName = {
2241
+ [K in LoaderOpName]: LoaderOpArgs[K] extends [
2242
+ ] | [
2243
+ unknown?
2244
+ ] | [
2245
+ unknown?,
2246
+ unknown?
2247
+ ] | [
2248
+ unknown?,
2249
+ unknown?,
2250
+ unknown?
2251
+ ] ? K : never;
2252
+ }[LoaderOpName];
2253
+ /** A validator-bound helper returned by `loader.schema(validator)`. */
2254
+ export interface LoaderSchemaFn {
2255
+ /** Single JSON document → valid? */
2256
+ (input: Uint8Array): boolean;
2257
+ /** Bulk JSON documents → bitset (1 per doc). */
2258
+ (inputs: Uint8Array[]): Uint8Array;
2259
+ /** Count how many bulk documents are valid. */
2260
+ count(inputs: Uint8Array[]): number;
2261
+ }
2262
+ /** The global callable loader. */
2263
+ interface Loader$1 {
2264
+ /** Higher-order function: `loader(op)` → specialized hot op function. */
2265
+ <K extends LoaderOpName>(op: K): LoaderOpFn<K>;
2266
+ /** Single-item dispatch. */
2267
+ run<K extends LoaderOpName>(op: K, input: Uint8Array, ...rest: LoaderOpArgs[K]): LoaderScalar<K>;
2268
+ /** Bulk dispatch (one packed batch call, or adaptive scalar loop). */
2269
+ run<K extends LoaderOpName>(op: K, inputs: Uint8Array[], ...rest: LoaderBulkArgs<K>): LoaderBulk<K>;
2270
+ /** Coalesced, cache-aware load (ops with required extra args throw). */
2271
+ load<K extends LoadableName>(op: K, input: Uint8Array, opts?: LoadOptions): Promise<LoaderScalar<K>>;
2272
+ /** Sync cache peek. */
2273
+ cache<K extends LoaderOpName>(op: K, input: Uint8Array, key?: string): LoaderScalar<K> | undefined;
2274
+ /**
2275
+ * Bind a `SchemaValidator` for repeated single/bulk JSON-schema validation
2276
+ * plus a whole-batch `count`. The returned callable uses the loader's normal
2277
+ * dispatch machinery (cost model + counters).
2278
+ */
2279
+ schema(validator: SchemaValidator): LoaderSchemaFn;
2280
+ /** Clear the shared cache (pending coalesced loads still flush). */
2281
+ clear(): void;
2282
+ /** Update options at runtime. */
2283
+ configure(options: LoaderOptions): void;
2284
+ readonly stats: LoaderStats;
2285
+ readonly opNames: LoaderOpName[];
2286
+ }
2287
+ /** Load-dispatch strategy selector for `load()`. */
2288
+ export type LoadStrategy = "auto" | "single" | "coalesce";
2289
+ /** Create an isolated loader with the given defaults. */
2290
+ export declare function createLoader(options?: LoaderOptions): Loader$1;
2291
+ /**
2292
+ * The global, ready-to-use loader. Most consumers just do
2293
+ * `import { loader } from "castrum"` and either `loader("validateEmail")(…)`
2294
+ * (higher-order function) or `loader.run("validateEmail", …)`.
2295
+ */
2296
+ export declare const loader: Loader$1;
2297
+ /**
2298
+ * Batch-validate JSON documents against a schema through the loader's bulk
2299
+ * `schemaValidate` path. Returns a bitset — byte `i` is 1 when document `i`
2300
+ * is valid (same shape as `rust.batch.schemaValidate`).
2301
+ */
2302
+ export declare function validateMany(validator: SchemaValidator, docs: Uint8Array[]): Uint8Array;
2303
+ /** Count how many of `docs` validate against `validator` (one packed call). */
2304
+ export declare function validateCount(validator: SchemaValidator, docs: Uint8Array[]): number;
2305
+ /**
2306
+ * Generic loader bulk dispatch: `loader.run(op, items, ...rest)` → one packed
2307
+ * batch native call (or the adaptive scalar loop for tiny batches).
2308
+ */
2309
+ export declare function runMany<K extends LoaderOpName>(op: K, items: Uint8Array[], ...rest: LoaderBulkArgs<K>): LoaderBulk<K>;
2310
+ /** Single-item loader dispatch — pairs with `runMany` for mixed workloads. */
2311
+ export declare function runOne<K extends LoaderOpName>(op: K, item: Uint8Array, ...rest: LoaderOpArgs[K]): LoaderScalar<K>;
2312
+ /** Readonly snapshot of the ingress decision, safe after `run()` returns. */
2313
+ export interface PipelineResult {
2314
+ readonly ok: boolean;
2315
+ readonly status: number;
2316
+ readonly errorCode: number;
2317
+ readonly terminal: boolean;
2318
+ readonly rateLimited: boolean;
2319
+ readonly bodyValidJson: boolean;
2320
+ readonly schemaValid: boolean;
2321
+ readonly bodyTruncated: boolean;
2322
+ readonly requestId: string;
2323
+ /** The raw request body the pipeline saw (read-only; aliases the request body). */
2324
+ readonly body: Uint8Array;
2325
+ /** The ingress metadata JSON body (when `emitMetadataJson` is on), else empty. */
2326
+ readonly metadataJson: Uint8Array;
2327
+ }
2328
+ /** Per-request context threaded into the app's handler. */
2329
+ export interface PipelineContext {
2330
+ readonly requestId: string;
2331
+ readonly ip: string | undefined;
2332
+ /** Mutable per-request storage for middleware/user state. */
2333
+ readonly locals: Map<string, unknown>;
2334
+ }
2335
+ /** Result of running `preprocess`: terminal short-circuit or pass-through. */
2336
+ export interface PreprocessOutcome {
2337
+ /** True when the pipeline short-circuited (error/denied) — serve `response`. */
2338
+ readonly terminal: boolean;
2339
+ readonly response: Response | null;
2340
+ readonly result: PipelineResult | null;
2341
+ readonly ctx: PipelineContext;
2342
+ }
2343
+ /** Options for `createPipeline`. */
2344
+ export interface CreatePipelineOptions {
2345
+ /** Ingress options (same surface as `createIngressHandler`). */
2346
+ options?: IngressHandlerOptions;
2347
+ /** Runtime hooks: onRequest/onResponse/onError, logging, pool sizing. */
2348
+ runtime?: BakedIngressRuntime;
2349
+ /** Body limit for `readBody`/`preprocess`. Default: 1 MiB. */
2350
+ maxBodyBytes?: number;
2351
+ /** Body-read deadline (ms) for `readBody`/`preprocess`. Default: 30s. */
2352
+ bodyTimeoutMs?: number;
2353
+ /**
2354
+ * When true (default), `preprocess` reads a request body (guarded) so the
2355
+ * result carries it. Set false when the app handler owns the body.
2356
+ */
2357
+ readBody?: boolean;
2358
+ /**
2359
+ * Optional renderer for the OK (non-terminal) case of `handleRequest`.
2360
+ * Defaults to a minimal `{"ok":true,"requestId":...}` JSON body.
2361
+ */
2362
+ render?: (result: PipelineResult, ctx: PipelineContext) => Response | Promise<Response>;
2363
+ }
2364
+ /** The composed ingress pipeline: preprocess / handleRequest / readBody. */
2365
+ export interface IngressPipeline {
2366
+ /** The underlying pre-baked handler (custom route factories / responses). */
2367
+ readonly ingress: OptimizedIngressHandler;
2368
+ /** Run the pipeline for a middleware/framework. */
2369
+ preprocess(req: Request, ip?: string): Promise<PreprocessOutcome>;
2370
+ /** Fetch-compatible handler: terminal response or the renderer's response. */
2371
+ handleRequest(req: Request, ip?: string): Promise<Response>;
2372
+ /** Stream-read a request body with the configured limits. */
2373
+ readBody(req: Request, maxBytes?: number, timeoutMs?: number): Promise<Uint8Array>;
2374
+ }
2375
+ /**
2376
+ * Create a framework-agnostic ingress pipeline.
2377
+ *
2378
+ * @example
2379
+ * ```ts
2380
+ * // Plain Bun.serve fetch handler
2381
+ * const pipeline = createPipeline({ options: { parseCookies: true } });
2382
+ * Bun.serve({ port: 3000, fetch: (req) => pipeline.handleRequest(req) });
2383
+ *
2384
+ * // Hono-style middleware: short-circuit on terminal, else handle the app route
2385
+ * app.use("*", async (c, next) => {
2386
+ * const { terminal, response, ctx } = await pipeline.preprocess(c.req.raw);
2387
+ * if (terminal && response) return response;
2388
+ * c.set("ingress", ctx); // pass requestId / locals into the app handler
2389
+ * await next();
2390
+ * });
2391
+ * ```
2392
+ */
2393
+ export declare function createPipeline(opts?: CreatePipelineOptions): IngressPipeline;
2394
+ /** One SSE event to frame via `sseResponse` (event/id/retry/data). */ export interface SseEvent {
2395
+ /** Optional `event:` field. */
2396
+ event?: string | null;
2397
+ /** Optional `id:` field. */
2398
+ id?: string | null;
2399
+ /** Optional `retry:` field (ms). */
2400
+ retry?: number | null;
2401
+ /** Event data. */
2402
+ data: string | Uint8Array;
2403
+ }
2404
+ /**
2405
+ * Build a `text/event-stream` Response from an (async) iterable of events.
2406
+ *
2407
+ * Each event is framed with the native `rust.sseEncodeEvent` (zero-DOM, byte
2408
+ * parity with the FFI primitive), so SSE stays on the fast path:
2409
+ *
2410
+ * @example
2411
+ * ```ts
2412
+ * async function* tick() {
2413
+ * for (let i = 0; i < 5; i++) {
2414
+ * await Bun.sleep(250);
2415
+ * yield { event: "tick", data: String(i) };
2416
+ * }
2417
+ * }
2418
+ * return sseResponse(tick(), { headers: { "x-request-id": rid } });
2419
+ * ```
2420
+ */
2421
+ export declare function sseResponse(events: AsyncIterable<SseEvent> | Iterable<SseEvent>, init?: ResponseInit): Response;
2422
+ /** The benchmark-proven default implementation for an op. */
2423
+ export type ProvenImpl = "native" | "js" | "bun";
2424
+ /** How decisive the baked winner is. */
2425
+ export type ProvenStatus = "proven" | "parity" | "unmeasured";
2426
+ /** One entry in the baked proven-selection registry. */
2427
+ export interface ProvenEntry {
2428
+ /** Selection op name (matches `opImpl(op)` / `src/selection.json` keys). */
2429
+ name: string;
2430
+ /** The baked default implementation: the benchmark-proven winner. */
2431
+ impl: ProvenImpl;
2432
+ /** `proven` = decisive win, `parity` = borderline, `unmeasured` = pinned by policy. */
2433
+ status: ProvenStatus;
2434
+ /** Measured ratio behind the decision (see module doc for the axis). */
2435
+ typicalRatio?: number;
2436
+ /** Why this classification. */
2437
+ note?: string;
2438
+ }
2439
+ /**
2440
+ * Baked proven-winner registry. Covering the FULL selection surface
2441
+ * (`src/selection.json` ops + every Bun built-in delegation) so nothing can be
2442
+ * silently dropped. Classifications are based on release/perf-build results;
2443
+ * `test/unit/shared/proven.test.ts` re-checks them against the LIVE wiring.
2444
+ */
2445
+ export declare const PROVEN_SELECTION: readonly ProvenEntry[];
2446
+ /**
2447
+ * The baked {@link ProvenEntry} for `op`, or `undefined` for unknown ops.
2448
+ * Pure data — never dlopens the addon.
2449
+ */
2450
+ export declare function provenEntry(op: string): ProvenEntry | undefined;
2451
+ /** The baked default implementation for `op` (`native` / `js` / `bun`), or `null`. */
2452
+ export declare function provenImpl(op: string): ProvenImpl | null;
2453
+ /** The baked status for `op`, or `null` for unknown ops. */
2454
+ export declare function provenStatus(op: string): ProvenStatus | null;
2455
+ /** Whether the op's baked winner is a decisive (`proven`) win. */
2456
+ export declare function isProven(op: string): boolean;
2457
+ /** The full baked registry (same array as {@link PROVEN_SELECTION}). */
2458
+ export declare function provenSurface(): readonly ProvenEntry[];
2459
+ /** A one-line summary of the baked selection (counts per implementation). */
2460
+ export declare function provenSummary(): string;
2461
+ /** Options accepted by `createRust` / `rust.configure`. */
2462
+ export interface RustOptions {
2463
+ /**
2464
+ * Rayon thread pool size.
2465
+ *
2466
+ * The rayon pool is process-wide and initialized **once** (native
2467
+ * `OnceLock`) — the first initialization wins. Importing this module
2468
+ * initializes the pool with defaults, so this option only takes effect when
2469
+ * the pool has not yet been created. For reliable control set
2470
+ * `CASTRUM_RAYON_THREADS` / `RUST_RAYON_THREADS` before import.
2471
+ * Default: max(1, hardwareConcurrency - 1).
2472
+ */
2473
+ rayonThreads?: number;
2474
+ /** Cache MIME lookups keyed by normalized extension. Default: true. */
2475
+ mimeCache?: boolean;
2476
+ /** Reuse HMAC signers keyed by key-buffer identity. Default: true. */
2477
+ hmacCache?: boolean;
2478
+ }
2479
+ /**
2480
+ * The `rust.batch.*` array-of-bytes batch surface: N items in → one packed
2481
+ * native call → bitsets / typed arrays / byte arrays out. Separated from the
2482
+ * implementation (`./build.ts`) so type-only consumers don't pull the native
2483
+ * resolver.
2484
+ */
2485
+ export interface RustBatch {
2486
+ jsonValid(items: Uint8Array[]): Uint8Array;
2487
+ crc32(items: Uint8Array[]): Uint32Array;
2488
+ validateEmail(items: Uint8Array[]): Uint8Array;
2489
+ validateUuid(items: Uint8Array[]): Uint8Array;
2490
+ validateIpv4(items: Uint8Array[]): Uint8Array;
2491
+ validateIpv6(items: Uint8Array[]): Uint8Array;
2492
+ jsonSumIds(items: Uint8Array[]): BigInt64Array;
2493
+ queryParse(items: Uint8Array[]): Uint8Array[];
2494
+ cookieParse(items: Uint8Array[]): Uint8Array[];
2495
+ httpParseRequest(items: Uint8Array[]): Uint8Array[];
2496
+ jsonPatch(docs: Uint8Array[], patches: Uint8Array[]): Uint8Array[];
2497
+ hexEncode(items: Uint8Array[]): Uint8Array[];
2498
+ hexDecode(items: Uint8Array[]): Uint8Array[];
2499
+ base64Encode(items: Uint8Array[], urlSafe?: boolean, padding?: boolean): Uint8Array[];
2500
+ base64Decode(items: Uint8Array[], urlSafe?: boolean, padding?: boolean): Uint8Array[];
2501
+ formParse(items: Uint8Array[]): Uint8Array[];
2502
+ signCookie(items: Uint8Array[], secret: Uint8Array): Uint8Array[];
2503
+ verifyCookie(items: Uint8Array[], secret: Uint8Array): Uint8Array;
2504
+ csrfVerify(items: Uint8Array[], secret: Uint8Array): Uint8Array;
2505
+ schemaValidate(validator: SchemaValidator, items: Uint8Array[]): Uint8Array;
2506
+ schemaValidateCount(validator: SchemaValidator, items: Uint8Array[]): number;
2507
+ fnv1a64(items: Uint8Array[]): BigUint64Array;
2508
+ etag(items: Uint8Array[], weak?: boolean | null): Uint8Array[];
2509
+ urlEncode(items: Uint8Array[]): Uint8Array[];
2510
+ urlDecode(items: Uint8Array[]): Uint8Array[];
2511
+ urlDecodeBytes(items: Uint8Array[]): Uint8Array[];
2512
+ mimeFromExtension(items: Uint8Array[]): Uint8Array[];
2513
+ wsAcceptKey(items: Uint8Array[]): Uint8Array[];
2514
+ base64UrlEncode(items: Uint8Array[]): Uint8Array[];
2515
+ base64UrlDecode(items: Uint8Array[]): Uint8Array[];
2516
+ passwordHash(passwords: Uint8Array[], salt: Uint8Array, options?: PasswordHashOptions | null): Uint8Array[];
2517
+ aeadEncrypt(key: Uint8Array, nonce: Uint8Array, items: Uint8Array[], algorithm?: string | null): Uint8Array[];
2518
+ aeadDecrypt(key: Uint8Array, nonce: Uint8Array, items: Uint8Array[], algorithm?: string | null): Uint8Array[];
2519
+ gzipCompress(items: Uint8Array[], level?: number | null): Uint8Array[];
2520
+ gzipDecompress(items: Uint8Array[], maxDecompressed?: number | null): Uint8Array[];
2521
+ brotliCompress(items: Uint8Array[], quality?: number | null): Uint8Array[];
2522
+ brotliDecompress(items: Uint8Array[], maxDecompressed?: number | null): Uint8Array[];
2523
+ jwtVerify(tokens: Uint8Array[], secret: Uint8Array, nowSeconds: number): Uint8Array;
2524
+ hmacSha256(items: Uint8Array[], key: Uint8Array): Uint8Array[];
2525
+ hmacSha256Verify(items: Uint8Array[], sigs: Uint8Array[], key: Uint8Array): Uint8Array;
2526
+ passwordVerify(items: Uint8Array[], phcs: Uint8Array[]): Uint8Array;
2527
+ urlResolve(bases: Uint8Array[], references: Uint8Array[]): Uint8Array[];
2528
+ jwtSign(items: Uint8Array[], secret: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number): Uint8Array[];
2529
+ sseEncode(items: Uint8Array[], event?: string | null, id?: string | null, retry?: number | null): Uint8Array[];
2530
+ wsFrameEncode(items: Uint8Array[], opcode: number, mask: boolean, fin: boolean): Uint8Array[];
2531
+ wsFrameDecode(items: Uint8Array[]): Uint8Array[];
2532
+ multipartParse(items: Uint8Array[], boundary: Uint8Array): MultipartPart[][];
2533
+ templateRender(source: string, contexts: Uint8Array[]): Uint8Array[];
2534
+ }
2535
+ /** Raw packed-wire FFI namespace. */
2536
+ export interface RustPacked {
2537
+ /** Packed CRC32 batch: packed `[u8[]]` items → packed u32 results. */
2538
+ crc32BatchPacked(input: Uint8Array): Uint8Array;
2539
+ /** Packed JSON-validity batch: packed `[u8[]]` items → packed 0/1 bitset. */
2540
+ jsonValidBatchPacked(input: Uint8Array): Uint8Array;
2541
+ /** Packed email-validator batch: packed `[u8[]]` items → packed 0/1 bitset. */
2542
+ validateEmailBatchPacked(input: Uint8Array): Uint8Array;
2543
+ /** Packed UUID-validator batch: packed `[u8[]]` items → packed 0/1 bitset. */
2544
+ validateUuidBatchPacked(input: Uint8Array): Uint8Array;
2545
+ /** Packed IPv4-validator batch: packed `[u8[]]` items → packed 0/1 bitset. */
2546
+ validateIpv4BatchPacked(input: Uint8Array): Uint8Array;
2547
+ /** Packed IPv6-validator batch: packed `[u8[]]` items → packed 0/1 bitset. */
2548
+ validateIpv6BatchPacked(input: Uint8Array): Uint8Array;
2549
+ /** Packed JSON-sum batch: packed `[{id}]` docs → packed `[ok u8][sum i64 LE]`. */
2550
+ jsonSumBatchPacked(input: Uint8Array): Uint8Array;
2551
+ /** Packed query-string parser batch: packed `[u8[]]` queries → packed pair lists. */
2552
+ queryParseBatchPacked(input: Uint8Array): Uint8Array;
2553
+ /** Packed cookie-header parser batch: packed `[u8[]]` headers → packed pair lists. */
2554
+ cookieParseBatchPacked(input: Uint8Array): Uint8Array;
2555
+ /** Packed HTTP-request parser batch: packed `[u8[]]` requests → packed request sections. */
2556
+ httpParseRequestBatchPacked(input: Uint8Array): Uint8Array;
2557
+ /** Batch RFC 6902 JSON Patch (two packed lists, zipped) → packed results. */
2558
+ jsonPatchBatchPacked(docs: Uint8Array, patches: Uint8Array): Uint8Array;
2559
+ /** Packed FNV-1a 64 batch (i64 per item). */
2560
+ fnv1A64BatchPacked(input: Uint8Array): Uint8Array;
2561
+ /** Packed ETag batch (10 strong / 12 weak bytes per item). */
2562
+ etagBatchPacked(input: Uint8Array, weak?: boolean | null): Uint8Array;
2563
+ /** Packed percent-encoding batch: packed `[u8[]]` items → packed hex-percent bytes. */
2564
+ urlEncodeBatchPacked(input: Uint8Array): Uint8Array;
2565
+ /** Packed percent-decoding batch: packed `[u8[]]` items → packed decoded bytes (string-safe). */
2566
+ urlDecodeBatchPacked(input: Uint8Array): Uint8Array;
2567
+ /** Packed percent-decoding batch returning raw bytes (`%FF` preserved). */
2568
+ urlDecodeBytesBatchPacked(input: Uint8Array): Uint8Array;
2569
+ /** Packed MIME-lookup batch: packed `[u8[]]` extensions → packed media-type strings. */
2570
+ mimeFromExtensionBatchPacked(input: Uint8Array): Uint8Array;
2571
+ /** Packed WebSocket accept-key batch: packed `[u8[]]` keys → packed base64 SHA-1 results. */
2572
+ wsAcceptKeyBatchPacked(input: Uint8Array): Uint8Array;
2573
+ /** Valid JSON count in a packed JSON batch (0..n; invalid items not counted). */
2574
+ jsonValidBatchCountPacked(input: Uint8Array): number;
2575
+ /** Valid-email count in a packed email batch. */
2576
+ validateEmailBatchCountPacked(input: Uint8Array): number;
2577
+ /** Valid-UUID count in a packed UUID batch. */
2578
+ validateUuidBatchCountPacked(input: Uint8Array): number;
2579
+ /** Valid-IPv4 count in a packed IPv4 batch. */
2580
+ validateIpv4BatchCountPacked(input: Uint8Array): number;
2581
+ /** Valid-IPv6 count in a packed IPv6 batch. */
2582
+ validateIpv6BatchCountPacked(input: Uint8Array): number;
2583
+ /** Total `id` sum across a packed JSON batch (valid items only). */
2584
+ jsonSumBatchTotalPacked(input: Uint8Array): number;
2585
+ /** Total packed-output length (bytes) a query batch will produce. */
2586
+ queryParseBatchTotalLenPacked(input: Uint8Array): number;
2587
+ /** Total packed-output length (bytes) a cookie batch will produce. */
2588
+ cookieParseBatchTotalLenPacked(input: Uint8Array): number;
2589
+ /** Total packed-output length (bytes) an HTTP-request batch will produce. */
2590
+ httpParseRequestBatchTotalLenPacked(input: Uint8Array): number;
2591
+ /** Packed Argon2id-hash batch: packed `[u8[]]` passwords + shared salt → packed PHC strings. */
2592
+ passwordHashBatchPacked(input: Uint8Array, salt: Uint8Array, options?: PasswordHashOptions | null): Uint8Array;
2593
+ /** Packed password-verify batch (two packed lists, zipped) → bitset. */
2594
+ passwordVerifyBatchPacked(passwords: Uint8Array, phcs: Uint8Array): Uint8Array;
2595
+ /** Packed URL-resolve batch (two packed lists, zipped) → packed results. */
2596
+ urlResolveBatchPacked(bases: Uint8Array, references: Uint8Array): Uint8Array;
2597
+ /** Packed AEAD-encrypt batch (packed inputs + shared key/nonce) → packed ciphertexts. */
2598
+ aeadEncryptBatchPacked(input: Uint8Array, key: Uint8Array, nonce: Uint8Array, algorithm?: string | null): Uint8Array;
2599
+ /** Packed AEAD-decrypt batch (packed inputs + shared key/nonce) → packed plaintexts. */
2600
+ aeadDecryptBatchPacked(input: Uint8Array, key: Uint8Array, nonce: Uint8Array, algorithm?: string | null): Uint8Array;
2601
+ /** Packed gzip-compress batch: packed `[u8[]]` items → packed gzip streams. */
2602
+ gzipCompressBatchPacked(input: Uint8Array, level?: number | null): Uint8Array;
2603
+ /** Packed gzip-decompress batch (64 MiB cap per item) → packed plaintexts. */
2604
+ gzipDecompressBatchPacked(input: Uint8Array): Uint8Array;
2605
+ /** Packed brotli-compress batch: packed `[u8[]]` items → packed brotli streams. */
2606
+ brotliCompressBatchPacked(input: Uint8Array, quality?: number | null): Uint8Array;
2607
+ /** Packed brotli-decompress batch (64 MiB cap per item) → packed plaintexts. */
2608
+ brotliDecompressBatchPacked(input: Uint8Array): Uint8Array;
2609
+ /** Packed HS256 JWT-sign batch (packed claims + shared secret) → packed tokens. */
2610
+ jwtSignBatchPacked(input: Uint8Array, secret: Uint8Array, ttlSeconds: number | null, nowSeconds: number): Uint8Array;
2611
+ /** Packed HS256 JWT-verify batch (packed tokens + shared secret) → packed claims/validity. */
2612
+ jwtVerifyBatchPacked(input: Uint8Array, secret: Uint8Array, nowSeconds: number): Uint8Array;
2613
+ /** Batch HMAC-SHA256 sign (packed in → hex-signature byte results). */
2614
+ hmacSha256BatchPacked(input: Uint8Array, key: Uint8Array): Uint8Array;
2615
+ /** Batch HMAC-SHA256 verify (two packed lists: data + hex sigs, zipped) → bitset. */
2616
+ hmacSha256VerifyBatchPacked(input: Uint8Array, sigs: Uint8Array, key: Uint8Array): Uint8Array;
2617
+ /** Packed multipart/form-data parser batch (packed bodies + shared boundary) → packed parts. */
2618
+ multipartParseBatchPacked(input: Uint8Array, boundary: Uint8Array): Uint8Array;
2619
+ /** Packed minijinja template batch: packed contexts + shared source → packed renders. */
2620
+ templateRenderBatchPacked(input: Uint8Array, source: string): Uint8Array;
2621
+ /** Packed SSE-frame batch: packed data + shared event/id/retry → packed frames. */
2622
+ sseEncodeBatchPacked(input: Uint8Array, event: string | null, id: string | null, retry: number | null): Uint8Array;
2623
+ /** Packed WebSocket-frame encode batch: packed payloads + shared opcode/mask/fin → packed frames. */
2624
+ wsFrameEncodeBatchPacked(input: Uint8Array, opcode: number, mask: boolean, fin: boolean): Uint8Array;
2625
+ /** Packed WebSocket-frame decode batch: packed frames → packed (fin, opcode, payload) results. */
2626
+ wsFrameDecodeBatchPacked(input: Uint8Array): Uint8Array;
2627
+ }
2628
+ /**
2629
+ * The flat `rust.*` scalar method surface: bytes in → normalized out. Built by
2630
+ * the scalar builders from the native addon + Bun built-in delegations.
2631
+ */
2632
+ export interface RustScalar {
2633
+ crc32(input: Uint8Array): number;
2634
+ fnv1a64(input: Uint8Array): bigint;
2635
+ /**
2636
+ * XXH3-64 over raw bytes (high-throughput non-cryptographic hash).
2637
+ * Compare against `Bun.hash.xxHash3` — see docs/bun-builtins-decision-matrix.md.
2638
+ */
2639
+ xxh3(input: Uint8Array): bigint;
2640
+ hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array | string;
2641
+ hmacSha256Verify(key: Uint8Array, data: Uint8Array, sig: Uint8Array | string): boolean;
2642
+ jsonValid(input: Uint8Array): boolean;
2643
+ /**
2644
+ * Parse JSON to a JS value (FFI packed structural parse on Bun — sonic-rs
2645
+ * parses ONCE, JS assembles from a token stream with no re-parse; napi
2646
+ * sonic-rs DOM fallback). Throws on invalid JSON.
2647
+ */
2648
+ jsonParse(input: Uint8Array): unknown;
2649
+ jsonSumIds(input: Uint8Array): bigint;
2650
+ jsonPatch(doc: Uint8Array, patch: Uint8Array): Uint8Array | string;
2651
+ mimeFromExtension(ext: Uint8Array): Uint8Array | string;
2652
+ randomToken(byteLen: number): Uint8Array | string;
2653
+ randomTokenInto(byteLen: number, output: Uint8Array): number;
2654
+ urlEncode(input: Uint8Array): Uint8Array | string;
2655
+ urlDecode(input: Uint8Array): Uint8Array;
2656
+ /**
2657
+ * Strict percent-decode without UTF-8 validation.
2658
+ */
2659
+ urlDecodeBytes(input: Uint8Array): Uint8Array;
2660
+ urlEncodeInto(input: Uint8Array, output: Uint8Array): number;
2661
+ urlDecodeInto(input: Uint8Array, output: Uint8Array): number;
2662
+ validateEmail(input: Uint8Array): boolean;
2663
+ validateUuid(input: Uint8Array): boolean;
2664
+ validateIpv4(input: Uint8Array): boolean;
2665
+ validateIpv6(input: Uint8Array): boolean;
2666
+ /**
2667
+ * Batch fixed-width hex validation: NEWLINE-separated lines in → one
2668
+ * verdict byte (1/0) per line. One native call replaces per-item
2669
+ * `/^[0-9a-f]{width}$/` regex loops (ObjectIds = width 24). Accepts bytes,
2670
+ * a joined string, or an array of id strings (joined internally).
2671
+ */
2672
+ hexValidateBatch(input: Uint8Array | readonly string[], width: number): Uint8Array;
2673
+ /**
2674
+ * Escape JS-RegExp metacharacters so `new RegExp(escaped, flags)` matches
2675
+ * the input literally — the safe literal-substring-search primitive.
2676
+ */
2677
+ regexEscape(input: Uint8Array | string): string;
2678
+ wsAcceptKey(key: Uint8Array): Uint8Array | string;
2679
+ wsAcceptKeyInto(key: Uint8Array, output: Uint8Array): number;
2680
+ httpParseRequestPacked(input: Uint8Array): Uint8Array;
2681
+ queryParsePacked(input: Uint8Array): Uint8Array;
2682
+ cookieParsePacked(input: Uint8Array): Uint8Array;
2683
+ httpParseRequestPackedInto(input: Uint8Array, output: Uint8Array): number;
2684
+ queryParsePackedInto(input: Uint8Array, output: Uint8Array): number;
2685
+ cookieParsePackedInto(input: Uint8Array, output: Uint8Array): number;
2686
+ /**
2687
+ * Parse an application/x-www-form-urlencoded body into packed pairs.
2688
+ */
2689
+ formParsePacked(input: Uint8Array): Uint8Array;
2690
+ formParsePackedInto(input: Uint8Array, output: Uint8Array): number;
2691
+ /**
2692
+ * Parse a Content-Type header into a structured media type.
2693
+ */
2694
+ parseMediaType(input: Uint8Array): MediaTypeResult;
2695
+ /**
2696
+ * Generate a strong/weak ETag (crc32-based).
2697
+ */
2698
+ etag(data: Uint8Array, weak?: boolean): Uint8Array | string;
2699
+ etagInto(data: Uint8Array, output: Uint8Array, weak?: boolean): number;
2700
+ /**
2701
+ * Format a unix timestamp as an IMF-fixdate HTTP-date.
2702
+ */
2703
+ httpDate(secs?: number): Uint8Array | string;
2704
+ httpDateInto(secs: number | undefined, output: Uint8Array): number;
2705
+ parseHttpDate(input: Uint8Array): bigint | null;
2706
+ parseAcceptEncoding(input: Uint8Array): EncodingPrefResult[];
2707
+ /**
2708
+ * Base64 encode (standard by default; url-safe/padding configurable).
2709
+ */
2710
+ base64Encode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array | string;
2711
+ /**
2712
+ * Base64 decode (standard); throws on invalid input.
2713
+ */
2714
+ base64Decode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array;
2715
+ base64EncodeInto(input: Uint8Array, output: Uint8Array, urlSafe?: boolean, padding?: boolean): number;
2716
+ base64DecodeInto(input: Uint8Array, output: Uint8Array, urlSafe?: boolean, padding?: boolean): number;
2717
+ base64UrlEncode(input: Uint8Array): Uint8Array | string;
2718
+ base64UrlDecode(input: Uint8Array): Uint8Array;
2719
+ /**
2720
+ * Lowercase hex encode.
2721
+ */
2722
+ hexEncode(input: Uint8Array): Uint8Array | string;
2723
+ /**
2724
+ * Hex decode; throws on odd length or invalid digits.
2725
+ */
2726
+ hexDecode(input: Uint8Array): Uint8Array;
2727
+ hexEncodeInto(input: Uint8Array, output: Uint8Array): number;
2728
+ hexDecodeInto(input: Uint8Array, output: Uint8Array): number;
2729
+ hmacSha256Into(key: Uint8Array, data: Uint8Array, output: Uint8Array): number;
2730
+ signCookieInto(value: Uint8Array, secret: Uint8Array, output: Uint8Array): number;
2731
+ verifyCookieInto(signed: Uint8Array, secret: Uint8Array, output: Uint8Array): number | null;
2732
+ csrfTokenInto(secret: Uint8Array, output: Uint8Array): number;
2733
+ aeadEncryptInto(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, output: Uint8Array, algorithm?: string | null): number;
2734
+ wsFrameEncodeInto(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean, output: Uint8Array): number;
2735
+ gzipCompressInto(data: Uint8Array, output: Uint8Array, level?: number | null): number;
2736
+ brotliCompressInto(data: Uint8Array, output: Uint8Array, quality?: number | null): number;
2737
+ /**
2738
+ * Sign a cookie value as `value.signature` (HMAC-SHA256 hex).
2739
+ */
2740
+ signCookie(value: Uint8Array, secret: Uint8Array): Uint8Array | string;
2741
+ /**
2742
+ * Verify a signed cookie; returns the value or null.
2743
+ */
2744
+ verifyCookie(signed: Uint8Array, secret: Uint8Array): Uint8Array | string | null;
2745
+ /**
2746
+ * Create a CSRF token (32-byte random hex + HMAC signature).
2747
+ */
2748
+ csrfToken(secret: Uint8Array): Uint8Array | string;
2749
+ /**
2750
+ * Constant-time verify a CSRF token.
2751
+ */
2752
+ csrfVerify(token: Uint8Array, secret: Uint8Array): boolean;
2753
+ /**
2754
+ * Resolve a URL reference against a base (RFC 3986).
2755
+ */
2756
+ urlResolve(base: Uint8Array, reference: Uint8Array): Uint8Array | string;
2757
+ /**
2758
+ * Build a percent-encoded query string from params (sorted keys).
2759
+ */
2760
+ urlEncodeQuery(params: Record<string, string>): Uint8Array | string;
2761
+ createSchemaValidator(schema: Uint8Array): SchemaValidatorInstance;
2762
+ createHmacSigner(key: Uint8Array): HmacSignerInstance;
2763
+ createTemplateRenderer(source: string): TemplateRendererInstance;
2764
+ createFormParser(capacity?: number): FormParserInstance;
2765
+ createMediaTypeParser(): MediaTypeParserInstance;
2766
+ /**
2767
+ * Higher-order conditional-request check: per-resource 304 decision (setup once).
2768
+ */
2769
+ createConditionalRequest(etagValue: Uint8Array, lastModifiedSecs?: number): ConditionalRequestInstance;
2770
+ /**
2771
+ * Higher-order negotiator: precompiles supported encodings once.
2772
+ */
2773
+ createAcceptNegotiator(supported: string[]): AcceptNegotiatorInstance;
2774
+ createBase64Codec(urlSafe?: boolean, padding?: boolean): Base64CodecInstance;
2775
+ createCookieSigner(secret: Uint8Array): CookieSignerInstance;
2776
+ createCsrfProtector(secret: Uint8Array): CsrfProtectorInstance;
2777
+ createUrlBuilder(base: Uint8Array): UrlBuilderInstance;
2778
+ createJwtSigner(secret: Uint8Array, ttlSeconds?: number): JwtSignerInstance;
2779
+ createAeadCipher(key: Uint8Array, algorithm?: string): AeadCipherInstance;
2780
+ createArgon2Hasher(options?: PasswordHashOptions | null): Argon2HasherInstance;
2781
+ createMediaTypeMatcher(expected: Uint8Array): MediaTypeMatcherInstance;
2782
+ /**
2783
+ * Sharded fixed-window per-key rate limiter. `maxEntries` clamps internally
2784
+ * (default 1,048,576). Each instance owns an independent budget.
2785
+ * @remarks Scalar per-check cost: ~3-14x SLOWER than the JS Map baseline —
2786
+ * prefer the ingress pipeline (one FFI for all stages) for native rate
2787
+ * limiting in a request path. [measured 2026-08]
2788
+ */
2789
+ createRateLimiter(limit: number, windowMs: number, maxEntries?: number | null): RateLimiterInstance;
2790
+ /**
2791
+ * Create a sharded metrics registry (counters / gauges / histograms +
2792
+ * Prometheus render). Fully C-ABI-backed on Bun (caller-owned native
2793
+ * handle); the napi class on Node. Call `destroy()` on ffi-backed
2794
+ * registries at shutdown.
2795
+ */
2796
+ createMetricsRegistry(): MetricsRegistryInstance;
2797
+ initThreadPool(rayonThreads?: number): void;
2798
+ rayonNumThreads(): number;
2799
+ jwtSign(claims: Record<string, unknown>, secret: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number): Uint8Array;
2800
+ /**
2801
+ * Sign a JWT from pre-serialized claim JSON bytes — skips the napi
2802
+ * `serde_json::Value` marshal of `jwtSign` for callers that already hold the
2803
+ * bytes (same injection semantics).
2804
+ */
2805
+ jwtSignBytes(claimsJson: Uint8Array, secret: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number): Uint8Array | string;
2806
+ jwtSignBytesInto(claimsJson: Uint8Array, secret: Uint8Array, output: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number): number;
2807
+ jwtVerify(token: Uint8Array, secret: Uint8Array, nowSeconds?: number): unknown;
2808
+ passwordHash(password: Uint8Array, salt: Uint8Array, options?: PasswordHashOptions | null): Uint8Array | string;
2809
+ passwordVerify(password: Uint8Array, phc: Uint8Array): boolean;
2810
+ /**
2811
+ * bcrypt password hash → `$2b$` PHC string (cost clamped 4..=31).
2812
+ */
2813
+ passwordHashBcrypt(password: Uint8Array, cost: number): string;
2814
+ /**
2815
+ * Verify a password against a bcrypt `$2b$` PHC string.
2816
+ */
2817
+ passwordVerifyBcrypt(password: Uint8Array, hash: string): boolean;
2818
+ /**
2819
+ * PBKDF2-HMAC-SHA256 key derivation (dkLen clamped to 1 MiB).
2820
+ */
2821
+ pbkdf2Sha256(password: Uint8Array, salt: Uint8Array, rounds: number, dkLen: number): Uint8Array;
2822
+ aeadEncrypt(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, algorithm?: string | null): Uint8Array;
2823
+ aeadDecrypt(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, algorithm?: string | null): Uint8Array | null;
2824
+ gzipCompress(data: Uint8Array, level?: number | null): Uint8Array;
2825
+ /**
2826
+ * @param maxDecompressed Caps decompressed output (default 64 MiB) as a
2827
+ * decompression-bomb guard; a larger value errors.
2828
+ */
2829
+ gzipDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array;
2830
+ /**
2831
+ * gzip-decompress into `output` (pooled — caller-owned buffer); returns bytes
2832
+ * written. Throws when `output` is too small (the exact required size is
2833
+ * reported) or the input is invalid / exceeds `maxDecompressed`.
2834
+ * @param maxDecompressed Caps decompressed output (default 64 MiB) as a
2835
+ * decompression-bomb guard; a larger value errors.
2836
+ */
2837
+ gzipDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number | null): number;
2838
+ brotliCompress(data: Uint8Array, quality?: number | null): Uint8Array;
2839
+ /**
2840
+ * @param maxDecompressed Caps decompressed output (default 64 MiB) as a
2841
+ * decompression-bomb guard; a larger value errors.
2842
+ */
2843
+ brotliDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array;
2844
+ /**
2845
+ * brotli-decompress into `output` (pooled — caller-owned buffer); returns
2846
+ * bytes written. Throws when `output` is too small (the exact required size
2847
+ * is reported) or the input is invalid / exceeds `maxDecompressed`.
2848
+ * @param maxDecompressed Caps decompressed output (default 64 MiB) as a
2849
+ * decompression-bomb guard; a larger value errors.
2850
+ */
2851
+ brotliDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number | null): number;
2852
+ multipartParse(body: Uint8Array, boundary: Uint8Array): MultipartPart[];
2853
+ multipartParsePacked(body: Uint8Array, boundary: Uint8Array): Uint8Array;
2854
+ multipartParsePackedInto(body: Uint8Array, boundary: Uint8Array, output: Uint8Array): number;
2855
+ wsFrameEncode(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean): Uint8Array;
2856
+ wsFrameDecode(data: Uint8Array): WsFrame | null;
2857
+ wsFrameDecodePackedInto(data: Uint8Array, output: Uint8Array): number | null;
2858
+ sseEncodeEvent(event: string | null, data: Uint8Array, id: string | null, retry: number | null): Uint8Array;
2859
+ sseEncodeEventInto(event: string | null, data: Uint8Array, id: string | null, retry: number | null, output: Uint8Array): number;
2860
+ }
2861
+ /** String-oriented FFI namespace. */
2862
+ export interface RustText {
2863
+ /**
2864
+ * Resolve a lowercase file extension to a MIME type string (e.g. `"html"` →
2865
+ * `"text/html"`). Unknown extensions return `"application/octet-stream"`.
2866
+ * @param ext the file extension WITHOUT a leading dot, case-insensitive.
2867
+ * @returns the MIME type for `ext`.
2868
+ */
2869
+ mimeFromExtension(ext: string): string;
2870
+ /**
2871
+ * RFC 3986 percent-encode a string (the `encodeURIComponent` unreserved
2872
+ * set). Under Bun this delegates to the native `encodeURIComponent` for
2873
+ * ~4x speed with byte-for-byte parity.
2874
+ * @param input the string to encode.
2875
+ * @returns the percent-encoded string.
2876
+ */
2877
+ urlEncode(input: string): string;
2878
+ /**
2879
+ * Percent-decode a string. Throws on malformed `%XX` or invalid UTF-8
2880
+ * output (parity with `decodeURIComponent`, which is used under Bun).
2881
+ * @param input the percent-encoded string to decode.
2882
+ * @returns the decoded string.
2866
2883
  */
2867
- loadStrategy?: LoadStrategy;
2868
- }
2869
- /** Options for `load()` / `opFn.load()`. */
2870
- export interface LoadOptions {
2884
+ urlDecode(input: string): string;
2871
2885
  /**
2872
- * Explicit cache key. When omitted (and caching is on) the default key is
2873
- * `fnv1a64(input)` — which costs one extra native call per load.
2886
+ * Compute the RFC 6455 `Sec-WebSocket-Accept` value for a client key.
2887
+ * @param key the base64 `Sec-WebSocket-Key` header value.
2888
+ * @returns the 28-byte base64 accept value.
2874
2889
  */
2875
- key?: string;
2876
- /** Override the loader-wide caching toggle for this call. Default true. */
2877
- cache?: boolean;
2878
- }
2879
- /** Per-op counters exposed on `opFn.stats`. */
2880
- export interface LoaderOpStats {
2881
- scalarCalls: number;
2882
- batchCalls: number;
2883
- itemsDispatched: number;
2884
- cachedHits: number;
2885
- /** Current learned batch threshold for this op. */
2886
- batchMin: number;
2887
- /** Current `load()` strategy: "single" (low load) or "coalesce" (bulk). */
2888
- mode: "single" | "coalesce";
2889
- }
2890
- /** Aggregate loader statistics. */
2891
- export interface LoaderStats {
2892
- scalarCalls: number;
2893
- batchCalls: number;
2894
- itemsDispatched: number;
2895
- flushes: number;
2896
- cachedHits: number;
2897
- cacheSize: number;
2898
- cacheEvictions: number;
2890
+ wsAcceptKey(key: string): string;
2891
+ /** @param input an email address. @returns whether it is RFC-valid. */
2892
+ validateEmail(input: string): boolean;
2893
+ /** @param input a UUID string. @returns whether it is a valid (v1-v8) UUID. */
2894
+ validateUuid(input: string): boolean;
2895
+ /** @param input an IPv4 address. @returns whether it is valid. */
2896
+ validateIpv4(input: string): boolean;
2897
+ /** @param input an IPv6 address. @returns whether it is valid. */
2898
+ validateIpv6(input: string): boolean;
2899
2899
  }
2900
- /** A specialized, pre-bound op function returned by `loader(op)`. */
2901
- export interface LoaderOpFn<K extends LoaderOpName> {
2902
- /** Single item → scalar result. */
2903
- (input: Uint8Array, ...rest: LoaderOpArgs[K]): LoaderScalar<K>;
2904
- /** Bulk items → ONE packed batch call; same shape as `rust.batch.<op>`. */
2905
- (inputs: Uint8Array[], ...rest: LoaderBulkArgs<K>): LoaderBulk<K>;
2906
- readonly name: K;
2907
- readonly stats: LoaderOpStats;
2908
- clear(): void;
2909
- /** Sync cache peek (default key `fnv1a64(input)` unless `key` given). */
2910
- cache(input: Uint8Array, key?: string): LoaderScalar<K> | undefined;
2900
+ /** The full Rust FFI client (scalar methods + namespaces + configuration). */
2901
+ export interface RustClient extends RustScalar {
2902
+ text: RustText;
2903
+ batch: RustBatch;
2904
+ packed: RustPacked;
2911
2905
  /**
2912
- * Coalesced, cache-aware load. Unavailable (`undefined`) on ops that require
2913
- * extra arguments (e.g. `hmacSha256`, `signCookie`) — use `run` for those.
2906
+ * Resolve the native transport in use on this runtime: `"ffi"` (bun:ffi —
2907
+ * the PRIMARY transport under Bun) or `"napi"` (Node, forced
2908
+ * `CASTRUM_FFI_MODE=napi`, or a failed ffi bind-time self-test).
2914
2909
  */
2915
- load: K extends LoadableName ? LoaderLoadFn<K> : undefined;
2910
+ transport(): "ffi" | "napi";
2911
+ /** Whether the bun:ffi transport is live (equivalent to `transport() === "ffi"`). */
2912
+ ffiActive(): boolean;
2913
+ /**
2914
+ * Update defaults on this instance. `mimeCache` / `hmacCache` apply
2915
+ * immediately (per-instance). `rayonThreads` is applied only if the
2916
+ * process-wide pool is not yet initialized (see {@link RustOptions}).
2917
+ */
2918
+ configure(options: RustOptions): void;
2916
2919
  }
2917
- export type LoaderLoadFn<K extends LoaderOpName> = (input: Uint8Array, opts?: LoadOptions) => Promise<LoaderScalar<K>>;
2918
- /** Ops whose extra args are all optional/empty → they support `load()`. */
2919
- export type LoadableName = {
2920
- [K in LoaderOpName]: LoaderOpArgs[K] extends [
2921
- ] | [
2922
- unknown?
2923
- ] | [
2924
- unknown?,
2925
- unknown?
2926
- ] | [
2927
- unknown?,
2928
- unknown?,
2929
- unknown?
2930
- ] ? K : never;
2931
- }[LoaderOpName];
2932
- /** A validator-bound helper returned by `loader.schema(validator)`. */
2933
- export interface LoaderSchemaFn {
2934
- /** Single JSON document → valid? */
2935
- (input: Uint8Array): boolean;
2936
- /** Bulk JSON documents → bitset (1 per doc). */
2937
- (inputs: Uint8Array[]): Uint8Array;
2938
- /** Count how many bulk documents are valid. */
2939
- count(inputs: Uint8Array[]): number;
2920
+ /**
2921
+ * Create an isolated Rust client with the given defaults.
2922
+ *
2923
+ * `mimeCache` / `hmacCache` are per-instance. `rayonThreads` only applies
2924
+ * before the process-wide rayon pool is initialized (once per process — see
2925
+ * {@link RustOptions.rayonThreads}); the pool defaults to the env var or
2926
+ * `max(1, hardwareConcurrency - 1)` otherwise.
2927
+ */
2928
+ export declare function createRust(options?: RustOptions): RustClient;
2929
+ /**
2930
+ * The default, ready-to-use `RustClient` created with no options (resolves
2931
+ * rayon threads from the host automatically). Most consumers just do
2932
+ * `import { rust } from "castrum"` and call `rust.<fn>(...)` directly.
2933
+ */
2934
+ export declare const rust: RustClient;
2935
+ /** The full `rust.*` surface — identical to `rust`; the proven wiring is baked (see {@link ../shared/proven}). */
2936
+ export type ProvenClient = RustClient;
2937
+ /** The full `rust.*` surface, exposed for audit. Same object as `rust`. */
2938
+ export declare const proven: ProvenClient;
2939
+ /** Recommended implementation for an operation. */
2940
+ export type OpImpl = "native" | "js";
2941
+ /** A selection decision: recommended implementation + rationale note. */
2942
+ export interface OpDecision {
2943
+ /** The recommended implementation when the addon is available. */
2944
+ readonly impl: OpImpl;
2945
+ /** Why this decision (measured ratio / rationale). */
2946
+ readonly note?: string;
2940
2947
  }
2941
- /** The global callable loader. */
2942
- interface Loader$1 {
2943
- /** Higher-order function: `loader(op)` → specialized hot op function. */
2944
- <K extends LoaderOpName>(op: K): LoaderOpFn<K>;
2945
- /** Single-item dispatch. */
2946
- run<K extends LoaderOpName>(op: K, input: Uint8Array, ...rest: LoaderOpArgs[K]): LoaderScalar<K>;
2947
- /** Bulk dispatch (one packed batch call, or adaptive scalar loop). */
2948
- run<K extends LoaderOpName>(op: K, inputs: Uint8Array[], ...rest: LoaderBulkArgs<K>): LoaderBulk<K>;
2949
- /** Coalesced, cache-aware load (ops with required extra args throw). */
2950
- load<K extends LoadableName>(op: K, input: Uint8Array, opts?: LoadOptions): Promise<LoaderScalar<K>>;
2951
- /** Sync cache peek. */
2952
- cache<K extends LoaderOpName>(op: K, input: Uint8Array, key?: string): LoaderScalar<K> | undefined;
2948
+ /**
2949
+ * The benchmark-driven native-vs-JS decision for `op` — runtime-aware.
2950
+ *
2951
+ * Returns `"native"` when the Rust addon is the recommended implementation,
2952
+ * `"js"` when a pure-JS (or Bun built-in) implementation wins, or `null` for
2953
+ * unknown ops (or when the addon is unavailable — with no addon there is no
2954
+ * "native" choice). Under Bun, ops in the adapter's builtins registry resolve
2955
+ * to `"js"` so the consumer's JS path delegates to the faster Bun built-in.
2956
+ *
2957
+ * The default is the BAKED proven selection (`src/shared/proven.ts`):
2958
+ * `native` / `js` directly, `bun` resolves to `"js"` via the builtins check
2959
+ * above. Unknown ops (and `bun` entries on a non-Bun runtime, where no
2960
+ * built-in exists) fall back to the addon's embedded decision
2961
+ * (`rust/selection.rs` ← `src/selection.json`).
2962
+ */
2963
+ export declare const opImpl: (op: string) => OpImpl | null;
2964
+ /** `true` when the Rust addon is the recommended impl for `op`. */
2965
+ export declare const isNativeOp: (op: string) => boolean;
2966
+ /** The decision record for `op`, or `null` when unknown. */
2967
+ export declare const opDecision: (op: string) => OpDecision | null;
2968
+ /** Options for an `AdaptiveEstimate`: EWMA smoothing, bounds, starting value. */ export interface AdaptiveEstimateOptions {
2953
2969
  /**
2954
- * Bind a `SchemaValidator` for repeated single/bulk JSON-schema validation
2955
- * plus a whole-batch `count`. The returned callable uses the loader's normal
2956
- * dispatch machinery (cost model + counters).
2970
+ * EWMA smoothing factor in (0, 1]. Higher reacts faster to new samples;
2971
+ * lower smooths more. Default: 0.25.
2957
2972
  */
2958
- schema(validator: SchemaValidator): LoaderSchemaFn;
2959
- /** Clear the shared cache (pending coalesced loads still flush). */
2960
- clear(): void;
2961
- /** Update options at runtime. */
2962
- configure(options: LoaderOptions): void;
2963
- readonly stats: LoaderStats;
2964
- readonly opNames: LoaderOpName[];
2973
+ alpha?: number;
2974
+ /** Starting estimate. Default 0 (the first sample wins). */
2975
+ initial?: number;
2976
+ /** Clamp the estimate to at least this. Default 0. */
2977
+ min?: number;
2978
+ /** Clamp the estimate to at most this (bounds memory / resources). Default Infinity. */
2979
+ max?: number;
2965
2980
  }
2966
- /** Load-dispatch strategy selector for `load()`. */
2967
- export type LoadStrategy = "auto" | "single" | "coalesce";
2968
- /** Create an isolated loader with the given defaults. */
2969
- export declare function createLoader(options?: LoaderOptions): Loader$1;
2970
2981
  /**
2971
- * The global, ready-to-use loader. Most consumers just do
2972
- * `import { loader } from "castrum"` and either `loader("validateEmail")(…)`
2973
- * (higher-order function) or `loader.run("validateEmail", …)`.
2982
+ * A bounded exponential-moving-average estimate of an observed quantity.
2983
+ *
2984
+ * `sample(x)` folds a new observation into the running estimate; `estimate`
2985
+ * reads the current value. The estimate is clamped to `[min, max]`, so it is
2986
+ * safe to drive resource sizing (buffer pools, batch thresholds) with it —
2987
+ * a single outlier cannot blow up the estimate, and it decays toward recent
2988
+ * observations.
2974
2989
  */
2975
- export declare const loader: Loader$1;
2990
+ export declare class AdaptiveEstimate {
2991
+ private readonly alpha;
2992
+ private readonly min;
2993
+ private readonly max;
2994
+ private readonly initial;
2995
+ private value;
2996
+ constructor(options?: AdaptiveEstimateOptions);
2997
+ /** Fold a new observation into the running estimate. */
2998
+ sample(x: number): void;
2999
+ /** Current estimate (clamped to `[min, max]`). */
3000
+ get estimate(): number;
3001
+ /** Reset the estimate (back to `initial`). */
3002
+ reset(): void;
3003
+ }
2976
3004
  /**
2977
- * Batch-validate JSON documents against a schema through the loader's bulk
2978
- * `schemaValidate` path. Returns a bitset — byte `i` is 1 when document `i`
2979
- * is valid (same shape as `rust.batch.schemaValidate`).
3005
+ * Shared UTF-8 encoder (encode / encodeInto), backed by the runtime-native
3006
+ * codec (`src/shared/codec.ts`): a shared native `TextEncoder` singleton on
3007
+ * both runtimes (on Bun 1.4 the engine-native implementation measures ~3x
3008
+ * faster than the old `Bun.ArrayBufferSink` path) plus a zero-copy `Buffer`
3009
+ * view write for `encodeInto`. Reusing a single object avoids allocation
3010
+ * overhead in hot paths.
3011
+ *
3012
+ * `encode` accepts the string-or-bytes union the text-returning `rust.*` ops
3013
+ * surface (Bun returns strings, Node returns bytes) and normalizes to bytes —
3014
+ * a string is UTF-8 encoded, bytes pass through unchanged.
2980
3015
  */
2981
- export declare function validateMany(validator: SchemaValidator, docs: Uint8Array[]): Uint8Array;
2982
- /** Count how many of `docs` validate against `validator` (one packed call). */
2983
- export declare function validateCount(validator: SchemaValidator, docs: Uint8Array[]): number;
3016
+ export declare const encoder: {
3017
+ encode(s: Uint8Array | string): Uint8Array;
3018
+ encodeInto(s: string, dest: Uint8Array): {
3019
+ read: number;
3020
+ written: number;
3021
+ };
3022
+ };
2984
3023
  /**
2985
- * Generic loader bulk dispatch: `loader.run(op, items, ...rest)` → one packed
2986
- * batch native call (or the adaptive scalar loop for tiny batches).
3024
+ * Shared UTF-8 decoder, backed by the runtime-native codec
3025
+ * (`src/shared/codec.ts`): on Bun it uses `new CString(ptr, 0, len)` from
3026
+ * `bun:ffi` (JSC-native, exact-length clone); under Node it falls back to
3027
+ * `TextDecoder`. Reusing a single object avoids allocation overhead in hot
3028
+ * paths.
3029
+ *
3030
+ * `decode` accepts the string-or-bytes union the text-returning `rust.*` ops
3031
+ * surface and normalizes to a string — bytes are UTF-8 decoded, strings pass
3032
+ * through unchanged.
2987
3033
  */
2988
- export declare function runMany<K extends LoaderOpName>(op: K, items: Uint8Array[], ...rest: LoaderBulkArgs<K>): LoaderBulk<K>;
2989
- /** Single-item loader dispatch — pairs with `runMany` for mixed workloads. */
2990
- export declare function runOne<K extends LoaderOpName>(op: K, item: Uint8Array, ...rest: LoaderOpArgs[K]): LoaderScalar<K>;
3034
+ export declare const decoder: {
3035
+ decode(bytes: Uint8Array | string): string;
3036
+ };
3037
+ /**
3038
+ * Generate a UUIDv7 (time-ordered) string, preferring Bun's native built-in.
3039
+ * Under Node this falls back to `crypto.randomUUID` (v4).
3040
+ *
3041
+ * @returns A UUID string.
3042
+ */
3043
+ export declare const uuidv7: () => string;
2991
3044
 
2992
3045
  export {
2993
3046
  Loader$1 as Loader,