castrum 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/castrum.darwin-arm64.node +0 -0
- package/castrum.darwin-x64.node +0 -0
- package/castrum.linux-arm64-gnu.node +0 -0
- package/castrum.linux-arm64-musl.node +0 -0
- package/castrum.linux-x64-gnu.node +0 -0
- package/castrum.linux-x64-musl.node +0 -0
- package/castrum.linux-x64-v3-gnu.node +0 -0
- package/castrum.win32-x64-msvc.node +0 -0
- package/dist/index.d.ts +501 -5
- package/dist/index.js +1648 -1325
- package/docs/BENCHMARKS.md +13 -0
- package/docs/CASE_STUDY.md +1 -1
- package/docs/ENVIRONMENT.md +1 -1
- package/docs/FFI_BUN_GUIDE.md +1 -1
- package/docs/INGRESS.md +4 -4
- package/docs/REPO_MAP.md +7 -5
- package/docs/superpowers/plans/2026-09-18-castrum-hardening-cleanup.md +617 -0
- package/index.ts +1 -0
- package/package.json +3 -2
- package/rust/crypto/aead.rs +44 -0
- package/rust/crypto/argon2.rs +32 -2
- package/rust/crypto/jwt/tests.rs +19 -0
- package/rust/crypto/jwt/token.rs +43 -28
- package/rust/crypto/session.rs +6 -0
- package/rust/ffi/crypto.rs +63 -32
- package/rust/ffi/hashing.rs +48 -33
- package/rust/ffi/http.rs +62 -36
- package/rust/ffi/metrics.rs +6 -2
- package/rust/ffi/payload.rs +47 -27
- package/rust/ffi/task.rs +51 -32
- package/rust/ffi/tests.rs +24 -0
- package/rust/ffi/util.rs +40 -0
- package/rust/http/headers.rs +13 -0
- package/rust/http/multipart.rs +139 -0
- package/rust/ingress/native_route.rs +21 -3
- package/rust/task/completion.rs +7 -0
- package/rust/task/mod.rs +1 -1
- package/rust/task/tests.rs +30 -0
- package/src/ingress/decode/baked-result.ts +13 -5
- package/src/ingress/decode/fast-result.ts +1 -1
- package/src/ingress/decode/result-base.ts +16 -2
- package/src/ingress/handlers.ts +103 -31
- package/src/ingress/headers/memoized-headers.ts +40 -0
- package/src/ingress/native-route.ts +25 -6
- package/src/ingress/options.ts +26 -2
- package/src/ingress/packing/gather-raw-headers.ts +24 -8
- package/src/ingress/response/baked-response.ts +118 -28
- package/src/ingress/router.ts +14 -2
- package/src/ingress/routes/common.ts +26 -10
- package/src/ingress/routes/echo.ts +50 -7
- package/src/ingress/routes/json-write.ts +21 -8
- package/src/ingress/routes/responder.ts +4 -4
- package/src/ingress/server-node.ts +33 -14
- package/src/ingress/server.ts +3 -4
- package/src/ingress/shared.ts +1 -1
- package/src/ingress/types.ts +15 -0
- package/src/loader/ops.ts +1 -1
- package/src/native/ffi/build/codecs-selftest.ts +305 -0
- package/src/native/ffi/build/codecs.ts +0 -296
- package/src/native/ffi/build/instances-selftest.ts +264 -0
- package/src/native/ffi/build/instances.ts +10 -259
- package/src/native/ffi/build/parse-selftest.ts +238 -0
- package/src/native/ffi/build/parse.ts +11 -234
- package/src/native/ffi/build.ts +5 -1
- package/src/native/ffi/selftest.ts +3 -3
- package/src/native/ffi/symbols.ts +460 -0
- package/src/native/ffi.ts +2 -434
- package/src/native/ingress-binding.ts +39 -0
- package/src/runtime/codec.ts +24 -0
- package/src/runtime/types.ts +11 -0
- package/src/rust-ffi/batch/build.ts +18 -8
- package/src/rust-ffi/batch/types.ts +15 -0
- package/src/rust-ffi/options.ts +19 -0
- package/src/rust-ffi/scalar/crypto.ts +8 -0
- package/src/rust-ffi/scalar/factories.ts +47 -20
- package/src/rust-ffi/scalar/http.ts +60 -10
- package/src/rust-ffi/scalar/interface.ts +4 -1
- package/src/rust-ffi/scalar/payload.ts +27 -1
- package/src/shared/buffer-pool.ts +44 -9
- package/src/shared/codec.ts +26 -0
- package/src/shared/packed/parsers.ts +64 -7
- package/src/task/runtime.ts +29 -28
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "castrum"
|
|
3
3
|
# Keep in sync with package.json "version" (single source of truth: the npm package).
|
|
4
|
-
version = "0.9.
|
|
4
|
+
version = "0.9.8"
|
|
5
5
|
edition = "2021"
|
|
6
6
|
# Keep in sync with package.json "license" (cargo-deny rejects a crate with no
|
|
7
7
|
# license expression).
|
|
Binary file
|
package/castrum.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -116,7 +116,11 @@ export interface NativeRoute {
|
|
|
116
116
|
runFrame(frame: Uint8Array): RouteWireResult;
|
|
117
117
|
/** Convenience: pack a `(query, cookie, body)` frame then {@link runFrame}. */
|
|
118
118
|
run(query: string, cookie: string, body: Uint8Array | null): RouteWireResult;
|
|
119
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Free the native handle. Idempotent: a second call is a no-op. After it,
|
|
121
|
+
* {@link runFrame}/{@link run} throw rather than pass a freed handle into the
|
|
122
|
+
* native stack.
|
|
123
|
+
*/
|
|
120
124
|
destroy(): void;
|
|
121
125
|
}
|
|
122
126
|
/**
|
|
@@ -182,6 +186,13 @@ declare abstract class IngressResultBase {
|
|
|
182
186
|
/** Request body bytes (may be an empty view). */
|
|
183
187
|
body: Uint8Array;
|
|
184
188
|
protected _buf: Uint8Array;
|
|
189
|
+
/**
|
|
190
|
+
* Valid byte length of `_buf` (the native `written` count). `_buf` may be the
|
|
191
|
+
* WHOLE pooled output buffer (`outputBufferSize` bytes), so bounds checks must
|
|
192
|
+
* use this, not `_buf.byteLength` — that lets `run()` skip a per-request
|
|
193
|
+
* `buffer.subarray(0, written)` view.
|
|
194
|
+
*/
|
|
195
|
+
protected _len: number;
|
|
185
196
|
protected _bodyJsonStart: number;
|
|
186
197
|
protected _bodyJsonLen: number;
|
|
187
198
|
private _cookiesBuf;
|
|
@@ -206,7 +217,7 @@ declare abstract class IngressResultBase {
|
|
|
206
217
|
* baked path passes the already-clamped `layout.safeBodyJsonLen`. Sections
|
|
207
218
|
* are stored as slices and decoded lazily by `cookiesJson()`/`queryJson()`.
|
|
208
219
|
*/
|
|
209
|
-
protected setSections(buf: Uint8Array, layout: PackedSectionLayout, bodyJsonLen: number): void;
|
|
220
|
+
protected setSections(buf: Uint8Array, layout: PackedSectionLayout, bodyJsonLen: number, len?: number): void;
|
|
210
221
|
/** Decoded cookies-JSON section, or `"{}"` when absent. */
|
|
211
222
|
cookiesJson(): string;
|
|
212
223
|
/** Decoded query-JSON section, or `"{}"` when absent. */
|
|
@@ -216,7 +227,7 @@ declare abstract class IngressResultBase {
|
|
|
216
227
|
}
|
|
217
228
|
/** Zero-alloc, reusable result decoder for the pre-baked handler path. */
|
|
218
229
|
export declare class BakedIngressResult extends IngressResultBase {
|
|
219
|
-
refresh(buf: Uint8Array, body: Uint8Array, view: DataView): void;
|
|
230
|
+
refresh(buf: Uint8Array, body: Uint8Array, view: DataView, len?: number): void;
|
|
220
231
|
invalidate(): void;
|
|
221
232
|
setInternalError(): void;
|
|
222
233
|
bodyJson(copy: boolean): Uint8Array;
|
|
@@ -427,6 +438,14 @@ export interface OptimizedIngressHandler {
|
|
|
427
438
|
string,
|
|
428
439
|
string
|
|
429
440
|
][];
|
|
441
|
+
/**
|
|
442
|
+
* Success-path headers as a ready `Headers` instance: the memoized base
|
|
443
|
+
* (template + reflected origin) plus per-request rate/request-id extras,
|
|
444
|
+
* without rebuilding `Headers` from an array on every response. Optional so
|
|
445
|
+
* hand-rolled mocks need not provide it; callers fall back to
|
|
446
|
+
* `memoizedHeaders(responseHeaders(...))`.
|
|
447
|
+
*/
|
|
448
|
+
successHeaders?(variant: number, requestIdHeader: string | null, origin: string | null, rateRemaining?: number, rateResetSecs?: number): Headers;
|
|
430
449
|
terminalHeaders(variant: number, ctx: BakedContext, result: BakedIngressResult | null): [
|
|
431
450
|
string,
|
|
432
451
|
string
|
|
@@ -949,6 +968,448 @@ export interface Argon2HasherInstance {
|
|
|
949
968
|
hash(password: Uint8Array, salt: Uint8Array): Uint8Array;
|
|
950
969
|
verify(password: Uint8Array, phc: Uint8Array): boolean;
|
|
951
970
|
}
|
|
971
|
+
/**
|
|
972
|
+
* The bound bun:ffi C-ABI surface. Each member is a dlopen'd `castrum_*`
|
|
973
|
+
* symbol wrapped so inputs are `(ptr,len)` byte slices and text outputs are
|
|
974
|
+
* returned as JS strings via JSC's native transfer (cstring clone) — zero
|
|
975
|
+
* `TextEncoder`/`TextDecoder` on the Bun path. `*Into` variants write into a
|
|
976
|
+
* caller buffer and return the byte count; variable-size ops return the exact
|
|
977
|
+
* required size on a too-small buffer so the transport grows once and retries
|
|
978
|
+
* exactly once.
|
|
979
|
+
*/
|
|
980
|
+
export interface BunFFI {
|
|
981
|
+
crc32(input: Uint8Array): number;
|
|
982
|
+
fnv1a64(input: Uint8Array): bigint;
|
|
983
|
+
xxh3(input: Uint8Array): bigint;
|
|
984
|
+
jsonValid(input: Uint8Array): boolean;
|
|
985
|
+
/** UTF-8 validity probe (native replacement for a fatal `TextDecoder`). */
|
|
986
|
+
utf8Valid(input: Uint8Array): boolean;
|
|
987
|
+
/** Lowercase-hex encode → string (native transfer, zero encode). */
|
|
988
|
+
hexEncode(input: Uint8Array): string;
|
|
989
|
+
/** Lowercase-hex encode into `output`; returns bytes written. */
|
|
990
|
+
hexEncodeInto(input: Uint8Array, output: Uint8Array): number;
|
|
991
|
+
/** RFC 3986 percent-encode → string. */
|
|
992
|
+
urlEncode(input: Uint8Array): string;
|
|
993
|
+
/** RFC 3986 percent-encode into `output`; returns bytes written. */
|
|
994
|
+
urlEncodeInto(input: Uint8Array, output: Uint8Array): number;
|
|
995
|
+
validateEmail(input: string): boolean;
|
|
996
|
+
validateUuid(input: string): boolean;
|
|
997
|
+
validateIpv4(input: string): boolean;
|
|
998
|
+
validateIpv6(input: string): boolean;
|
|
999
|
+
/** Byte-input sibling of {@link validateEmail} (`(ptr,len)` — no decode). */
|
|
1000
|
+
validateEmailBytes(input: Uint8Array): boolean;
|
|
1001
|
+
/** Byte-input sibling of {@link validateUuid} (`(ptr,len)` — no decode). */
|
|
1002
|
+
validateUuidBytes(input: Uint8Array): boolean;
|
|
1003
|
+
/** Byte-input sibling of {@link validateIpv4} (`(ptr,len)` — no decode). */
|
|
1004
|
+
validateIpv4Bytes(input: Uint8Array): boolean;
|
|
1005
|
+
/** Byte-input sibling of {@link validateIpv6} (`(ptr,len)` — no decode). */
|
|
1006
|
+
validateIpv6Bytes(input: Uint8Array): boolean;
|
|
1007
|
+
/** Sum of `id` fields across a JSON array → bigint (throws on non-array input). */
|
|
1008
|
+
jsonSumIds(input: Uint8Array): bigint;
|
|
1009
|
+
hmacSha256Verify(key: Uint8Array, data: Uint8Array, signature: Uint8Array): boolean;
|
|
1010
|
+
csrfVerify(token: Uint8Array, secret: Uint8Array): boolean;
|
|
1011
|
+
passwordVerify(password: Uint8Array, phc: Uint8Array): boolean;
|
|
1012
|
+
/** `phc` is a `cstring` ARG (the engine transcodes the JS PHC string in-engine). */
|
|
1013
|
+
passwordVerifyBcrypt(password: Uint8Array, phc: string): boolean;
|
|
1014
|
+
/** Hex-decode into a fresh buffer (size `input.length / 2`). */
|
|
1015
|
+
hexDecode(input: Uint8Array): Uint8Array;
|
|
1016
|
+
/** Hex-decode into `output`; returns bytes written (throws if too small). */
|
|
1017
|
+
hexDecodeInto(input: Uint8Array, output: Uint8Array): number;
|
|
1018
|
+
/** Percent-decode into a fresh buffer (size `input.length`). */
|
|
1019
|
+
urlDecode(input: Uint8Array): Uint8Array;
|
|
1020
|
+
/** Percent-decode into `output`; returns bytes written (throws if too small). */
|
|
1021
|
+
urlDecodeInto(input: Uint8Array, output: Uint8Array): number;
|
|
1022
|
+
/** base64-decode into a fresh buffer (size `ceil(len/4)*3`). */
|
|
1023
|
+
base64Decode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array;
|
|
1024
|
+
/** base64-decode into `output`; returns bytes written (throws if too small). */
|
|
1025
|
+
base64DecodeInto(input: Uint8Array, output: Uint8Array, urlSafe?: boolean, padding?: boolean): number;
|
|
1026
|
+
/** RFC 6455 Sec-WebSocket-Accept → string (cstring return; `key` is a `cstring` ARG). */
|
|
1027
|
+
wsAcceptKey(key: string): string;
|
|
1028
|
+
/** RFC 6455 Sec-WebSocket-Accept into `output`; returns bytes written. */
|
|
1029
|
+
wsAcceptKeyInto(key: Uint8Array, output: Uint8Array): number;
|
|
1030
|
+
/** crc32 ETag → string (10 strong / 12 weak chars, cstring return). */
|
|
1031
|
+
etag(data: Uint8Array, weak?: boolean): string;
|
|
1032
|
+
/** crc32 ETag into `output`; returns bytes written. */
|
|
1033
|
+
etagInto(data: Uint8Array, output: Uint8Array, weak?: boolean): number;
|
|
1034
|
+
/** Evaluate a precompiled `ConditionalRequest` (opaque `inner` handle from its
|
|
1035
|
+
* napi `inner_ptr()`) — true → 304. `null` header args = absent (flags encode
|
|
1036
|
+
* presence so a present-but-empty header is distinct). A null (0) handle → false. */
|
|
1037
|
+
conditionalIsNotModified(inner: number, ifNoneMatch: Uint8Array | null, ifModifiedSince: Uint8Array | null): boolean;
|
|
1038
|
+
/** Precompiled `MediaTypeMatcher` wildcard match (opaque `inner` handle). */
|
|
1039
|
+
mediaTypeMatcherMatches(inner: number, actual: Uint8Array): boolean;
|
|
1040
|
+
/** Precompiled `AcceptNegotiator` → best supported encoding (opaque `inner`
|
|
1041
|
+
* handle); `null` = identity. */
|
|
1042
|
+
acceptNegotiatorNegotiate(inner: number, header: Uint8Array): string | null;
|
|
1043
|
+
/** Precompiled `AcceptNegotiator` → best supported encoding with SERVER-
|
|
1044
|
+
* preference tie-breaking (RFC 7231 server semantics, opaque `inner` handle);
|
|
1045
|
+
* `null` = identity. */
|
|
1046
|
+
acceptNegotiatorNegotiateServer(inner: number, header: Uint8Array): string | null;
|
|
1047
|
+
/** Precompiled `JwtSigner` → compact token from pre-serialized claim JSON
|
|
1048
|
+
* (opaque `inner` handle); throws on invalid claims / null handle. */
|
|
1049
|
+
jwtSignerSign(inner: number, claimsJson: Uint8Array, nowSeconds: number): Uint8Array;
|
|
1050
|
+
/** Precompiled `JwtSigner` → claims JSON bytes (opaque `inner` handle);
|
|
1051
|
+
* `null` = invalid signature / expired / malformed. */
|
|
1052
|
+
jwtSignerVerify(inner: number, token: Uint8Array, nowSeconds: number): Uint8Array | null;
|
|
1053
|
+
/** Ed25519 keypair generation → `{ privateKey, publicKey }` (PKCS#8 v1 DER
|
|
1054
|
+
* private + SPKI DER public bytes); throws on CSPRNG failure. */
|
|
1055
|
+
ed25519GenerateKeypair(): {
|
|
1056
|
+
privateKey: Uint8Array;
|
|
1057
|
+
publicKey: Uint8Array;
|
|
1058
|
+
};
|
|
1059
|
+
/** Ed25519 sign → 64-byte signature; throws on invalid private key. */
|
|
1060
|
+
ed25519Sign(msg: Uint8Array, privateKey: Uint8Array): Uint8Array;
|
|
1061
|
+
/** Ed25519 verify → whether `signature` is valid for `msg` under `publicKey`. */
|
|
1062
|
+
ed25519Verify(msg: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean;
|
|
1063
|
+
/** EdDSA (Ed25519) JWT sign → compact token; `null` = invalid claims/private
|
|
1064
|
+
* key. `ttl <= 0` = no `iat`/`exp` injection. */
|
|
1065
|
+
jwtEdDSASign(claimsJson: Uint8Array, privateKey: Uint8Array, ttl: number, nowSeconds: number): string | null;
|
|
1066
|
+
/** EdDSA (Ed25519) JWT verify → claims JSON string; `null` = invalid. */
|
|
1067
|
+
jwtEdDSAVerify(token: Uint8Array, publicKey: Uint8Array, nowSeconds: number): string | null;
|
|
1068
|
+
/** Compiled `TemplateRenderer` → UTF-8 bytes from a pre-serialized JSON
|
|
1069
|
+
* context (opaque `inner` handle); throws on invalid context / render error. */
|
|
1070
|
+
templateRender(inner: number, contextJson: Uint8Array): Uint8Array;
|
|
1071
|
+
/** Compiled `SchemaValidator` → whether a document is valid (opaque `inner` handle). */
|
|
1072
|
+
schemaValidatorValidate(inner: number, doc: Uint8Array): boolean;
|
|
1073
|
+
/** `RateLimiter` string-key check → { allowed, remaining, resetMs } (opaque
|
|
1074
|
+
* `inner` handle; `key` is a `cstring` ARG); throws on a null handle. */
|
|
1075
|
+
rateLimiterCheck(inner: number, key: string, nowMs: number): RateLimiterVerdict;
|
|
1076
|
+
/** `RateLimiter` pre-hashed key check → { allowed, remaining, resetMs }. */
|
|
1077
|
+
rateLimiterCheckKey(inner: number, key: number, nowMs: number): RateLimiterVerdict;
|
|
1078
|
+
/** `byteLen` random bytes → `byteLen * 2` hex-char string (cstring return). */
|
|
1079
|
+
randomToken(byteLen: number): string;
|
|
1080
|
+
/** base64-encode → string. */
|
|
1081
|
+
base64Encode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): string;
|
|
1082
|
+
/** base64-encode into `output`; returns bytes written. */
|
|
1083
|
+
base64EncodeInto(input: Uint8Array, output: Uint8Array, urlSafe?: boolean, padding?: boolean): number;
|
|
1084
|
+
/** HMAC-SHA256 → 64-char lowercase-hex string. */
|
|
1085
|
+
hmacSha256(key: Uint8Array, data: Uint8Array): string;
|
|
1086
|
+
/** HMAC-SHA256 hex into `output`; returns bytes written (throws if too small). */
|
|
1087
|
+
hmacSha256Into(key: Uint8Array, data: Uint8Array, output: Uint8Array): number;
|
|
1088
|
+
/** Sign cookie `value` as `value.<64-hex>` → string (cstring return). */
|
|
1089
|
+
signCookie(value: Uint8Array, secret: Uint8Array): string;
|
|
1090
|
+
/** Sign cookie into `output`; returns bytes written (throws if too small). */
|
|
1091
|
+
signCookieInto(value: Uint8Array, secret: Uint8Array, output: Uint8Array): number;
|
|
1092
|
+
/** Verify a signed cookie → value string, or `null` on bad signature. */
|
|
1093
|
+
verifyCookie(signed: Uint8Array, secret: Uint8Array): string | null;
|
|
1094
|
+
/** Verify a signed cookie into `output`; returns value length or `null` on bad signature. */
|
|
1095
|
+
verifyCookieInto(signed: Uint8Array, secret: Uint8Array, output: Uint8Array): number | null;
|
|
1096
|
+
/** CSRF token (129 chars: 64 rnd-hex + '.' + 64 sig-hex) → string. */
|
|
1097
|
+
csrfToken(secret: Uint8Array): string;
|
|
1098
|
+
/** CSRF token into `output`; returns bytes written (throws if too small). */
|
|
1099
|
+
csrfTokenInto(secret: Uint8Array, output: Uint8Array): number;
|
|
1100
|
+
/** Argon2id PHC hash → string (m/t/p/out_len params). */
|
|
1101
|
+
passwordHash(password: Uint8Array, salt: Uint8Array, mCost: number, tCost: number, pCost: number, outLen: number): string;
|
|
1102
|
+
/** bcrypt `$2b$` PHC → string (cost clamped 4..=31). */
|
|
1103
|
+
passwordHashBcrypt(password: Uint8Array, cost: number): string;
|
|
1104
|
+
/** PBKDF2-HMAC-SHA256 → `dkLen` bytes (rounds, dkLen clamped). */
|
|
1105
|
+
pbkdf2Sha256(password: Uint8Array, salt: Uint8Array, rounds: number, dkLen: number): Uint8Array;
|
|
1106
|
+
/** AEAD encrypt (0 = AES-256-GCM, 1 = ChaCha20-Poly1305) → ct+tag. */
|
|
1107
|
+
aeadEncrypt(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, algorithm?: number): Uint8Array;
|
|
1108
|
+
/** AEAD encrypt into `output` (ct + 16-byte tag); returns bytes written. */
|
|
1109
|
+
aeadEncryptInto(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, output: Uint8Array, algorithm?: number): number;
|
|
1110
|
+
/** AEAD decrypt → plaintext, or `null` on auth failure. */
|
|
1111
|
+
aeadDecrypt(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, algorithm?: number): Uint8Array | null;
|
|
1112
|
+
/** RFC 6455 frame encode into a fresh buffer. */
|
|
1113
|
+
wsFrameEncode(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean): Uint8Array;
|
|
1114
|
+
/** RFC 6455 frame encode into `output`; returns bytes written (throws if too small). */
|
|
1115
|
+
wsFrameEncodeInto(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean, output: Uint8Array): number;
|
|
1116
|
+
/** RFC 6902 JSON patch → JSON text string. */
|
|
1117
|
+
jsonPatch(doc: Uint8Array, patch: Uint8Array): string;
|
|
1118
|
+
/** gzip-compress into a fresh buffer (level clamped 0..=9, default 6). */
|
|
1119
|
+
gzipCompress(data: Uint8Array, level?: number): Uint8Array;
|
|
1120
|
+
/** gzip-compress into `output`; returns bytes written (throws if too small). */
|
|
1121
|
+
gzipCompressInto(data: Uint8Array, output: Uint8Array, level?: number): number;
|
|
1122
|
+
/** gzip-decompress into a fresh buffer (capped by `maxDecompressed`). */
|
|
1123
|
+
gzipDecompress(data: Uint8Array, maxDecompressed?: number): Uint8Array;
|
|
1124
|
+
/** gzip-decompress into `output`; returns bytes written (throws if too small). */
|
|
1125
|
+
gzipDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number): number;
|
|
1126
|
+
/**
|
|
1127
|
+
* gzip ISIZE trailer probe → the uncompressed size for a single-member gzip
|
|
1128
|
+
* stream, or `0` when unknown (invalid stream / multi-member). Used to
|
|
1129
|
+
* PRE-SIZE a destination buffer for the zero-copy task path.
|
|
1130
|
+
*/
|
|
1131
|
+
gzipIsize(data: Uint8Array): number;
|
|
1132
|
+
/** brotli-compress into a fresh buffer (quality clamped 0..=11, default 5). */
|
|
1133
|
+
brotliCompress(data: Uint8Array, quality?: number): Uint8Array;
|
|
1134
|
+
/** brotli-compress into `output`; returns bytes written (throws if too small). */
|
|
1135
|
+
brotliCompressInto(data: Uint8Array, output: Uint8Array, quality?: number): number;
|
|
1136
|
+
/** brotli-decompress into a fresh buffer (capped by `maxDecompressed`). */
|
|
1137
|
+
brotliDecompress(data: Uint8Array, maxDecompressed?: number): Uint8Array;
|
|
1138
|
+
/** brotli-decompress into `output`; returns bytes written (throws if too small). */
|
|
1139
|
+
brotliDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number): number;
|
|
1140
|
+
/** HTTP request parse → packed output into `output`; returns bytes written. */
|
|
1141
|
+
httpParseRequestPackedInto(input: Uint8Array, output: Uint8Array): number;
|
|
1142
|
+
/** Query string parse → packed output into `output`; returns bytes written. */
|
|
1143
|
+
queryParsePackedInto(input: Uint8Array, output: Uint8Array): number;
|
|
1144
|
+
/** Cookie header parse → packed output into `output`; returns bytes written. */
|
|
1145
|
+
cookieParsePackedInto(input: Uint8Array, output: Uint8Array): number;
|
|
1146
|
+
/** Sign JWT (HS256) from pre-serialized claim JSON (ttl<=0 → no iat/exp) → string. */
|
|
1147
|
+
jwtSignBytes(claimsJson: Uint8Array, secret: Uint8Array, ttl: number, now: number): string;
|
|
1148
|
+
/** Sign JWT into `output`; returns bytes written (throws if too small). */
|
|
1149
|
+
jwtSignBytesInto(claimsJson: Uint8Array, secret: Uint8Array, ttl: number, now: number, output: Uint8Array): number;
|
|
1150
|
+
/** Decode a WS frame into packed `[flags][opcode][u32 len][payload]`; null on malformed. */
|
|
1151
|
+
wsFrameDecodePacked(data: Uint8Array): Uint8Array | null;
|
|
1152
|
+
/**
|
|
1153
|
+
* Decode a WS frame into `output` (sized ≥ `data.length + 6`); returns bytes
|
|
1154
|
+
* written, null on malformed. Pooled sibling of `wsFrameDecodePacked`.
|
|
1155
|
+
*/
|
|
1156
|
+
wsFrameDecodePackedInto(data: Uint8Array, output: Uint8Array): number | null;
|
|
1157
|
+
/** Parse multipart/form-data into the packed parts layout. */
|
|
1158
|
+
multipartParsePacked(body: Uint8Array, boundary: Uint8Array): Uint8Array;
|
|
1159
|
+
/** Parse multipart/form-data into `output`; returns bytes written (throws if too small). */
|
|
1160
|
+
multipartParsePackedInto(body: Uint8Array, boundary: Uint8Array, output: Uint8Array): number;
|
|
1161
|
+
/** Parse x-www-form-urlencoded into packed pairs into `output`. */
|
|
1162
|
+
formParsePackedInto(input: Uint8Array, output: Uint8Array): number;
|
|
1163
|
+
/**
|
|
1164
|
+
* Verify an HS256 JWT → claims JSON text (cstring return; `null` on invalid
|
|
1165
|
+
* signature / expired / malformed). `nowSeconds` is an i64 C arg.
|
|
1166
|
+
*/
|
|
1167
|
+
jwtVerify(token: Uint8Array, secret: Uint8Array, nowSeconds: number): string | null;
|
|
1168
|
+
/**
|
|
1169
|
+
* Parse JSON → packed token stream bytes (structural, no re-parse). Layout:
|
|
1170
|
+
* `[u32 strCount]{[u32 len][utf8]}... [u32 treeLen][tree]` — a deduplicated
|
|
1171
|
+
* string table followed by a start/end-marker value tree:
|
|
1172
|
+
* `0=null 1=false 2=true 3=number(f64 LE) 4=string(u32 table idx)
|
|
1173
|
+
* 5=array start 6=object start 7=array end 8=object end
|
|
1174
|
+
* 9=object key(u32 table idx)` (object body `6,(9,keyIdx,value)*,8`).
|
|
1175
|
+
* Throws on invalid JSON / too-small output. The scalar wrapper decodes the
|
|
1176
|
+
* table once and assembles the value — no second parse.
|
|
1177
|
+
*/
|
|
1178
|
+
jsonParsePacked(input: Uint8Array): Uint8Array;
|
|
1179
|
+
/**
|
|
1180
|
+
* Parse a Content-Type header → packed verdict bytes:
|
|
1181
|
+
* `[u32 mediaTypeLen][mediaType][u32 charsetLen (0xFFFFFFFF = none)][charset]
|
|
1182
|
+
* [u32 boundaryLen][boundary][u32 paramCount]{[u32 keyLen][key][u32 valLen][val]}`.
|
|
1183
|
+
* Throws on invalid media type / too-small output.
|
|
1184
|
+
*/
|
|
1185
|
+
parseMediaType(input: Uint8Array): Uint8Array;
|
|
1186
|
+
/** Parse an IMF-fixdate → unix seconds, or `null` on malformed (packed
|
|
1187
|
+
* `[u8 ok][i64 secs LE]` output, decoded here). */
|
|
1188
|
+
parseHttpDate(input: Uint8Array): bigint | null;
|
|
1189
|
+
/**
|
|
1190
|
+
* Parse an Accept-Encoding header → packed verdict bytes:
|
|
1191
|
+
* `[u32 count]{[u32 encLen][enc][f32 q][u32 order]}` (empty header → count 0).
|
|
1192
|
+
* Throws on too-small output.
|
|
1193
|
+
*/
|
|
1194
|
+
parseAcceptEncoding(input: Uint8Array): Uint8Array;
|
|
1195
|
+
/** Percent-encode a query from packed pairs (the `packPairs` layout) → string,
|
|
1196
|
+
* keys SORTED (matches napi `BTreeMap` ordering); `null` on malformed input. */
|
|
1197
|
+
urlEncodeQuery(input: Uint8Array): string | null;
|
|
1198
|
+
/** RFC 3986 URL resolution (base + reference) → string; `null` on non-UTF-8. */
|
|
1199
|
+
urlResolve(base: Uint8Array, reference: Uint8Array): string | null;
|
|
1200
|
+
/** Resolve a reference against a `UrlBuilder`'s PRECOMPILED base (opaque
|
|
1201
|
+
* `inner` handle from its napi `inner_ptr()`) → bytes; throws on a null
|
|
1202
|
+
* handle / non-UTF-8 reference / too-small output. */
|
|
1203
|
+
urlBuilderResolve(inner: number, reference: Uint8Array): Uint8Array;
|
|
1204
|
+
/** Extension → MIME type → string (unknown → `application/octet-stream`; `ext` is a `cstring` ARG). */
|
|
1205
|
+
mimeFromExtension(ext: string): string | null;
|
|
1206
|
+
/**
|
|
1207
|
+
* 29-byte HTTP-date (`Sun, 06 Nov 1994 08:49:37 GMT`) into `output`; returns
|
|
1208
|
+
* bytes written (throws on too-small / out-of-range year). FFI sibling of the
|
|
1209
|
+
* napi `httpDateInto` — kills the napi crossing on the hot path.
|
|
1210
|
+
*/
|
|
1211
|
+
httpDateInto(secs: number, output: Uint8Array): number;
|
|
1212
|
+
/** 29-byte HTTP-date → string (allocating sibling of httpDateInto). */
|
|
1213
|
+
httpDate(secs?: number): string;
|
|
1214
|
+
/**
|
|
1215
|
+
* Encode one SSE event → bytes (fresh buffer). event/id null = line omitted
|
|
1216
|
+
* (a present-but-empty string emits the line, matching napi `Option`).
|
|
1217
|
+
* NOTE: event/id stay `(ptr,len)` byte args — a cstring-ARG conversion was
|
|
1218
|
+
* attempted and reverted (2026-08-23); see docs/FFI_BUN_GUIDE.md §14 before
|
|
1219
|
+
* touching this signature.
|
|
1220
|
+
* FFI sibling of the napi `sse_encode_event`.
|
|
1221
|
+
*/
|
|
1222
|
+
sseEncodeEvent(event: string | null, data: Uint8Array, id: string | null, retry: number | null): Uint8Array;
|
|
1223
|
+
/**
|
|
1224
|
+
* Encode one SSE event into `output` (sized ≥ `data.length + 64`); returns
|
|
1225
|
+
* bytes written (throws if too small / invalid UTF-8). Pooled sibling of
|
|
1226
|
+
* `sseEncodeEvent` — removes the per-call output-buffer alloc.
|
|
1227
|
+
*/
|
|
1228
|
+
sseEncodeEventInto(event: string | null, data: Uint8Array, id: string | null, retry: number | null, output: Uint8Array): number;
|
|
1229
|
+
/** Random hex token into `output`; returns bytes written (throws if too small). */
|
|
1230
|
+
randomTokenInto(byteLen: number, output: Uint8Array): number;
|
|
1231
|
+
/**
|
|
1232
|
+
* Run the ingress pipeline on a packed frame via the opaque inner handle from
|
|
1233
|
+
* `Ingress.ingressInnerPtr()` (valid only while the instance is alive — the
|
|
1234
|
+
* caller must hold it). Returns bytes written; throws on error / too-small.
|
|
1235
|
+
*/
|
|
1236
|
+
ingressHandlePacked(inner: number, input: Uint8Array, body: Uint8Array | null, output: Uint8Array): number;
|
|
1237
|
+
/**
|
|
1238
|
+
* Run the ingress pipeline from raw request components via the opaque inner
|
|
1239
|
+
* handle (valid only while the instance is alive). `url`/`ip` are JS strings
|
|
1240
|
+
* passed as `bun:ffi` `cstring` ARGs — the engine transcodes them to
|
|
1241
|
+
* call-scoped NUL-terminated UTF-8 buffers in-engine, so the JS side skips
|
|
1242
|
+
* the frame assembly + `Buffer.write` encode for the URL/IP. `rid`/`headers`
|
|
1243
|
+
* are the packed byte slices; `body` is the request body (or null); `output`
|
|
1244
|
+
* receives the packed decision. Same wire format as {@link ingressHandlePacked}.
|
|
1245
|
+
* Returns bytes written; throws on error / too-small.
|
|
1246
|
+
*/
|
|
1247
|
+
ingressHandleComponents(inner: number, methodKind: number, url: string, ip: string, rid: Uint8Array, headers: Uint8Array, body: Uint8Array | null, output: Uint8Array): number;
|
|
1248
|
+
/**
|
|
1249
|
+
* Write the ingress binary-layout constants (38 × u32 LE — `rust/ffi.rs`
|
|
1250
|
+
* `IngressLayout`, numeric source `rust/ingress/output.rs`) into `output`;
|
|
1251
|
+
* returns bytes written. Lets `src/ingress/constants.ts` read the layout via
|
|
1252
|
+
* bun:ffi on Bun so importing the package does NOT dlopen the napi addon.
|
|
1253
|
+
*/
|
|
1254
|
+
ingressLayout(out: Uint8Array): number;
|
|
1255
|
+
/**
|
|
1256
|
+
* Compile a route descriptor (the `@ignex/native` route-wire v3 format:
|
|
1257
|
+
* magic `ROUT`, version 3, limits, stage tags, draft-07 body schema) into an
|
|
1258
|
+
* opaque handle. Returns the handle (`0` = invalid descriptor / compile
|
|
1259
|
+
* failure / panic — throws). The handle is owned by the caller and must be
|
|
1260
|
+
* released with {@link routeDestroy}.
|
|
1261
|
+
*/
|
|
1262
|
+
routeCompile(descriptor: Uint8Array): number;
|
|
1263
|
+
/**
|
|
1264
|
+
* Run a compiled route on one request frame, writing the packed verdict
|
|
1265
|
+
* (`[flags u32][errorCode u32]` + optional query/cookie pair sections) into
|
|
1266
|
+
* `output`. Returns bytes written (`0` = real error — throws; `> output.length`
|
|
1267
|
+
* = the EXACT required size — the caller allocates once and retries, per the
|
|
1268
|
+
* growExact convention).
|
|
1269
|
+
*/
|
|
1270
|
+
routeRun(handle: number, frame: Uint8Array, output: Uint8Array): number;
|
|
1271
|
+
/** Destroy a compiled route handle (frees the native `Box<NativeRoute>`). */
|
|
1272
|
+
routeDestroy(handle: number): void;
|
|
1273
|
+
/** Create an empty metrics registry → a caller-owned opaque handle. */
|
|
1274
|
+
metricsCreate(): number;
|
|
1275
|
+
/**
|
|
1276
|
+
* Declare a counter family → its `u32` series id (`0xFFFFFFFF` = error).
|
|
1277
|
+
* `name` and `labelKeys` are `cstring` ARGs; label keys are
|
|
1278
|
+
* `\u001f`-separated (empty string = no labels).
|
|
1279
|
+
*/
|
|
1280
|
+
metricsCounter(handle: number, name: string, labelKeys: string): number;
|
|
1281
|
+
/** Declare a gauge family → its series id. Same ABI as {@link metricsCounter}. */
|
|
1282
|
+
metricsGauge(handle: number, name: string, labelKeys: string): number;
|
|
1283
|
+
/**
|
|
1284
|
+
* Declare a histogram family → its series id. `bucketsCsv` is a
|
|
1285
|
+
* comma-separated finite positive list (empty string = default buckets).
|
|
1286
|
+
*/
|
|
1287
|
+
metricsHistogram(handle: number, name: string, labelKeys: string, bucketsCsv: string): number;
|
|
1288
|
+
/**
|
|
1289
|
+
* Counter/gauge `+= amount`, or histogram observe `amount`. `values` is the
|
|
1290
|
+
* packed `\u001f`-separated label values. Returns true on success.
|
|
1291
|
+
*/
|
|
1292
|
+
metricsRecord(handle: number, series: number, values: Uint8Array, amount: number): boolean;
|
|
1293
|
+
/** Gauge assignment (`= value`). Returns true on success. */
|
|
1294
|
+
metricsGaugeSet(handle: number, series: number, values: Uint8Array, value: number): boolean;
|
|
1295
|
+
/**
|
|
1296
|
+
* Zero-encode sibling of {@link metricsRecord}: `values` is the JOINED
|
|
1297
|
+
* `\u001f`-separated JS string — the engine transcodes it in-engine.
|
|
1298
|
+
*/
|
|
1299
|
+
metricsRecordStr(handle: number, series: number, values: string, amount: number): boolean;
|
|
1300
|
+
/** Zero-encode sibling of {@link metricsGaugeSet} (joined string values). */
|
|
1301
|
+
metricsGaugeSetStr(handle: number, series: number, values: string, value: number): boolean;
|
|
1302
|
+
/**
|
|
1303
|
+
* Render the Prometheus text format into `output`; returns bytes written
|
|
1304
|
+
* (`0` = null handle / error; `> output.length` = exact required size).
|
|
1305
|
+
*/
|
|
1306
|
+
metricsRender(handle: number, output: Uint8Array): number;
|
|
1307
|
+
/**
|
|
1308
|
+
* Dump the packed v1 series snapshot into `output`; returns bytes written
|
|
1309
|
+
* (`0` = null handle / error; `> output.length` = exact required size).
|
|
1310
|
+
*/
|
|
1311
|
+
metricsSnapshot(handle: number, output: Uint8Array): number;
|
|
1312
|
+
/** Destroy a registry handle (frees the native `Box<MetricsRegistry>`). */
|
|
1313
|
+
metricsDestroy(handle: number): void;
|
|
1314
|
+
/**
|
|
1315
|
+
* Record a BATCH of events in one crossing. Packed layout:
|
|
1316
|
+
* `[u32 n]{[u32 series][u32 valsLen][vals][f64 amount]}`; returns true when
|
|
1317
|
+
* all entries applied.
|
|
1318
|
+
*/
|
|
1319
|
+
metricsRecordBatch(handle: number, packed: Uint8Array): boolean;
|
|
1320
|
+
/**
|
|
1321
|
+
* Fused wire-level validation: RAW query string → JSON → draft-07 validate
|
|
1322
|
+
* against the compiled schema (`inner` from `SchemaValidator.innerPtr()`).
|
|
1323
|
+
*/
|
|
1324
|
+
queryValidate(inner: number, qs: string): boolean;
|
|
1325
|
+
/** Fused cookie-header variant of {@link queryValidate}. */
|
|
1326
|
+
cookieValidate(inner: number, header: string): boolean;
|
|
1327
|
+
/**
|
|
1328
|
+
* Seal a session envelope natively: builds
|
|
1329
|
+
* `{"id":"…","data":<dataJson>,"exp":exp}` and HMAC-signs it →
|
|
1330
|
+
* `payload.<hex>` token string (`null` = failure).
|
|
1331
|
+
*/
|
|
1332
|
+
sessionSeal(id: string, dataJson: string, expSecs: number, secret: string): string | null;
|
|
1333
|
+
/**
|
|
1334
|
+
* Open a sealed session token: verify + extract in one crossing into
|
|
1335
|
+
* `output` as `[u8 ok][i64 exp][u32 idLen][id][u32 dataLen][dataJson]`.
|
|
1336
|
+
* Returns bytes written (`0` = bad signature / error; `> output.length` =
|
|
1337
|
+
* exact required size).
|
|
1338
|
+
*/
|
|
1339
|
+
sessionOpen(token: string, secret: string, output: Uint8Array): number;
|
|
1340
|
+
/**
|
|
1341
|
+
* Byte-arg `{@link sessionSeal}`: same envelope + wire format, inputs as
|
|
1342
|
+
* bytes. Preferred for user-derived `id`/`dataJson`, because a `cstring` ARG
|
|
1343
|
+
* is engine-transcoded (a per-arg cost) and SILENTLY TRUNCATES at an
|
|
1344
|
+
* embedded `U+0000` before Rust sees it. `null` = failure.
|
|
1345
|
+
*/
|
|
1346
|
+
sessionSealBytes(id: Uint8Array, dataJson: Uint8Array, expSecs: number, secret: Uint8Array): string | null;
|
|
1347
|
+
/**
|
|
1348
|
+
* Byte-arg `{@link sessionOpen}`: same output layout and needed-size
|
|
1349
|
+
* convention, `token`/`secret` as bytes (exact length, no transcode).
|
|
1350
|
+
*/
|
|
1351
|
+
sessionOpenBytes(token: Uint8Array, secret: Uint8Array, output: Uint8Array): number;
|
|
1352
|
+
/**
|
|
1353
|
+
* Validate NEWLINE-separated lines as fixed-width hex into `output`
|
|
1354
|
+
* (one verdict byte per line); returns bytes written (`0` = bad width /
|
|
1355
|
+
* error; `> output.length` = exact required size).
|
|
1356
|
+
*/
|
|
1357
|
+
hexValidateBatchInto(input: Uint8Array, width: number, output: Uint8Array): number;
|
|
1358
|
+
/**
|
|
1359
|
+
* String-input sibling of {@link hexValidateBatchInto}: the NEWLINE-
|
|
1360
|
+
* separated ids cross as a `cstring` ARG (engine-transcoded — no encode).
|
|
1361
|
+
*/
|
|
1362
|
+
hexValidateBatchStr(ids: string, width: number, output: Uint8Array): number;
|
|
1363
|
+
/**
|
|
1364
|
+
* Escape JS-RegExp metacharacters into `output`; returns bytes written
|
|
1365
|
+
* (`0` = error; `> output.length` = exact required size).
|
|
1366
|
+
*/
|
|
1367
|
+
regexEscapeInto(input: Uint8Array, output: Uint8Array): number;
|
|
1368
|
+
/**
|
|
1369
|
+
* Zero-copy text sibling of {@link regexEscapeInto}: cstring ARG in,
|
|
1370
|
+
* cstring return out (engine-cloned — zero JS decode). `null` = failure.
|
|
1371
|
+
*/
|
|
1372
|
+
regexEscapeStr(input: string): string | null;
|
|
1373
|
+
/** Start the shared native task pool; 0 = started, 1 = already running. */
|
|
1374
|
+
taskInit(threads: number): number;
|
|
1375
|
+
/** Submit a task op with packed `args` under `taskId`; 1 = accepted. */
|
|
1376
|
+
taskSubmit(op: number, args: Uint8Array, taskId: number): number;
|
|
1377
|
+
/**
|
|
1378
|
+
* Zero-copy submit: the op writes its result DIRECTLY into `output` on the
|
|
1379
|
+
* pool thread. `output` must be a stable, caller-owned buffer that stays
|
|
1380
|
+
* alive (and untouched) until the completion is drained — the runtime holds
|
|
1381
|
+
* the reference. The completion body is the 8-byte LE written length.
|
|
1382
|
+
*/
|
|
1383
|
+
taskSubmitOut(op: number, args: Uint8Array, taskId: number, output: Uint8Array): number;
|
|
1384
|
+
/**
|
|
1385
|
+
* Zero-copy INPUT: only `hdr` is copied; `data` is read in place by the pool
|
|
1386
|
+
* thread, so the caller must keep it alive (and untouched) until completion.
|
|
1387
|
+
*/
|
|
1388
|
+
taskSubmitSlice(op: number, hdr: Uint8Array, data: Uint8Array, taskId: number): number;
|
|
1389
|
+
/**
|
|
1390
|
+
* Drain finished tasks into `output` (packed `[u32 count][entry…]`).
|
|
1391
|
+
* Returns bytes written, `0` when nothing is pending, or the EXACT required
|
|
1392
|
+
* size when `output` is too small (nothing is consumed in that case).
|
|
1393
|
+
*/
|
|
1394
|
+
taskDrain(output: Uint8Array): number;
|
|
1395
|
+
/** Finished-but-undrained completion count. */
|
|
1396
|
+
taskPending(): number;
|
|
1397
|
+
/** Mark a task cancelled; 1 = newly marked, 0 = unknown/already finished. */
|
|
1398
|
+
taskCancel(taskId: number): number;
|
|
1399
|
+
/** Register the thread-safe doorbell trampoline address (`0` disables it). */
|
|
1400
|
+
taskSetDoorbell(ptr: number): number;
|
|
1401
|
+
/** Stop the task pool and drop pending completions. */
|
|
1402
|
+
taskShutdown(): number;
|
|
1403
|
+
/** Configured task-pool worker count (0 before the pool starts). */
|
|
1404
|
+
taskThreads(): number;
|
|
1405
|
+
}
|
|
1406
|
+
/** Unpacked rate-limit verdict (from the packed
|
|
1407
|
+
* `[u8 allowed][u32 remaining LE][i64 reset_ms LE]` C-ABI output). */
|
|
1408
|
+
export interface RateLimiterVerdict {
|
|
1409
|
+
allowed: boolean;
|
|
1410
|
+
remaining: number;
|
|
1411
|
+
resetMs: number;
|
|
1412
|
+
}
|
|
952
1413
|
/**
|
|
953
1414
|
* Create the runtime-appropriate ingress server: `Bun.serve` on Bun, the
|
|
954
1415
|
* `node:http` adapter on Node. Selected ONCE at module load — the branch
|
|
@@ -1227,7 +1688,7 @@ export declare function readHandler(ingress: OptimizedIngressHandler, opts?: Bak
|
|
|
1227
1688
|
export declare const deleteHandler: typeof readHandler;
|
|
1228
1689
|
/**
|
|
1229
1690
|
* Pre-baked echo handler: streams the request body back with the client's
|
|
1230
|
-
* Content-Type, bounded by `maxBodyBytes`.
|
|
1691
|
+
* Content-Type (coerced to a non-active type), bounded by `maxBodyBytes`.
|
|
1231
1692
|
*/
|
|
1232
1693
|
export declare function echoHandler(ingress: OptimizedIngressHandler, opts?: BakedHandlerOptions): (req: Request, srv?: unknown) => Promise<Response>;
|
|
1233
1694
|
/**
|
|
@@ -1586,6 +2047,23 @@ export declare function createIngressSync(options?: IngressOptions): SyncIngress
|
|
|
1586
2047
|
* @returns an async handler returning a snapshot context
|
|
1587
2048
|
*/
|
|
1588
2049
|
export declare function createIngress(options?: IngressOptions): IngressHandler;
|
|
2050
|
+
/**
|
|
2051
|
+
* Synchronous ingress writers backed by castrum's process-lifetime FFI binding.
|
|
2052
|
+
* Handles MUST originate from addonPath and remain owned/alive while in use.
|
|
2053
|
+
* Writers throw on native failure; a return larger than output.byteLength is
|
|
2054
|
+
* the exact required capacity, not a completed write. No buffer is retained.
|
|
2055
|
+
*/
|
|
2056
|
+
export type IngressBinding = Pick<BunFFI, "ingressHandleComponents" | "ingressHandlePacked" | "ingressLayout"> & {
|
|
2057
|
+
readonly addonPath: string;
|
|
2058
|
+
};
|
|
2059
|
+
/**
|
|
2060
|
+
* Share castrum's self-tested ingress writers without a second dlopen/map.
|
|
2061
|
+
* @returns The binding and actual binary path, or null when FFI is unavailable.
|
|
2062
|
+
* @example
|
|
2063
|
+
* const binding = getIngressBinding()
|
|
2064
|
+
* // Verify binding.addonPath matches the addon that owns your ingress handle.
|
|
2065
|
+
*/
|
|
2066
|
+
export declare function getIngressBinding(): IngressBinding | null;
|
|
1589
2067
|
/** Parse a query string (`a=1&b=2`) into an object via the native parser. */
|
|
1590
2068
|
export declare function parseQueryString(query: string): Record<string, string | string[]>;
|
|
1591
2069
|
/** Parse a cookie header (`a=1; b=2`) into an object via the native parser. */
|
|
@@ -2513,8 +2991,23 @@ export interface RustBatch {
|
|
|
2513
2991
|
wsAcceptKey(items: Uint8Array[]): Uint8Array[];
|
|
2514
2992
|
base64UrlEncode(items: Uint8Array[]): Uint8Array[];
|
|
2515
2993
|
base64UrlDecode(items: Uint8Array[]): Uint8Array[];
|
|
2994
|
+
/**
|
|
2995
|
+
* Parallel password hashing. WARNING: the SAME `salt` is applied to every
|
|
2996
|
+
* item. Reusing one salt across real user passwords enables a single
|
|
2997
|
+
* precomputation to attack them all — generate a unique salt per password in
|
|
2998
|
+
* production (this batch form is for precomputed/benchmark workloads).
|
|
2999
|
+
*/
|
|
2516
3000
|
passwordHash(passwords: Uint8Array[], salt: Uint8Array, options?: PasswordHashOptions | null): Uint8Array[];
|
|
3001
|
+
/**
|
|
3002
|
+
* Parallel AEAD encrypt. `nonce` MUST be exactly 12 bytes and MUST be fresh
|
|
3003
|
+
* for every batch call with the same key: a unique per-item nonce is derived
|
|
3004
|
+
* by XOR-ing the item index into the low 8 bytes, so reusing a base nonce
|
|
3005
|
+
* across calls reuses keystream/tag material for colliding indices
|
|
3006
|
+
* (catastrophic for AES-GCM / ChaCha20-Poly1305). A non-12-byte nonce is
|
|
3007
|
+
* rejected with an error.
|
|
3008
|
+
*/
|
|
2517
3009
|
aeadEncrypt(key: Uint8Array, nonce: Uint8Array, items: Uint8Array[], algorithm?: string | null): Uint8Array[];
|
|
3010
|
+
/** Parallel AEAD decrypt. Pass the SAME base nonce and item order as the encrypt call. */
|
|
2518
3011
|
aeadDecrypt(key: Uint8Array, nonce: Uint8Array, items: Uint8Array[], algorithm?: string | null): Uint8Array[];
|
|
2519
3012
|
gzipCompress(items: Uint8Array[], level?: number | null): Uint8Array[];
|
|
2520
3013
|
gzipDecompress(items: Uint8Array[], maxDecompressed?: number | null): Uint8Array[];
|
|
@@ -2781,7 +3274,10 @@ export interface RustScalar {
|
|
|
2781
3274
|
createMediaTypeMatcher(expected: Uint8Array): MediaTypeMatcherInstance;
|
|
2782
3275
|
/**
|
|
2783
3276
|
* Sharded fixed-window per-key rate limiter. `maxEntries` clamps internally
|
|
2784
|
-
* (default 1,048,576)
|
|
3277
|
+
* (default 1,048,576) and has an effective floor of 16,384
|
|
3278
|
+
* (`max_per_shard = max(maxEntries / 256, 64)` × 256 shards) — values below
|
|
3279
|
+
* that are silently raised, so don't rely on it to bound a tiny key space.
|
|
3280
|
+
* Each instance owns an independent budget.
|
|
2785
3281
|
* @remarks Scalar per-check cost: ~3-14x SLOWER than the JS Map baseline —
|
|
2786
3282
|
* prefer the ingress pipeline (one FFI for all stages) for native rate
|
|
2787
3283
|
* limiting in a request path. [measured 2026-08]
|