castrum 0.7.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +154 -368
- package/castrum.linux-x64-gnu.node +0 -0
- package/dist/index.d.ts +2763 -0
- package/dist/index.js +8707 -0
- package/docs/API.md +337 -0
- package/docs/ARCHITECTURE.md +507 -0
- package/docs/BENCHMARKS.md +343 -0
- package/docs/CASE_STUDY.md +240 -0
- package/docs/ENVIRONMENT.md +81 -0
- package/docs/FFI_BUN_GUIDE.md +327 -0
- package/docs/GETTING_STARTED.md +192 -0
- package/docs/INGRESS-ROUTER.md +81 -0
- package/docs/INGRESS.md +746 -0
- package/docs/NATIVE-ROUTE.md +53 -0
- package/docs/REPO_MAP.md +279 -0
- package/docs/adr/0001-two-wire-formats.md +39 -0
- package/docs/adr/0002-zero-dom-fast-schema.md +38 -0
- package/docs/adr/0003-bun-builtins-delegation.md +43 -0
- package/docs/bun-builtins-decision-matrix.md +115 -0
- package/index.ts +44 -8
- package/package.json +98 -33
- package/src/ingress/body.ts +289 -0
- package/src/ingress/constants.ts +220 -48
- package/src/ingress/context.ts +136 -0
- package/src/ingress/decode/baked-result.ts +121 -0
- package/src/ingress/decode/fast-result.ts +160 -0
- package/src/ingress/decode/packed-sections.ts +56 -0
- package/src/ingress/decode/result-base.ts +172 -0
- package/src/ingress/errors.ts +72 -0
- package/src/ingress/fast.ts +152 -1021
- package/src/ingress/handlers.ts +639 -1001
- package/src/ingress/headers/baked-templates.ts +102 -0
- package/src/ingress/headers/cors.ts +44 -0
- package/src/ingress/headers/fast-templates.ts +238 -0
- package/src/ingress/headers/hsts.ts +83 -0
- package/src/ingress/health.ts +68 -0
- package/src/ingress/index.ts +142 -405
- package/src/ingress/metrics.ts +132 -0
- package/src/ingress/options.ts +197 -0
- package/src/ingress/packing/gather-raw-headers.ts +139 -0
- package/src/ingress/packing/header-packing.ts +75 -0
- package/src/ingress/packing/input-packer.ts +164 -0
- package/src/ingress/packing/scratch.ts +50 -0
- package/src/ingress/packing/select-headers.ts +102 -0
- package/src/ingress/response/error-bodies.ts +75 -0
- package/src/ingress/response/terminal.ts +59 -0
- package/src/ingress/router.ts +237 -0
- package/src/ingress/routes/common.ts +79 -0
- package/src/ingress/routes/delete.ts +14 -0
- package/src/ingress/routes/echo.ts +107 -0
- package/src/ingress/routes/fallback.ts +23 -0
- package/src/ingress/routes/head.ts +27 -0
- package/src/ingress/routes/index.ts +11 -0
- package/src/ingress/routes/json-write.ts +129 -0
- package/src/ingress/routes/options.ts +26 -0
- package/src/ingress/routes/read.ts +35 -0
- package/src/ingress/routes/responder.ts +159 -0
- package/src/ingress/server-node.ts +376 -0
- package/src/ingress/server.ts +475 -0
- package/src/ingress/shared.ts +132 -0
- package/src/ingress/status.ts +97 -0
- package/src/ingress/types.ts +191 -0
- package/src/integration/batch.ts +58 -0
- package/src/integration/index.ts +29 -0
- package/src/integration/pipeline.ts +220 -0
- package/src/integration/streaming.ts +78 -0
- package/src/integration/websocket.ts +93 -0
- package/src/loader/batch.ts +137 -0
- package/src/loader/cost.ts +105 -0
- package/src/loader/index.ts +745 -0
- package/src/loader/ops.ts +657 -0
- package/src/loader/state.ts +84 -0
- package/src/loader/types.ts +145 -0
- package/src/native/ffi/constants.ts +58 -0
- package/src/native/ffi/selftest.ts +729 -0
- package/src/native/ffi/types.ts +474 -0
- package/src/native/ffi.ts +1775 -0
- package/src/native/index.ts +8 -207
- package/src/native/loader.ts +240 -0
- package/src/native/types/instances.ts +274 -0
- package/src/native/types.ts +401 -0
- package/src/runtime/builtins.ts +131 -0
- package/src/runtime/codec.ts +147 -0
- package/src/runtime/detect.ts +36 -0
- package/src/runtime/index.ts +28 -0
- package/src/runtime/native.ts +35 -0
- package/src/runtime/server.ts +31 -0
- package/src/runtime/transport.ts +64 -0
- package/src/runtime/types.ts +173 -0
- package/src/runtime/uuid.ts +45 -0
- package/src/runtime/websocket.ts +30 -0
- package/src/rust-ffi/addon.ts +10 -0
- package/src/rust-ffi/batch/build.ts +155 -0
- package/src/rust-ffi/batch/index.ts +8 -0
- package/src/rust-ffi/batch/types.ts +92 -0
- package/src/rust-ffi/client.ts +87 -0
- package/src/rust-ffi/context.ts +193 -0
- package/src/rust-ffi/index.ts +51 -541
- package/src/rust-ffi/into.ts +21 -0
- package/src/rust-ffi/options.ts +113 -0
- package/src/rust-ffi/packed.ts +253 -0
- package/src/rust-ffi/proven.ts +28 -0
- package/src/rust-ffi/scalar/crypto.ts +346 -0
- package/src/rust-ffi/scalar/factories.ts +459 -0
- package/src/rust-ffi/scalar/hashing.ts +74 -0
- package/src/rust-ffi/scalar/http.ts +337 -0
- package/src/rust-ffi/scalar/index.ts +31 -0
- package/src/rust-ffi/scalar/interface.ts +325 -0
- package/src/rust-ffi/scalar/json-packed.ts +175 -0
- package/src/rust-ffi/scalar/json.ts +61 -0
- package/src/rust-ffi/scalar/payload.ts +241 -0
- package/src/rust-ffi/text.ts +121 -0
- package/src/selection.json +194 -0
- package/src/selection.ts +73 -0
- package/src/shared/adaptive.ts +67 -0
- package/src/shared/buffer-pool.ts +264 -0
- package/src/shared/bytes.ts +108 -9
- package/src/shared/codec.ts +78 -0
- package/src/shared/env.ts +20 -0
- package/src/shared/log.ts +75 -0
- package/src/shared/metrics.ts +247 -0
- package/src/shared/packed/index.ts +10 -0
- package/src/shared/packed/parsers.ts +31 -0
- package/src/shared/packed/schema.ts +23 -0
- package/src/shared/packed/wire.ts +504 -0
- package/src/shared/proven.ts +289 -0
- package/src/shared/request-id.ts +67 -0
- package/src/shared/response.ts +104 -0
- package/src/shared/runtime.ts +29 -0
- package/src/shared/trace.ts +81 -0
- package/src/shared/uuid.ts +15 -0
- package/Cargo.toml +0 -73
- package/bench.ts +0 -3
- package/rust/batch.rs +0 -195
- package/rust/cookie_parser.rs +0 -70
- package/rust/cors.rs +0 -263
- package/rust/hashing.rs +0 -164
- package/rust/headers.rs +0 -234
- package/rust/hmac_sha256.rs +0 -113
- package/rust/http_parser.rs +0 -132
- package/rust/ingress.rs +0 -616
- package/rust/ingress_constants.rs +0 -89
- package/rust/ip_trust.rs +0 -185
- package/rust/json_ops.rs +0 -66
- package/rust/json_patch_ops.rs +0 -25
- package/rust/json_schema.rs +0 -151
- package/rust/json_ser.rs +0 -429
- package/rust/lib.rs +0 -48
- package/rust/method.rs +0 -141
- package/rust/mime_lookup.rs +0 -76
- package/rust/output.rs +0 -238
- package/rust/proxy.rs +0 -90
- package/rust/query_parser.rs +0 -141
- package/rust/random_token.rs +0 -52
- package/rust/rate_limit.rs +0 -203
- package/rust/terminal.rs +0 -95
- package/rust/unit_tests.rs +0 -843
- package/rust/url_codec.rs +0 -133
- package/rust/util.rs +0 -537
- package/rust/validation.rs +0 -107
- package/rust/websocket.rs +0 -23
- package/src/baseline/index.ts +0 -12
- package/src/baseline/tasks/cookie.ts +0 -22
- package/src/baseline/tasks/hashing.ts +0 -18
- package/src/baseline/tasks/hmac.ts +0 -38
- package/src/baseline/tasks/http.ts +0 -62
- package/src/baseline/tasks/json-patch.ts +0 -12
- package/src/baseline/tasks/json.ts +0 -50
- package/src/baseline/tasks/mime.ts +0 -5
- package/src/baseline/tasks/query.ts +0 -14
- package/src/baseline/tasks/token.ts +0 -15
- package/src/baseline/tasks/url.ts +0 -11
- package/src/baseline/tasks/validation.ts +0 -19
- package/src/baseline/tasks/websocket.ts +0 -14
- package/src/bench/assert.ts +0 -70
- package/src/bench/checks.ts +0 -227
- package/src/bench/checksum.ts +0 -27
- package/src/bench/comparisons.ts +0 -70
- package/src/bench/concurrent-worker.ts +0 -135
- package/src/bench/fixtures.ts +0 -233
- package/src/bench/index.ts +0 -2
- package/src/bench/measure.ts +0 -177
- package/src/bench/now.ts +0 -3
- package/src/bench/report.ts +0 -79
- package/src/bench/run.ts +0 -57
- package/src/bench/tasks/complex.ts +0 -232
- package/src/bench/tasks/concurrent.ts +0 -213
- package/src/bench/tasks/cookie.ts +0 -44
- package/src/bench/tasks/hashing.ts +0 -33
- package/src/bench/tasks/hmac.ts +0 -36
- package/src/bench/tasks/http.ts +0 -50
- package/src/bench/tasks/index.ts +0 -52
- package/src/bench/tasks/json-patch.ts +0 -21
- package/src/bench/tasks/json.ts +0 -33
- package/src/bench/tasks/mime.ts +0 -21
- package/src/bench/tasks/query.ts +0 -44
- package/src/bench/tasks/stress.ts +0 -142
- package/src/bench/tasks/token.ts +0 -20
- package/src/bench/tasks/url.ts +0 -34
- package/src/bench/tasks/validation.ts +0 -57
- package/src/bench/tasks/websocket.ts +0 -21
- package/src/bench/types.ts +0 -60
- package/src/data/json-rows.ts +0 -33
- package/src/ingress/packed-input.ts +0 -59
- package/src/shared/json.ts +0 -18
- package/src/shared/packed.ts +0 -316
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
// src/rust-ffi/scalar/http.ts — HTTP wire-format + parsing scalar methods.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors rust/http/* (packed parsers, media type, etag, HTTP dates,
|
|
4
|
+
// Accept-Encoding, URL codec/join, MIME lookup) plus the `util/validation`
|
|
5
|
+
// validators and the WebSocket accept-key.
|
|
6
|
+
//
|
|
7
|
+
// Runtime dispatch is centralized in the adapter (`ctx.runtime`): Bun built-in
|
|
8
|
+
// delegations (urlEncode/urlDecode/httpDate) come from `builtins.has(op)` and
|
|
9
|
+
// the native call from `transport.resolve(op)` / `transport.ffi` (bun:ffi
|
|
10
|
+
// first, napi fallback) — no inline `isBun()` / `getBunFFI()`.
|
|
11
|
+
|
|
12
|
+
import type { EncodingPrefResult, MediaTypeResult } from '../../native'
|
|
13
|
+
import type { BunFFI } from '../../native/ffi'
|
|
14
|
+
import { decoder } from '../../shared/bytes'
|
|
15
|
+
import { decodeUtf8Fatal } from '../../shared/codec'
|
|
16
|
+
import { packPairs } from '../../shared/packed'
|
|
17
|
+
import { writeInto } from '../into'
|
|
18
|
+
import { memoizeFfi, type RustClientContext, resolveNative } from '../context'
|
|
19
|
+
|
|
20
|
+
// Mirror napi `url_decode`'s UTF-8 validation (rust/http/url_codec.rs): only
|
|
21
|
+
// validate when a high byte is present (the ASCII fast path skips it — same as
|
|
22
|
+
// Rust's saw_high check). On the Bun path the check is a native C-ABI probe
|
|
23
|
+
// (`castrum_utf8_valid`); under Node (no ffi) the codec's fatal TextDecoder is
|
|
24
|
+
// the fallback — no TextDecoder runs on the Bun path.
|
|
25
|
+
function ensureValidUtf8(bytes: Uint8Array, ffi: BunFFI | null): void {
|
|
26
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
27
|
+
if ((bytes[i] ?? 0) >= 0x80) {
|
|
28
|
+
if (ffi) {
|
|
29
|
+
if (!ffi.utf8Valid(bytes)) {
|
|
30
|
+
throw new Error('url decode: invalid UTF-8 in decoded output')
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
try {
|
|
34
|
+
decodeUtf8Fatal(bytes)
|
|
35
|
+
} catch {
|
|
36
|
+
throw new Error('url decode: invalid UTF-8 in decoded output')
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Read an unsigned little-endian u32 at `off` (0 past the end). */
|
|
45
|
+
function u32LE(b: Uint8Array, off: number): number {
|
|
46
|
+
return (
|
|
47
|
+
((b[off] ?? 0) |
|
|
48
|
+
((b[off + 1] ?? 0) << 8) |
|
|
49
|
+
((b[off + 2] ?? 0) << 16) |
|
|
50
|
+
((b[off + 3] ?? 0) << 24)) >>>
|
|
51
|
+
0
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Unpack the `castrum_parse_media_type` verdict into the napi-shaped result.
|
|
57
|
+
* Layout: `[u32 mediaTypeLen][mediaType][u32 charsetLen (0xFFFFFFFF = none)]
|
|
58
|
+
* [charset][u32 boundaryLen][boundary][u32 paramCount]{[u32 keyLen][key]
|
|
59
|
+
* [u32 valLen][val]}` — charset/boundary also appear in `params`, matching the
|
|
60
|
+
* napi `HashMap` (which keeps every param including charset/boundary).
|
|
61
|
+
*/
|
|
62
|
+
function unpackMediaType(packed: Uint8Array): MediaTypeResult {
|
|
63
|
+
let off = 0
|
|
64
|
+
const mtLen = u32LE(packed, off)
|
|
65
|
+
off += 4
|
|
66
|
+
const mediaType = decoder.decode(packed.subarray(off, off + mtLen))
|
|
67
|
+
off += mtLen
|
|
68
|
+
let charset: string | null = null
|
|
69
|
+
const csLen = u32LE(packed, off)
|
|
70
|
+
off += 4
|
|
71
|
+
if (csLen !== 0xffffffff) {
|
|
72
|
+
charset = decoder.decode(packed.subarray(off, off + csLen))
|
|
73
|
+
off += csLen
|
|
74
|
+
}
|
|
75
|
+
let boundary: string | null = null
|
|
76
|
+
const bLen = u32LE(packed, off)
|
|
77
|
+
off += 4
|
|
78
|
+
if (bLen !== 0xffffffff) {
|
|
79
|
+
boundary = decoder.decode(packed.subarray(off, off + bLen))
|
|
80
|
+
off += bLen
|
|
81
|
+
}
|
|
82
|
+
const count = u32LE(packed, off)
|
|
83
|
+
off += 4
|
|
84
|
+
const params: Record<string, string> = {}
|
|
85
|
+
for (let i = 0; i < count; i++) {
|
|
86
|
+
const kLen = u32LE(packed, off)
|
|
87
|
+
off += 4
|
|
88
|
+
const key = decoder.decode(packed.subarray(off, off + kLen))
|
|
89
|
+
off += kLen
|
|
90
|
+
const vLen = u32LE(packed, off)
|
|
91
|
+
off += 4
|
|
92
|
+
const val = decoder.decode(packed.subarray(off, off + vLen))
|
|
93
|
+
off += vLen
|
|
94
|
+
params[key] = val
|
|
95
|
+
}
|
|
96
|
+
return { mediaType, charset, boundary, params }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Unpack the `castrum_parse_accept_encoding` verdict (f32 q-values) into the
|
|
100
|
+
* napi-shaped array. Layout: `[u32 count]{[u32 encLen][enc][f32 q][u32 order]}`. */
|
|
101
|
+
function unpackAcceptEncoding(packed: Uint8Array): EncodingPrefResult[] {
|
|
102
|
+
const count = u32LE(packed, 0)
|
|
103
|
+
let off = 4
|
|
104
|
+
const out: EncodingPrefResult[] = []
|
|
105
|
+
const view = new DataView(packed.buffer, packed.byteOffset, packed.byteLength)
|
|
106
|
+
for (let i = 0; i < count; i++) {
|
|
107
|
+
const encLen = u32LE(packed, off)
|
|
108
|
+
off += 4
|
|
109
|
+
const encoding = decoder.decode(packed.subarray(off, off + encLen))
|
|
110
|
+
off += encLen
|
|
111
|
+
const q = view.getFloat32(off, true)
|
|
112
|
+
off += 4
|
|
113
|
+
const order = u32LE(packed, off)
|
|
114
|
+
off += 4
|
|
115
|
+
out.push({ encoding, q, order })
|
|
116
|
+
}
|
|
117
|
+
return out
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** HTTP / parsing scalar methods (`Pick<RustScalar, ...>`). */
|
|
121
|
+
export function buildHttp(ctx: RustClientContext) {
|
|
122
|
+
const { builtins, transport } = ctx.runtime
|
|
123
|
+
// Resolve the native fns through the shared first-use cache so the hot
|
|
124
|
+
// scalar path skips the lazy-addon Proxy get on every call. `resolveNative`
|
|
125
|
+
// is FFI-first (bun:ffi when live, napi fallback) via the adapter.
|
|
126
|
+
const n = (name: string) => resolveNative(ctx, name)
|
|
127
|
+
// Hoist the immutable per-runtime builtin-delegation decisions into a
|
|
128
|
+
// bind-time map (BUILTIN_OPS is a module constant, never mutated after
|
|
129
|
+
// load) so the hot path reads an object property instead of a `Set.has`.
|
|
130
|
+
const HAS = {
|
|
131
|
+
urlEncode: builtins.has('urlEncode'),
|
|
132
|
+
urlDecode: builtins.has('urlDecode'),
|
|
133
|
+
httpDate: builtins.has('httpDate'),
|
|
134
|
+
}
|
|
135
|
+
// Lazy-memoized ffi surface: binds on first call (no eager dlopen at
|
|
136
|
+
// import), then a single local read per call — no getter hop.
|
|
137
|
+
const ffi = memoizeFfi(transport)
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
mimeFromExtension(ext: Uint8Array): Uint8Array | string {
|
|
141
|
+
// FFI-first: the `cstring` ARG is string-only, so bytes are decoded once
|
|
142
|
+
// (cheap CString clone) before the call. napi keeps the memoized-bytes
|
|
143
|
+
// path (cachedMime).
|
|
144
|
+
const f = ffi()
|
|
145
|
+
if (f) return f.mimeFromExtension(decoder.decode(ext)) ?? 'application/octet-stream'
|
|
146
|
+
return ctx.cachedMime(ext)
|
|
147
|
+
},
|
|
148
|
+
urlEncode(input: Uint8Array): Uint8Array | string {
|
|
149
|
+
// Under Bun, Bun's native `encodeURIComponent` beats the rust+FFI/NAPI
|
|
150
|
+
// crossing (~4x on the string path, ~1.3x on bytes) and skips the C-ABI
|
|
151
|
+
// call entirely — see docs/bun-builtins-decision-matrix.md. Bun returns
|
|
152
|
+
// the percent-encoded STRING (native transfer). Parity is pinned by
|
|
153
|
+
// test/unit/features/url.test.ts.
|
|
154
|
+
if (HAS.urlEncode) return builtins.urlEncode(input)
|
|
155
|
+
return n('urlEncode')(input) as Uint8Array
|
|
156
|
+
},
|
|
157
|
+
urlDecode(input: Uint8Array): Uint8Array {
|
|
158
|
+
// Same rationale as `urlEncode`; the strict `urlDecode` already throws on
|
|
159
|
+
// invalid UTF-8 (ensureValidUtf8 below), matching decodeURIComponent's
|
|
160
|
+
// URIError (a subclass of Error), so the JS path is behavior-equivalent.
|
|
161
|
+
if (HAS.urlDecode) return builtins.urlDecode(input)
|
|
162
|
+
const f = ffi()
|
|
163
|
+
if (f) {
|
|
164
|
+
const out = f.urlDecode(input)
|
|
165
|
+
// napi `url_decode` validates UTF-8 (only when a high byte is present);
|
|
166
|
+
// `url_decode_bytes`/`url_decode_into` do not. This is the strict one.
|
|
167
|
+
ensureValidUtf8(out, transport.ffi)
|
|
168
|
+
return out
|
|
169
|
+
}
|
|
170
|
+
return n('urlDecode')(input) as Uint8Array
|
|
171
|
+
},
|
|
172
|
+
urlDecodeBytes(input: Uint8Array): Uint8Array {
|
|
173
|
+
const f = ffi()
|
|
174
|
+
if (f) return f.urlDecode(input) // byte semantics match url_decode_bytes
|
|
175
|
+
return n('urlDecodeBytes')(input) as Uint8Array
|
|
176
|
+
},
|
|
177
|
+
urlEncodeInto(input: Uint8Array, output: Uint8Array): number {
|
|
178
|
+
return n('urlEncodeInto')(input, output) as number
|
|
179
|
+
},
|
|
180
|
+
urlDecodeInto(input: Uint8Array, output: Uint8Array): number {
|
|
181
|
+
// no UTF-8 check — matches napi url_decode_into on both transports
|
|
182
|
+
return n('urlDecodeInto')(input, output) as number
|
|
183
|
+
},
|
|
184
|
+
validateEmail(input: Uint8Array): boolean {
|
|
185
|
+
// FFI-first: the `cstring` ARG is string-only, so bytes are decoded once
|
|
186
|
+
// (cheap CString clone) before the call — still faster than the old
|
|
187
|
+
// `(ptr,len)` view path (see bench:margin cstring scenario). napi
|
|
188
|
+
// fallback keeps the byte path.
|
|
189
|
+
const f = ffi()
|
|
190
|
+
if (f) return f.validateEmail(decoder.decode(input))
|
|
191
|
+
return n('validateEmail')(input) as boolean
|
|
192
|
+
},
|
|
193
|
+
validateUuid(input: Uint8Array): boolean {
|
|
194
|
+
const f = ffi()
|
|
195
|
+
if (f) return f.validateUuid(decoder.decode(input))
|
|
196
|
+
return n('validateUuid')(input) as boolean
|
|
197
|
+
},
|
|
198
|
+
validateIpv4(input: Uint8Array): boolean {
|
|
199
|
+
const f = ffi()
|
|
200
|
+
if (f) return f.validateIpv4(decoder.decode(input))
|
|
201
|
+
return n('validateIpv4')(input) as boolean
|
|
202
|
+
},
|
|
203
|
+
validateIpv6(input: Uint8Array): boolean {
|
|
204
|
+
const f = ffi()
|
|
205
|
+
if (f) return f.validateIpv6(decoder.decode(input))
|
|
206
|
+
return n('validateIpv6')(input) as boolean
|
|
207
|
+
},
|
|
208
|
+
wsAcceptKey(key: Uint8Array): Uint8Array | string {
|
|
209
|
+
// FFI-first: the `cstring` ARG is string-only, so bytes are decoded once
|
|
210
|
+
// (cheap CString clone) before the call. napi fallback keeps the bytes.
|
|
211
|
+
const f = ffi()
|
|
212
|
+
if (f) return f.wsAcceptKey(decoder.decode(key))
|
|
213
|
+
return n('wsAcceptKey')(key) as Uint8Array | string
|
|
214
|
+
},
|
|
215
|
+
wsAcceptKeyInto(key: Uint8Array, output: Uint8Array): number {
|
|
216
|
+
const f = ffi()
|
|
217
|
+
if (f) return f.wsAcceptKeyInto(key, output)
|
|
218
|
+
return writeInto('ws accept key', output, n('wsAcceptKey')(key) as Uint8Array)
|
|
219
|
+
},
|
|
220
|
+
httpParseRequestPacked(input: Uint8Array): Uint8Array {
|
|
221
|
+
const f = ffi()
|
|
222
|
+
if (f) {
|
|
223
|
+
const out = new Uint8Array(input.length * 9 + 16)
|
|
224
|
+
const w = f.httpParseRequestPackedInto(input, out)
|
|
225
|
+
return out.subarray(0, w)
|
|
226
|
+
}
|
|
227
|
+
return n('httpParseRequestPacked')(input) as Uint8Array
|
|
228
|
+
},
|
|
229
|
+
httpParseRequestPackedInto(input: Uint8Array, output: Uint8Array): number {
|
|
230
|
+
return n('httpParseRequestPackedInto')(input, output) as number
|
|
231
|
+
},
|
|
232
|
+
queryParsePacked(input: Uint8Array): Uint8Array {
|
|
233
|
+
const f = ffi()
|
|
234
|
+
if (f) {
|
|
235
|
+
const out = new Uint8Array(input.length * 9 + 16)
|
|
236
|
+
const w = f.queryParsePackedInto(input, out)
|
|
237
|
+
return out.subarray(0, w)
|
|
238
|
+
}
|
|
239
|
+
return n('queryParsePacked')(input) as Uint8Array
|
|
240
|
+
},
|
|
241
|
+
queryParsePackedInto(input: Uint8Array, output: Uint8Array): number {
|
|
242
|
+
return n('queryParsePackedInto')(input, output) as number
|
|
243
|
+
},
|
|
244
|
+
cookieParsePacked(input: Uint8Array): Uint8Array {
|
|
245
|
+
const f = ffi()
|
|
246
|
+
if (f) {
|
|
247
|
+
const out = new Uint8Array(input.length * 9 + 16)
|
|
248
|
+
const w = f.cookieParsePackedInto(input, out)
|
|
249
|
+
return out.subarray(0, w)
|
|
250
|
+
}
|
|
251
|
+
return n('cookieParsePacked')(input) as Uint8Array
|
|
252
|
+
},
|
|
253
|
+
cookieParsePackedInto(input: Uint8Array, output: Uint8Array): number {
|
|
254
|
+
return n('cookieParsePackedInto')(input, output) as number
|
|
255
|
+
},
|
|
256
|
+
formParsePacked(input: Uint8Array): Uint8Array {
|
|
257
|
+
const f = ffi()
|
|
258
|
+
if (f) {
|
|
259
|
+
// x-www-form-urlencoded uses the SAME packed-pairs core as query parse.
|
|
260
|
+
const out = new Uint8Array(input.length * 9 + 16)
|
|
261
|
+
const w = f.formParsePackedInto(input, out)
|
|
262
|
+
return out.subarray(0, w)
|
|
263
|
+
}
|
|
264
|
+
return n('formParsePacked')(input) as Uint8Array
|
|
265
|
+
},
|
|
266
|
+
formParsePackedInto(input: Uint8Array, output: Uint8Array): number {
|
|
267
|
+
// Pooled sibling of formParsePacked — caller-owned output buffer (the
|
|
268
|
+
// napi addon has no packed `Into` for form, so copy via the allocating
|
|
269
|
+
// call on that path; same wire output, so the copy is exact).
|
|
270
|
+
const f = ffi()
|
|
271
|
+
if (f) return f.formParsePackedInto(input, output)
|
|
272
|
+
return writeInto('form parse', output, n('formParsePacked')(input) as Uint8Array)
|
|
273
|
+
},
|
|
274
|
+
parseMediaType(input: Uint8Array): MediaTypeResult {
|
|
275
|
+
const f = ffi()
|
|
276
|
+
if (f) return unpackMediaType(f.parseMediaType(input))
|
|
277
|
+
const r = n('parseMediaType')(input) as MediaTypeResult
|
|
278
|
+
// napi serializes Option fields as undefined; normalize to null to match
|
|
279
|
+
// the declared `string | null` public type.
|
|
280
|
+
return {
|
|
281
|
+
mediaType: r.mediaType,
|
|
282
|
+
charset: r.charset ?? null,
|
|
283
|
+
boundary: r.boundary ?? null,
|
|
284
|
+
params: r.params,
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
etag(data: Uint8Array, weak?: boolean): Uint8Array | string {
|
|
288
|
+
return n('etag')(data, weak ?? undefined) as Uint8Array | string
|
|
289
|
+
},
|
|
290
|
+
etagInto(data: Uint8Array, output: Uint8Array, weak?: boolean): number {
|
|
291
|
+
return n('etagInto')(data, output, weak ?? undefined) as number
|
|
292
|
+
},
|
|
293
|
+
httpDate(secs?: number): Uint8Array | string {
|
|
294
|
+
// Bun's Date.toUTCString (RFC 1123 IMF-fixdate) beats the rust+FFI
|
|
295
|
+
// crossing (~3.7x, measured); Bun returns the STRING (native transfer).
|
|
296
|
+
if (HAS.httpDate) return builtins.httpDate(secs)
|
|
297
|
+
return n('httpDate')(secs ?? undefined) as Uint8Array
|
|
298
|
+
},
|
|
299
|
+
httpDateInto(secs: number | undefined, output: Uint8Array): number {
|
|
300
|
+
// FFI-first: `castrum_http_date_into` writes directly into the caller
|
|
301
|
+
// buffer (no napi crossing). `undefined` → 0 (matches the napi
|
|
302
|
+
// `Option<f64>` default). napi fallback keeps the old path.
|
|
303
|
+
const f = ffi()
|
|
304
|
+
if (f) return f.httpDateInto(secs ?? 0, output)
|
|
305
|
+
return n('httpDateInto')(secs ?? undefined, output) as number
|
|
306
|
+
},
|
|
307
|
+
parseHttpDate(input: Uint8Array): bigint | null {
|
|
308
|
+
return n('parseHttpDate')(input) as bigint | null
|
|
309
|
+
},
|
|
310
|
+
parseAcceptEncoding(input: Uint8Array): EncodingPrefResult[] {
|
|
311
|
+
const f = ffi()
|
|
312
|
+
if (f) return unpackAcceptEncoding(f.parseAcceptEncoding(input))
|
|
313
|
+
return n('parseAcceptEncoding')(input) as EncodingPrefResult[]
|
|
314
|
+
},
|
|
315
|
+
urlResolve(base: Uint8Array, reference: Uint8Array): Uint8Array | string {
|
|
316
|
+
// FFI-first: cstring resolution (native transfer — zero encode).
|
|
317
|
+
const f = ffi()
|
|
318
|
+
if (f) {
|
|
319
|
+
const s = f.urlResolve(base, reference)
|
|
320
|
+
if (s === null) throw new Error('url resolve: invalid UTF-8 in input')
|
|
321
|
+
return s
|
|
322
|
+
}
|
|
323
|
+
return n('urlResolve')(base, reference) as Uint8Array
|
|
324
|
+
},
|
|
325
|
+
urlEncodeQuery(params: Record<string, string>): Uint8Array | string {
|
|
326
|
+
// FFI-first: pack the params into the shared pairs layout, then the C
|
|
327
|
+
// side sorts keys (BTreeMap) and percent-encodes → cstring.
|
|
328
|
+
const f = ffi()
|
|
329
|
+
if (f) {
|
|
330
|
+
const s = f.urlEncodeQuery(packPairs(Object.entries(params)))
|
|
331
|
+
if (s === null) throw new Error('url encode query: invalid input')
|
|
332
|
+
return s
|
|
333
|
+
}
|
|
334
|
+
return n('urlEncodeQuery')(params) as Uint8Array
|
|
335
|
+
},
|
|
336
|
+
}
|
|
337
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/rust-ffi/scalar/index.ts — Scalar + feature FFI methods (barrel).
|
|
2
|
+
//
|
|
3
|
+
// The byte-level single-value methods of the Rust client (crc32, hmac, json,
|
|
4
|
+
// jwt, password, aead, compress, ... plus the class factories and runtime
|
|
5
|
+
// controls). Composed into the client by ./client.ts.
|
|
6
|
+
//
|
|
7
|
+
// The `RustScalar` interface (the public surface, with its perf JSDoc) lives
|
|
8
|
+
// in `interface.ts`; the implementation is split into domain builders mirroring
|
|
9
|
+
// `rust/` (hashing, json, http, crypto, payload, factories).
|
|
10
|
+
|
|
11
|
+
import type { RustClientContext } from '../context'
|
|
12
|
+
import { buildCrypto } from './crypto'
|
|
13
|
+
import { buildFactories } from './factories'
|
|
14
|
+
import { buildHashing } from './hashing'
|
|
15
|
+
import { buildHttp } from './http'
|
|
16
|
+
import { buildJson } from './json'
|
|
17
|
+
import { buildPayload } from './payload'
|
|
18
|
+
|
|
19
|
+
export type { RustScalar } from './interface'
|
|
20
|
+
|
|
21
|
+
/** Build the scalar/feature method set for a client context. */
|
|
22
|
+
export function buildScalar(ctx: RustClientContext): import('./interface').RustScalar {
|
|
23
|
+
return {
|
|
24
|
+
...buildHashing(ctx),
|
|
25
|
+
...buildJson(ctx),
|
|
26
|
+
...buildHttp(ctx),
|
|
27
|
+
...buildCrypto(ctx),
|
|
28
|
+
...buildPayload(ctx),
|
|
29
|
+
...buildFactories(ctx),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
// src/rust-ffi/scalar/interface.ts — The `RustScalar` method surface.
|
|
2
|
+
//
|
|
3
|
+
// One file per public method. The implementation is split into domain
|
|
4
|
+
// builders (hashing, json, http, crypto, payload, factories) composed by
|
|
5
|
+
// `buildScalar` in index.ts.
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
AcceptNegotiatorInstance,
|
|
9
|
+
AeadCipherInstance,
|
|
10
|
+
Argon2HasherInstance,
|
|
11
|
+
Base64CodecInstance,
|
|
12
|
+
ConditionalRequestInstance,
|
|
13
|
+
CookieSignerInstance,
|
|
14
|
+
CsrfProtectorInstance,
|
|
15
|
+
EncodingPrefResult,
|
|
16
|
+
FormParserInstance,
|
|
17
|
+
HmacSignerInstance,
|
|
18
|
+
JwtSignerInstance,
|
|
19
|
+
MediaTypeMatcherInstance,
|
|
20
|
+
MediaTypeParserInstance,
|
|
21
|
+
MediaTypeResult,
|
|
22
|
+
MultipartPart,
|
|
23
|
+
PasswordHashOptions,
|
|
24
|
+
RateLimiterInstance,
|
|
25
|
+
SchemaValidatorInstance,
|
|
26
|
+
TemplateRendererInstance,
|
|
27
|
+
UrlBuilderInstance,
|
|
28
|
+
WsFrame,
|
|
29
|
+
} from '../../native'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The flat `rust.*` scalar method surface: bytes in → normalized out. Built by
|
|
33
|
+
* the scalar builders from the native addon + Bun built-in delegations.
|
|
34
|
+
*/
|
|
35
|
+
export interface RustScalar {
|
|
36
|
+
// ── Scalar utilities (bytes in → normalized out) ──
|
|
37
|
+
crc32(input: Uint8Array): number
|
|
38
|
+
fnv1a64(input: Uint8Array): bigint
|
|
39
|
+
/**
|
|
40
|
+
* XXH3-64 over raw bytes (high-throughput non-cryptographic hash).
|
|
41
|
+
* Compare against `Bun.hash.xxHash3` — see docs/bun-builtins-decision-matrix.md.
|
|
42
|
+
*/
|
|
43
|
+
xxh3(input: Uint8Array): bigint
|
|
44
|
+
hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array | string
|
|
45
|
+
hmacSha256Verify(key: Uint8Array, data: Uint8Array, sig: Uint8Array | string): boolean
|
|
46
|
+
jsonValid(input: Uint8Array): boolean
|
|
47
|
+
/**
|
|
48
|
+
* Parse JSON to a JS value (FFI packed structural parse on Bun — sonic-rs
|
|
49
|
+
* parses ONCE, JS assembles from a token stream with no re-parse; napi
|
|
50
|
+
* sonic-rs DOM fallback). Throws on invalid JSON.
|
|
51
|
+
*/
|
|
52
|
+
jsonParse(input: Uint8Array): unknown
|
|
53
|
+
jsonSumIds(input: Uint8Array): bigint
|
|
54
|
+
jsonPatch(doc: Uint8Array, patch: Uint8Array): Uint8Array | string
|
|
55
|
+
mimeFromExtension(ext: Uint8Array): Uint8Array | string
|
|
56
|
+
randomToken(byteLen: number): Uint8Array | string
|
|
57
|
+
randomTokenInto(byteLen: number, output: Uint8Array): number
|
|
58
|
+
urlEncode(input: Uint8Array): Uint8Array | string
|
|
59
|
+
urlDecode(input: Uint8Array): Uint8Array
|
|
60
|
+
/**
|
|
61
|
+
* Strict percent-decode without UTF-8 validation.
|
|
62
|
+
*/
|
|
63
|
+
urlDecodeBytes(input: Uint8Array): Uint8Array
|
|
64
|
+
urlEncodeInto(input: Uint8Array, output: Uint8Array): number
|
|
65
|
+
urlDecodeInto(input: Uint8Array, output: Uint8Array): number
|
|
66
|
+
validateEmail(input: Uint8Array): boolean
|
|
67
|
+
validateUuid(input: Uint8Array): boolean
|
|
68
|
+
validateIpv4(input: Uint8Array): boolean
|
|
69
|
+
validateIpv6(input: Uint8Array): boolean
|
|
70
|
+
wsAcceptKey(key: Uint8Array): Uint8Array | string
|
|
71
|
+
wsAcceptKeyInto(key: Uint8Array, output: Uint8Array): number
|
|
72
|
+
|
|
73
|
+
// ── Low-level / packed scalar ──
|
|
74
|
+
httpParseRequestPacked(input: Uint8Array): Uint8Array
|
|
75
|
+
queryParsePacked(input: Uint8Array): Uint8Array
|
|
76
|
+
cookieParsePacked(input: Uint8Array): Uint8Array
|
|
77
|
+
httpParseRequestPackedInto(input: Uint8Array, output: Uint8Array): number
|
|
78
|
+
queryParsePackedInto(input: Uint8Array, output: Uint8Array): number
|
|
79
|
+
cookieParsePackedInto(input: Uint8Array, output: Uint8Array): number
|
|
80
|
+
/**
|
|
81
|
+
* Parse an application/x-www-form-urlencoded body into packed pairs.
|
|
82
|
+
*/
|
|
83
|
+
formParsePacked(input: Uint8Array): Uint8Array
|
|
84
|
+
formParsePackedInto(input: Uint8Array, output: Uint8Array): number
|
|
85
|
+
/**
|
|
86
|
+
* Parse a Content-Type header into a structured media type.
|
|
87
|
+
*/
|
|
88
|
+
parseMediaType(input: Uint8Array): MediaTypeResult
|
|
89
|
+
/**
|
|
90
|
+
* Generate a strong/weak ETag (crc32-based).
|
|
91
|
+
*/
|
|
92
|
+
etag(data: Uint8Array, weak?: boolean): Uint8Array | string
|
|
93
|
+
etagInto(data: Uint8Array, output: Uint8Array, weak?: boolean): number
|
|
94
|
+
/**
|
|
95
|
+
* Format a unix timestamp as an IMF-fixdate HTTP-date.
|
|
96
|
+
*/
|
|
97
|
+
httpDate(secs?: number): Uint8Array | string
|
|
98
|
+
httpDateInto(secs: number | undefined, output: Uint8Array): number
|
|
99
|
+
parseHttpDate(input: Uint8Array): bigint | null
|
|
100
|
+
parseAcceptEncoding(input: Uint8Array): EncodingPrefResult[]
|
|
101
|
+
/**
|
|
102
|
+
* Base64 encode (standard by default; url-safe/padding configurable).
|
|
103
|
+
*/
|
|
104
|
+
base64Encode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array | string
|
|
105
|
+
/**
|
|
106
|
+
* Base64 decode (standard); throws on invalid input.
|
|
107
|
+
*/
|
|
108
|
+
base64Decode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array
|
|
109
|
+
base64EncodeInto(
|
|
110
|
+
input: Uint8Array,
|
|
111
|
+
output: Uint8Array,
|
|
112
|
+
urlSafe?: boolean,
|
|
113
|
+
padding?: boolean,
|
|
114
|
+
): number
|
|
115
|
+
base64DecodeInto(
|
|
116
|
+
input: Uint8Array,
|
|
117
|
+
output: Uint8Array,
|
|
118
|
+
urlSafe?: boolean,
|
|
119
|
+
padding?: boolean,
|
|
120
|
+
): number
|
|
121
|
+
base64UrlEncode(input: Uint8Array): Uint8Array | string
|
|
122
|
+
base64UrlDecode(input: Uint8Array): Uint8Array
|
|
123
|
+
/**
|
|
124
|
+
* Lowercase hex encode.
|
|
125
|
+
*/
|
|
126
|
+
hexEncode(input: Uint8Array): Uint8Array | string
|
|
127
|
+
/**
|
|
128
|
+
* Hex decode; throws on odd length or invalid digits.
|
|
129
|
+
*/
|
|
130
|
+
hexDecode(input: Uint8Array): Uint8Array
|
|
131
|
+
hexEncodeInto(input: Uint8Array, output: Uint8Array): number
|
|
132
|
+
hexDecodeInto(input: Uint8Array, output: Uint8Array): number
|
|
133
|
+
hmacSha256Into(key: Uint8Array, data: Uint8Array, output: Uint8Array): number
|
|
134
|
+
signCookieInto(value: Uint8Array, secret: Uint8Array, output: Uint8Array): number
|
|
135
|
+
verifyCookieInto(signed: Uint8Array, secret: Uint8Array, output: Uint8Array): number | null
|
|
136
|
+
csrfTokenInto(secret: Uint8Array, output: Uint8Array): number
|
|
137
|
+
aeadEncryptInto(
|
|
138
|
+
key: Uint8Array,
|
|
139
|
+
nonce: Uint8Array,
|
|
140
|
+
plaintext: Uint8Array,
|
|
141
|
+
output: Uint8Array,
|
|
142
|
+
algorithm?: string | null,
|
|
143
|
+
): number
|
|
144
|
+
wsFrameEncodeInto(
|
|
145
|
+
opcode: number,
|
|
146
|
+
payload: Uint8Array,
|
|
147
|
+
mask: boolean,
|
|
148
|
+
fin: boolean,
|
|
149
|
+
output: Uint8Array,
|
|
150
|
+
): number
|
|
151
|
+
gzipCompressInto(data: Uint8Array, output: Uint8Array, level?: number | null): number
|
|
152
|
+
brotliCompressInto(data: Uint8Array, output: Uint8Array, quality?: number | null): number
|
|
153
|
+
/**
|
|
154
|
+
* Sign a cookie value as `value.signature` (HMAC-SHA256 hex).
|
|
155
|
+
*/
|
|
156
|
+
signCookie(value: Uint8Array, secret: Uint8Array): Uint8Array | string
|
|
157
|
+
/**
|
|
158
|
+
* Verify a signed cookie; returns the value or null.
|
|
159
|
+
*/
|
|
160
|
+
verifyCookie(signed: Uint8Array, secret: Uint8Array): Uint8Array | string | null
|
|
161
|
+
/**
|
|
162
|
+
* Create a CSRF token (32-byte random hex + HMAC signature).
|
|
163
|
+
*/
|
|
164
|
+
csrfToken(secret: Uint8Array): Uint8Array | string
|
|
165
|
+
/**
|
|
166
|
+
* Constant-time verify a CSRF token.
|
|
167
|
+
*/
|
|
168
|
+
csrfVerify(token: Uint8Array, secret: Uint8Array): boolean
|
|
169
|
+
/**
|
|
170
|
+
* Resolve a URL reference against a base (RFC 3986).
|
|
171
|
+
*/
|
|
172
|
+
urlResolve(base: Uint8Array, reference: Uint8Array): Uint8Array | string
|
|
173
|
+
/**
|
|
174
|
+
* Build a percent-encoded query string from params (sorted keys).
|
|
175
|
+
*/
|
|
176
|
+
urlEncodeQuery(params: Record<string, string>): Uint8Array | string
|
|
177
|
+
|
|
178
|
+
// ── Factories / runtime ──
|
|
179
|
+
createSchemaValidator(schema: Uint8Array): SchemaValidatorInstance
|
|
180
|
+
createHmacSigner(key: Uint8Array): HmacSignerInstance
|
|
181
|
+
createTemplateRenderer(source: string): TemplateRendererInstance
|
|
182
|
+
createFormParser(capacity?: number): FormParserInstance
|
|
183
|
+
createMediaTypeParser(): MediaTypeParserInstance
|
|
184
|
+
/**
|
|
185
|
+
* Higher-order conditional-request check: per-resource 304 decision (setup once).
|
|
186
|
+
*/
|
|
187
|
+
createConditionalRequest(
|
|
188
|
+
etagValue: Uint8Array,
|
|
189
|
+
lastModifiedSecs?: number,
|
|
190
|
+
): ConditionalRequestInstance
|
|
191
|
+
/**
|
|
192
|
+
* Higher-order negotiator: precompiles supported encodings once.
|
|
193
|
+
*/
|
|
194
|
+
createAcceptNegotiator(supported: string[]): AcceptNegotiatorInstance
|
|
195
|
+
createBase64Codec(urlSafe?: boolean, padding?: boolean): Base64CodecInstance
|
|
196
|
+
createCookieSigner(secret: Uint8Array): CookieSignerInstance
|
|
197
|
+
createCsrfProtector(secret: Uint8Array): CsrfProtectorInstance
|
|
198
|
+
createUrlBuilder(base: Uint8Array): UrlBuilderInstance
|
|
199
|
+
createJwtSigner(secret: Uint8Array, ttlSeconds?: number): JwtSignerInstance
|
|
200
|
+
createAeadCipher(key: Uint8Array, algorithm?: string): AeadCipherInstance
|
|
201
|
+
createArgon2Hasher(options?: PasswordHashOptions | null): Argon2HasherInstance
|
|
202
|
+
createMediaTypeMatcher(expected: Uint8Array): MediaTypeMatcherInstance
|
|
203
|
+
/**
|
|
204
|
+
* Sharded fixed-window per-key rate limiter. `maxEntries` clamps internally
|
|
205
|
+
* (default 1,048,576). Each instance owns an independent budget.
|
|
206
|
+
* @remarks Scalar per-check cost: ~3-14x SLOWER than the JS Map baseline —
|
|
207
|
+
* prefer the ingress pipeline (one FFI for all stages) for native rate
|
|
208
|
+
* limiting in a request path. [measured 2026-08]
|
|
209
|
+
*/
|
|
210
|
+
createRateLimiter(
|
|
211
|
+
limit: number,
|
|
212
|
+
windowMs: number,
|
|
213
|
+
maxEntries?: number | null,
|
|
214
|
+
): RateLimiterInstance
|
|
215
|
+
initThreadPool(rayonThreads?: number): void
|
|
216
|
+
rayonNumThreads(): number
|
|
217
|
+
|
|
218
|
+
// ── Backend-framework scalar features ──
|
|
219
|
+
jwtSign(
|
|
220
|
+
claims: Record<string, unknown>,
|
|
221
|
+
secret: Uint8Array,
|
|
222
|
+
ttlSeconds?: number | null,
|
|
223
|
+
nowSeconds?: number,
|
|
224
|
+
): Uint8Array
|
|
225
|
+
/**
|
|
226
|
+
* Sign a JWT from pre-serialized claim JSON bytes — skips the napi
|
|
227
|
+
* `serde_json::Value` marshal of `jwtSign` for callers that already hold the
|
|
228
|
+
* bytes (same injection semantics).
|
|
229
|
+
*/
|
|
230
|
+
jwtSignBytes(
|
|
231
|
+
claimsJson: Uint8Array,
|
|
232
|
+
secret: Uint8Array,
|
|
233
|
+
ttlSeconds?: number | null,
|
|
234
|
+
nowSeconds?: number,
|
|
235
|
+
): Uint8Array | string
|
|
236
|
+
jwtSignBytesInto(
|
|
237
|
+
claimsJson: Uint8Array,
|
|
238
|
+
secret: Uint8Array,
|
|
239
|
+
output: Uint8Array,
|
|
240
|
+
ttlSeconds?: number | null,
|
|
241
|
+
nowSeconds?: number,
|
|
242
|
+
): number
|
|
243
|
+
jwtVerify(token: Uint8Array, secret: Uint8Array, nowSeconds?: number): unknown
|
|
244
|
+
passwordHash(
|
|
245
|
+
password: Uint8Array,
|
|
246
|
+
salt: Uint8Array,
|
|
247
|
+
options?: PasswordHashOptions | null,
|
|
248
|
+
): Uint8Array | string
|
|
249
|
+
passwordVerify(password: Uint8Array, phc: Uint8Array): boolean
|
|
250
|
+
/**
|
|
251
|
+
* bcrypt password hash → `$2b$` PHC string (cost clamped 4..=31).
|
|
252
|
+
*/
|
|
253
|
+
passwordHashBcrypt(password: Uint8Array, cost: number): string
|
|
254
|
+
/**
|
|
255
|
+
* Verify a password against a bcrypt `$2b$` PHC string.
|
|
256
|
+
*/
|
|
257
|
+
passwordVerifyBcrypt(password: Uint8Array, hash: string): boolean
|
|
258
|
+
/**
|
|
259
|
+
* PBKDF2-HMAC-SHA256 key derivation (dkLen clamped to 1 MiB).
|
|
260
|
+
*/
|
|
261
|
+
pbkdf2Sha256(password: Uint8Array, salt: Uint8Array, rounds: number, dkLen: number): Uint8Array
|
|
262
|
+
aeadEncrypt(
|
|
263
|
+
key: Uint8Array,
|
|
264
|
+
nonce: Uint8Array,
|
|
265
|
+
plaintext: Uint8Array,
|
|
266
|
+
algorithm?: string | null,
|
|
267
|
+
): Uint8Array
|
|
268
|
+
aeadDecrypt(
|
|
269
|
+
key: Uint8Array,
|
|
270
|
+
nonce: Uint8Array,
|
|
271
|
+
ciphertext: Uint8Array,
|
|
272
|
+
algorithm?: string | null,
|
|
273
|
+
): Uint8Array | null
|
|
274
|
+
gzipCompress(data: Uint8Array, level?: number | null): Uint8Array
|
|
275
|
+
/**
|
|
276
|
+
* @param maxDecompressed Caps decompressed output (default 64 MiB) as a
|
|
277
|
+
* decompression-bomb guard; a larger value errors.
|
|
278
|
+
*/
|
|
279
|
+
gzipDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array
|
|
280
|
+
/**
|
|
281
|
+
* gzip-decompress into `output` (pooled — caller-owned buffer); returns bytes
|
|
282
|
+
* written. Throws when `output` is too small (the exact required size is
|
|
283
|
+
* reported) or the input is invalid / exceeds `maxDecompressed`.
|
|
284
|
+
* @param maxDecompressed Caps decompressed output (default 64 MiB) as a
|
|
285
|
+
* decompression-bomb guard; a larger value errors.
|
|
286
|
+
*/
|
|
287
|
+
gzipDecompressInto(data: Uint8Array, output: Uint8Array, maxDecompressed?: number | null): number
|
|
288
|
+
brotliCompress(data: Uint8Array, quality?: number | null): Uint8Array
|
|
289
|
+
/**
|
|
290
|
+
* @param maxDecompressed Caps decompressed output (default 64 MiB) as a
|
|
291
|
+
* decompression-bomb guard; a larger value errors.
|
|
292
|
+
*/
|
|
293
|
+
brotliDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array
|
|
294
|
+
/**
|
|
295
|
+
* brotli-decompress into `output` (pooled — caller-owned buffer); returns
|
|
296
|
+
* bytes written. Throws when `output` is too small (the exact required size
|
|
297
|
+
* is reported) or the input is invalid / exceeds `maxDecompressed`.
|
|
298
|
+
* @param maxDecompressed Caps decompressed output (default 64 MiB) as a
|
|
299
|
+
* decompression-bomb guard; a larger value errors.
|
|
300
|
+
*/
|
|
301
|
+
brotliDecompressInto(
|
|
302
|
+
data: Uint8Array,
|
|
303
|
+
output: Uint8Array,
|
|
304
|
+
maxDecompressed?: number | null,
|
|
305
|
+
): number
|
|
306
|
+
multipartParse(body: Uint8Array, boundary: Uint8Array): MultipartPart[]
|
|
307
|
+
multipartParsePacked(body: Uint8Array, boundary: Uint8Array): Uint8Array
|
|
308
|
+
multipartParsePackedInto(body: Uint8Array, boundary: Uint8Array, output: Uint8Array): number
|
|
309
|
+
wsFrameEncode(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean): Uint8Array
|
|
310
|
+
wsFrameDecode(data: Uint8Array): WsFrame | null
|
|
311
|
+
wsFrameDecodePackedInto(data: Uint8Array, output: Uint8Array): number | null
|
|
312
|
+
sseEncodeEvent(
|
|
313
|
+
event: string | null,
|
|
314
|
+
data: Uint8Array,
|
|
315
|
+
id: string | null,
|
|
316
|
+
retry: number | null,
|
|
317
|
+
): Uint8Array
|
|
318
|
+
sseEncodeEventInto(
|
|
319
|
+
event: string | null,
|
|
320
|
+
data: Uint8Array,
|
|
321
|
+
id: string | null,
|
|
322
|
+
retry: number | null,
|
|
323
|
+
output: Uint8Array,
|
|
324
|
+
): number
|
|
325
|
+
}
|