castrum 0.7.0 → 0.9.0
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 +322 -59
- package/castrum.linux-x64-gnu.node +0 -0
- package/dist/index.d.ts +3161 -0
- package/dist/index.js +7306 -0
- package/docs/ARCHITECTURE.md +415 -0
- package/docs/BENCHMARKS.md +307 -0
- package/docs/ENVIRONMENT.md +79 -0
- package/docs/GETTING_STARTED.md +209 -0
- package/docs/INGRESS.md +745 -0
- package/docs/REPO_MAP.md +259 -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 +42 -0
- package/docs/bun-builtins-decision-matrix.md +108 -0
- package/index.ts +51 -8
- package/package.json +73 -20
- package/src/data/json-rows.ts +13 -13
- package/src/ingress/body.ts +149 -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 +563 -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 +67 -0
- package/src/ingress/index.ts +130 -405
- package/src/ingress/metrics.ts +132 -0
- package/src/ingress/options.ts +197 -0
- package/src/ingress/packing/gather-raw-headers.ts +75 -0
- package/src/ingress/packing/header-packing.ts +75 -0
- package/src/ingress/packing/input-packer.ts +150 -0
- package/src/ingress/packing/scratch.ts +50 -0
- package/src/ingress/packing/select-headers.ts +96 -0
- package/src/ingress/response/error-bodies.ts +75 -0
- package/src/ingress/response/terminal.ts +59 -0
- package/src/ingress/routes/common.ts +38 -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 +37 -0
- package/src/ingress/routes/index.ts +10 -0
- package/src/ingress/routes/json-write.ts +153 -0
- package/src/ingress/routes/options.ts +36 -0
- package/src/ingress/routes/read.ts +45 -0
- package/src/ingress/server-node.ts +376 -0
- package/src/ingress/server.ts +443 -0
- package/src/ingress/shared.ts +132 -0
- package/src/ingress/status.ts +97 -0
- package/src/ingress/types.ts +157 -0
- package/src/integration/batch.ts +58 -0
- package/src/integration/index.ts +29 -0
- package/src/integration/pipeline.ts +217 -0
- package/src/integration/streaming.ts +79 -0
- package/src/integration/websocket.ts +92 -0
- package/src/loader/batch.ts +133 -0
- package/src/loader/cost.ts +104 -0
- package/src/loader/index.ts +930 -0
- package/src/loader/ops.ts +657 -0
- package/src/native/ffi.ts +1541 -0
- package/src/native/index.ts +8 -207
- package/src/native/loader.ts +194 -0
- package/src/native/types.ts +598 -0
- package/src/rust-ffi/addon.ts +10 -0
- package/src/rust-ffi/batch.ts +431 -0
- package/src/rust-ffi/client.ts +88 -0
- package/src/rust-ffi/context.ts +164 -0
- package/src/rust-ffi/index.ts +40 -541
- package/src/rust-ffi/options.ts +113 -0
- package/src/rust-ffi/packed.ts +253 -0
- package/src/rust-ffi/proven.ts +42 -0
- package/src/rust-ffi/scalar/crypto.ts +265 -0
- package/src/rust-ffi/scalar/factories.ts +233 -0
- package/src/rust-ffi/scalar/hashing.ts +72 -0
- package/src/rust-ffi/scalar/http.ts +206 -0
- package/src/rust-ffi/scalar/index.ts +31 -0
- package/src/rust-ffi/scalar/interface.ts +450 -0
- package/src/rust-ffi/scalar/json.ts +45 -0
- package/src/rust-ffi/scalar/payload.ts +124 -0
- package/src/rust-ffi/text.ts +95 -0
- package/src/selection.json +194 -0
- package/src/selection.ts +77 -0
- package/src/shared/adaptive.ts +68 -0
- package/src/shared/bench-classify.ts +209 -0
- package/src/shared/buffer-pool.ts +209 -0
- package/src/shared/bytes.ts +35 -5
- package/src/shared/env.ts +20 -0
- package/src/shared/log.ts +74 -0
- package/src/shared/metrics.ts +247 -0
- package/src/shared/packed.ts +350 -154
- package/src/shared/proven.ts +596 -0
- package/src/shared/request-id.ts +67 -0
- package/src/shared/response.ts +104 -0
- package/src/shared/runtime.ts +34 -0
- package/src/shared/trace.ts +81 -0
- package/src/shared/uuid.ts +31 -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/ingress/packed-input.ts +0 -59
- package/src/shared/json.ts +0 -18
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
// src/rust-ffi/scalar/interface.ts — The `RustScalar` method surface.
|
|
2
|
+
//
|
|
3
|
+
// One file per public method (with its perf JSDoc, written by
|
|
4
|
+
// `scripts/annotate-performance.ts`). The implementation is split into domain
|
|
5
|
+
// builders (hashing, json, http, crypto, payload, factories) composed by
|
|
6
|
+
// `buildScalar` in index.ts.
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
HmacSignerInstance,
|
|
10
|
+
SchemaValidatorInstance,
|
|
11
|
+
TemplateRendererInstance,
|
|
12
|
+
FormParserInstance,
|
|
13
|
+
MediaTypeParserInstance,
|
|
14
|
+
MediaTypeResult,
|
|
15
|
+
MediaTypeMatcherInstance,
|
|
16
|
+
ConditionalRequestInstance,
|
|
17
|
+
EncodingPrefResult,
|
|
18
|
+
AcceptNegotiatorInstance,
|
|
19
|
+
Base64CodecInstance,
|
|
20
|
+
CookieSignerInstance,
|
|
21
|
+
CsrfProtectorInstance,
|
|
22
|
+
UrlBuilderInstance,
|
|
23
|
+
JwtSignerInstance,
|
|
24
|
+
AeadCipherInstance,
|
|
25
|
+
Argon2HasherInstance,
|
|
26
|
+
RateLimiterInstance,
|
|
27
|
+
MultipartPart,
|
|
28
|
+
WsFrame,
|
|
29
|
+
PasswordHashOptions,
|
|
30
|
+
} from '../../native'
|
|
31
|
+
|
|
32
|
+
/** Scalar + factory + runtime methods of the Rust client. */
|
|
33
|
+
export interface RustScalar {
|
|
34
|
+
// ── Scalar utilities (bytes in → normalized out) ──
|
|
35
|
+
/**
|
|
36
|
+
* @performance CRC32: ~3.7x slower than the JS baseline (2/3 tasks failed) [check:annotate]
|
|
37
|
+
* @deprecated Slower than the native JS baseline (~3.7x) — prefer the JS/Bun baseline. [check:annotate]
|
|
38
|
+
* @remarks Sub-µs FFI-crossing bound vs the same-engine JS crc-32 loop (~0.23-0.44x across runs); use the packed crc32BatchPacked path which wins on larger inputs. [check:annotate]
|
|
39
|
+
*/
|
|
40
|
+
crc32(input: Uint8Array): number
|
|
41
|
+
/**
|
|
42
|
+
* @performance FNV-1a 64: ~6.6x faster than the JS baseline [check:annotate]
|
|
43
|
+
*/
|
|
44
|
+
fnv1a64(input: Uint8Array): bigint
|
|
45
|
+
/**
|
|
46
|
+
* XXH3-64 over raw bytes (high-throughput non-cryptographic hash).
|
|
47
|
+
* Compare against `Bun.hash.xxHash3` — see docs/bun-builtins-decision-matrix.md.
|
|
48
|
+
*/
|
|
49
|
+
xxh3(input: Uint8Array): bigint
|
|
50
|
+
/**
|
|
51
|
+
* @performance HMAC sign: ~1.1x faster than the JS baseline (2/3 tasks won) [check:annotate]
|
|
52
|
+
*/
|
|
53
|
+
hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array
|
|
54
|
+
/**
|
|
55
|
+
* @performance HMAC verify: ~2.5x faster than the JS baseline [check:annotate]
|
|
56
|
+
*/
|
|
57
|
+
hmacSha256Verify(key: Uint8Array, data: Uint8Array, sig: Uint8Array): boolean
|
|
58
|
+
/**
|
|
59
|
+
* @performance JSON valid: ~3.2x faster than the JS baseline (8/8 tasks won) [check:annotate]
|
|
60
|
+
*/
|
|
61
|
+
jsonValid(input: Uint8Array): boolean
|
|
62
|
+
/**
|
|
63
|
+
* Parse JSON to a JS value (native sonic-rs DOM → napi). Throws on invalid JSON.
|
|
64
|
+
* @performance JSON parse: ~5.0x slower than the JS baseline [check:annotate]
|
|
65
|
+
* @deprecated Slower than the native JS baseline (~5.0x) — prefer the JS/Bun baseline. [check:annotate]
|
|
66
|
+
* @remarks Bun's JSON.parse beats the native DOM + napi marshaling path (~5x). [check:annotate]
|
|
67
|
+
*/
|
|
68
|
+
jsonParse(input: Uint8Array): unknown
|
|
69
|
+
/**
|
|
70
|
+
* @performance JSON sum: ~2.9x faster than the JS baseline (4/4 tasks won) [check:annotate]
|
|
71
|
+
*/
|
|
72
|
+
jsonSumIds(input: Uint8Array): bigint
|
|
73
|
+
/**
|
|
74
|
+
* @performance JSON Patch: ~1.2x faster than the JS baseline [check:annotate]
|
|
75
|
+
* @remarks Modest but real win; swings ~0.9-1.5x run-to-run (DOM marshal dominated). [check:annotate]
|
|
76
|
+
*/
|
|
77
|
+
jsonPatch(doc: Uint8Array, patch: Uint8Array): Uint8Array
|
|
78
|
+
/**
|
|
79
|
+
* @performance MIME lookup: ≈ parity with the JS baseline [check:annotate]
|
|
80
|
+
* @remarks Noisy sub-µs op: wins ~1.6x on good runs, loses on bad runs; phf table lookup. [check:annotate]
|
|
81
|
+
*/
|
|
82
|
+
mimeFromExtension(ext: Uint8Array): Uint8Array
|
|
83
|
+
/**
|
|
84
|
+
* @performance Random token: ≈ parity with the JS baseline [check:annotate]
|
|
85
|
+
*/
|
|
86
|
+
randomToken(byteLen: number): Uint8Array
|
|
87
|
+
/**
|
|
88
|
+
* @performance URL encode: ~2.2x slower than the JS baseline (2/3 tasks failed) [check:annotate]
|
|
89
|
+
* @deprecated Slower than the native JS baseline (~2.2x) — prefer the JS/Bun baseline. [check:annotate]
|
|
90
|
+
* @remarks Loses ~2.8x on the shipped baseline release build (only wins in the LOCAL SIMD perf build). [check:annotate]
|
|
91
|
+
*/
|
|
92
|
+
urlEncode(input: Uint8Array): Uint8Array
|
|
93
|
+
/**
|
|
94
|
+
* @performance URL decode: ~2.2x slower than the JS baseline (2/3 tasks failed) [check:annotate]
|
|
95
|
+
* @deprecated Slower than the native JS baseline (~2.2x) — prefer the JS/Bun baseline. [check:annotate]
|
|
96
|
+
* @remarks Loses ~2.8x on the shipped baseline release build (only wins in the LOCAL SIMD perf build). [check:annotate]
|
|
97
|
+
*/
|
|
98
|
+
urlDecode(input: Uint8Array): Uint8Array
|
|
99
|
+
/** Strict percent-decode without UTF-8 validation. */
|
|
100
|
+
urlDecodeBytes(input: Uint8Array): Uint8Array
|
|
101
|
+
/** Reusable-output percent-encode; returns bytes written. */
|
|
102
|
+
urlEncodeInto(input: Uint8Array, output: Uint8Array): number
|
|
103
|
+
/** Reusable-output percent-decode; returns bytes written. */
|
|
104
|
+
urlDecodeInto(input: Uint8Array, output: Uint8Array): number
|
|
105
|
+
/**
|
|
106
|
+
* @performance Email validation: ~3.1x faster than the JS baseline (4/4 tasks won) [check:annotate]
|
|
107
|
+
*/
|
|
108
|
+
validateEmail(input: Uint8Array): boolean
|
|
109
|
+
/**
|
|
110
|
+
* @performance UUID validation: ≈ parity with the JS baseline (2/4 tasks won) [check:annotate]
|
|
111
|
+
*/
|
|
112
|
+
validateUuid(input: Uint8Array): boolean
|
|
113
|
+
/**
|
|
114
|
+
* @performance IPv4 validation: ≈ parity with the JS baseline (2/2 tasks won) [check:annotate]
|
|
115
|
+
*/
|
|
116
|
+
validateIpv4(input: Uint8Array): boolean
|
|
117
|
+
/**
|
|
118
|
+
* @performance IPv6 validation: ~1.9x faster than the JS baseline [check:annotate]
|
|
119
|
+
*/
|
|
120
|
+
validateIpv6(input: Uint8Array): boolean
|
|
121
|
+
/**
|
|
122
|
+
* @performance WebSocket accept: ~1.9x faster than the JS baseline (1/2 tasks won) [check:annotate]
|
|
123
|
+
*/
|
|
124
|
+
wsAcceptKey(key: Uint8Array): Uint8Array
|
|
125
|
+
|
|
126
|
+
// ── Low-level / packed scalar ──
|
|
127
|
+
/**
|
|
128
|
+
* @performance HTTP parse: ~5.3x faster than the JS baseline (6/6 tasks won) [check:annotate]
|
|
129
|
+
*/
|
|
130
|
+
httpParseRequestPacked(input: Uint8Array): Uint8Array
|
|
131
|
+
/**
|
|
132
|
+
* @performance Query parse: ~3.8x faster than the JS baseline (6/6 tasks won) [check:annotate]
|
|
133
|
+
*/
|
|
134
|
+
queryParsePacked(input: Uint8Array): Uint8Array
|
|
135
|
+
/**
|
|
136
|
+
* @performance Cookie parse: ~2.6x faster than the JS baseline (5/5 tasks won) [check:annotate]
|
|
137
|
+
*/
|
|
138
|
+
cookieParsePacked(input: Uint8Array): Uint8Array
|
|
139
|
+
/** Reusable-output packed HTTP parse; returns bytes written. */
|
|
140
|
+
httpParseRequestPackedInto(input: Uint8Array, output: Uint8Array): number
|
|
141
|
+
/** Reusable-output packed query parse; returns bytes written. */
|
|
142
|
+
queryParsePackedInto(input: Uint8Array, output: Uint8Array): number
|
|
143
|
+
/** Reusable-output packed cookie parse; returns bytes written. */
|
|
144
|
+
cookieParsePackedInto(input: Uint8Array, output: Uint8Array): number
|
|
145
|
+
/**
|
|
146
|
+
* Parse an application/x-www-form-urlencoded body into packed pairs.
|
|
147
|
+
* @performance Form parse: ~4.7x faster than the JS baseline (2/2 tasks won) [check:annotate]
|
|
148
|
+
* @remarks Benchmarked via the FormParser instance (same parser core). [check:annotate]
|
|
149
|
+
*/
|
|
150
|
+
formParsePacked(input: Uint8Array): Uint8Array
|
|
151
|
+
/**
|
|
152
|
+
* Parse a Content-Type header into a structured media type.
|
|
153
|
+
* @performance Media type parse: ~1.3x slower than the JS baseline [check:annotate]
|
|
154
|
+
* @deprecated Slower than the native JS baseline (~1.3x) — prefer the JS/Bun baseline. [check:annotate]
|
|
155
|
+
* @remarks Consistent ~0.71-0.81x (sub-µs FFI crossing vs the JS baseline); use MediaTypeMatcher (precompiled expected) for the zero-alloc match path. [check:annotate]
|
|
156
|
+
*/
|
|
157
|
+
parseMediaType(input: Uint8Array): MediaTypeResult
|
|
158
|
+
/**
|
|
159
|
+
* Generate a strong/weak ETag (crc32-based).
|
|
160
|
+
* @performance ETag: ≈ parity with the JS baseline [check:annotate]
|
|
161
|
+
*/
|
|
162
|
+
etag(data: Uint8Array, weak?: boolean): Uint8Array
|
|
163
|
+
/** Reusable-output etag; returns bytes written (10 strong / 12 weak). */
|
|
164
|
+
etagInto(data: Uint8Array, output: Uint8Array, weak?: boolean): number
|
|
165
|
+
/**
|
|
166
|
+
* Format a unix timestamp as an IMF-fixdate HTTP-date.
|
|
167
|
+
* @performance HTTP date: ~1.5x slower than the JS baseline (2/2 tasks failed) [check:annotate]
|
|
168
|
+
* @deprecated Slower than the native JS baseline (~1.5x) — prefer the JS/Bun baseline. [check:annotate]
|
|
169
|
+
* @remarks Date.toUTCString() is native (~1.5-1.7x loss); pooled httpDateInto cuts it to ~1.3x. [check:annotate]
|
|
170
|
+
*/
|
|
171
|
+
httpDate(secs?: number): Uint8Array
|
|
172
|
+
/** Reusable-output http-date; returns bytes written (29). Throws if `output` is too small or the year is out of the fixed-width range. */
|
|
173
|
+
httpDateInto(secs: number | undefined, output: Uint8Array): number
|
|
174
|
+
/** Parse an IMF-fixdate HTTP-date back to unix seconds. */
|
|
175
|
+
parseHttpDate(input: Uint8Array): bigint | null
|
|
176
|
+
/** Parse an Accept-Encoding header into ordered preferences. */
|
|
177
|
+
parseAcceptEncoding(input: Uint8Array): EncodingPrefResult[]
|
|
178
|
+
/**
|
|
179
|
+
* Base64 encode (standard by default; url-safe/padding configurable).
|
|
180
|
+
* @performance Base64 encode: ~1.6x slower than the JS baseline [check:annotate]
|
|
181
|
+
* @deprecated Slower than the native JS baseline (~1.6x) — prefer the JS/Bun baseline. [check:annotate]
|
|
182
|
+
* @remarks Bun Buffer base64 is SIMD; loses ~1.5x. [check:annotate]
|
|
183
|
+
*/
|
|
184
|
+
base64Encode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array
|
|
185
|
+
/**
|
|
186
|
+
* Base64 decode (standard); throws on invalid input.
|
|
187
|
+
* @performance Base64 decode: ≈ parity with the JS baseline [check:annotate]
|
|
188
|
+
*/
|
|
189
|
+
base64Decode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array
|
|
190
|
+
/** Reusable-output base64 encode; returns bytes written. */
|
|
191
|
+
base64EncodeInto(
|
|
192
|
+
input: Uint8Array,
|
|
193
|
+
output: Uint8Array,
|
|
194
|
+
urlSafe?: boolean,
|
|
195
|
+
padding?: boolean,
|
|
196
|
+
): number
|
|
197
|
+
/** Reusable-output base64 decode; returns bytes written. Throws on invalid input. */
|
|
198
|
+
base64DecodeInto(
|
|
199
|
+
input: Uint8Array,
|
|
200
|
+
output: Uint8Array,
|
|
201
|
+
urlSafe?: boolean,
|
|
202
|
+
padding?: boolean,
|
|
203
|
+
): number
|
|
204
|
+
base64UrlEncode(input: Uint8Array): Uint8Array
|
|
205
|
+
base64UrlDecode(input: Uint8Array): Uint8Array
|
|
206
|
+
/**
|
|
207
|
+
* Lowercase hex encode.
|
|
208
|
+
* @performance Hex encode: ~1.4x slower than the JS baseline (1/2 tasks failed) [check:annotate]
|
|
209
|
+
* @deprecated Slower than the native JS baseline (~1.4x) — prefer the JS/Bun baseline. [check:annotate]
|
|
210
|
+
* @remarks Buffer.toString('hex') is native SIMD; pooled hexEncodeInto cuts the loss from ~2.2x to ~1.35x. [check:annotate]
|
|
211
|
+
*/
|
|
212
|
+
hexEncode(input: Uint8Array): Uint8Array
|
|
213
|
+
/**
|
|
214
|
+
* Hex decode; throws on odd length or invalid digits.
|
|
215
|
+
* @performance Hex decode: ≈ parity with the JS baseline [check:annotate]
|
|
216
|
+
* @remarks Slightly slower than Buffer hex decode (~1.3x). [check:annotate]
|
|
217
|
+
*/
|
|
218
|
+
hexDecode(input: Uint8Array): Uint8Array
|
|
219
|
+
/** Reusable-output lowercase-hex encode; returns bytes written. */
|
|
220
|
+
hexEncodeInto(input: Uint8Array, output: Uint8Array): number
|
|
221
|
+
/** Reusable-output hex decode; returns bytes written. Throws on bad input. */
|
|
222
|
+
hexDecodeInto(input: Uint8Array, output: Uint8Array): number
|
|
223
|
+
/** Reusable-output HMAC-SHA256 hex (64 bytes); returns bytes written. Throws on too-small buffer. */
|
|
224
|
+
hmacSha256Into(key: Uint8Array, data: Uint8Array, output: Uint8Array): number
|
|
225
|
+
/** Reusable-output cookie sign (`value.<64-hex>`); returns bytes written. Throws on too-small buffer. */
|
|
226
|
+
signCookieInto(value: Uint8Array, secret: Uint8Array, output: Uint8Array): number
|
|
227
|
+
/** Reusable-output AEAD encrypt (ct + 16-byte tag); returns bytes written. Throws on too-small buffer. */
|
|
228
|
+
aeadEncryptInto(
|
|
229
|
+
key: Uint8Array,
|
|
230
|
+
nonce: Uint8Array,
|
|
231
|
+
plaintext: Uint8Array,
|
|
232
|
+
output: Uint8Array,
|
|
233
|
+
algorithm?: string | null,
|
|
234
|
+
): number
|
|
235
|
+
/** Reusable-output WS frame encode; returns bytes written. Throws on too-small buffer. */
|
|
236
|
+
wsFrameEncodeInto(
|
|
237
|
+
opcode: number,
|
|
238
|
+
payload: Uint8Array,
|
|
239
|
+
mask: boolean,
|
|
240
|
+
fin: boolean,
|
|
241
|
+
output: Uint8Array,
|
|
242
|
+
): number
|
|
243
|
+
/** Reusable-output gzip compress; returns bytes written. Throws on too-small buffer. */
|
|
244
|
+
gzipCompressInto(data: Uint8Array, output: Uint8Array, level?: number | null): number
|
|
245
|
+
/** Reusable-output brotli compress; returns bytes written. Throws on too-small buffer. */
|
|
246
|
+
brotliCompressInto(data: Uint8Array, output: Uint8Array, quality?: number | null): number
|
|
247
|
+
/**
|
|
248
|
+
* Sign a cookie value as `value.signature` (HMAC-SHA256 hex).
|
|
249
|
+
* @performance Cookie sign: ~1.0x faster than the JS baseline [check:annotate]
|
|
250
|
+
*/
|
|
251
|
+
signCookie(value: Uint8Array, secret: Uint8Array): Uint8Array
|
|
252
|
+
/**
|
|
253
|
+
* Verify a signed cookie; returns the value or null.
|
|
254
|
+
* @performance Cookie verify: ~9.5x faster than the JS baseline [check:annotate]
|
|
255
|
+
*/
|
|
256
|
+
verifyCookie(signed: Uint8Array, secret: Uint8Array): Uint8Array | null
|
|
257
|
+
/**
|
|
258
|
+
* Create a CSRF token (32-byte random hex + HMAC signature).
|
|
259
|
+
* @performance CSRF create: ~13.7x faster than the JS baseline [check:annotate]
|
|
260
|
+
*/
|
|
261
|
+
csrfToken(secret: Uint8Array): Uint8Array
|
|
262
|
+
/**
|
|
263
|
+
* Constant-time verify a CSRF token.
|
|
264
|
+
* @performance CSRF verify: ~3.3x faster than the JS baseline [check:annotate]
|
|
265
|
+
*/
|
|
266
|
+
csrfVerify(token: Uint8Array, secret: Uint8Array): boolean
|
|
267
|
+
/**
|
|
268
|
+
* Resolve a URL reference against a base (RFC 3986).
|
|
269
|
+
* @performance URL resolve: ~1.8x slower than the JS baseline [check:annotate]
|
|
270
|
+
* @deprecated Slower than the native JS baseline (~1.8x) — prefer the JS/Bun baseline. [check:annotate]
|
|
271
|
+
* @remarks Auto-deprecated: 1/1 benchmarks failed in the latest run (static classification was parity). [check:annotate]
|
|
272
|
+
* @remarks Noisy ~1.5µs op; swings 0.74-1.03x run-to-run. UrlBuilder reuses the parsed base. [check:annotate]
|
|
273
|
+
*/
|
|
274
|
+
urlResolve(base: Uint8Array, reference: Uint8Array): Uint8Array
|
|
275
|
+
/**
|
|
276
|
+
* Build a percent-encoded query string from params (sorted keys).
|
|
277
|
+
* @performance URL query build: ~1.4x slower than the JS baseline [check:annotate]
|
|
278
|
+
* @deprecated Slower than the native JS baseline (~1.4x) — prefer the JS/Bun baseline. [check:annotate]
|
|
279
|
+
* @remarks encodeURIComponent baseline wins (~1.2-1.35x). [check:annotate]
|
|
280
|
+
*/
|
|
281
|
+
urlEncodeQuery(params: Record<string, string>): Uint8Array
|
|
282
|
+
|
|
283
|
+
// ── Factories / runtime ──
|
|
284
|
+
/**
|
|
285
|
+
* @performance JSON schema validate: ~1.7x faster than the JS baseline (2/2 tasks won) [check:annotate]
|
|
286
|
+
* @remarks Zero-DOM fast path validates raw bytes for the common keyword subset (scalar ~1.2-2.4x, batch ~1.0-1.56x vs ajv); unsupported keywords fall back to the jsonschema crate. [check:annotate]
|
|
287
|
+
*/
|
|
288
|
+
createSchemaValidator(schema: Uint8Array): SchemaValidatorInstance
|
|
289
|
+
createHmacSigner(key: Uint8Array): HmacSignerInstance
|
|
290
|
+
createTemplateRenderer(source: string): TemplateRendererInstance
|
|
291
|
+
/** Higher-order form parser: owns a reusable output buffer (setup once). */
|
|
292
|
+
createFormParser(capacity?: number): FormParserInstance
|
|
293
|
+
/** Higher-order media-type parser: reusable wildcard matcher. */
|
|
294
|
+
createMediaTypeParser(): MediaTypeParserInstance
|
|
295
|
+
/**
|
|
296
|
+
* Higher-order conditional-request check: per-resource 304 decision (setup once).
|
|
297
|
+
* @performance Conditional request: ~1.7x faster than the JS baseline [check:annotate]
|
|
298
|
+
*/
|
|
299
|
+
createConditionalRequest(
|
|
300
|
+
etagValue: Uint8Array,
|
|
301
|
+
lastModifiedSecs?: number,
|
|
302
|
+
): ConditionalRequestInstance
|
|
303
|
+
/**
|
|
304
|
+
* Higher-order negotiator: precompiles supported encodings once.
|
|
305
|
+
* @performance Accept-Encoding negotiate: ~4.2x faster than the JS baseline [check:annotate]
|
|
306
|
+
*/
|
|
307
|
+
createAcceptNegotiator(supported: string[]): AcceptNegotiatorInstance
|
|
308
|
+
/** Higher-order codec: base64 config compiled once. */
|
|
309
|
+
createBase64Codec(urlSafe?: boolean, padding?: boolean): Base64CodecInstance
|
|
310
|
+
/** Higher-order cookie signer: HMAC key compiled once. */
|
|
311
|
+
createCookieSigner(secret: Uint8Array): CookieSignerInstance
|
|
312
|
+
/** Higher-order CSRF protector: HMAC key compiled once. */
|
|
313
|
+
createCsrfProtector(secret: Uint8Array): CsrfProtectorInstance
|
|
314
|
+
/** Higher-order URL builder: base parsed once, reused. */
|
|
315
|
+
createUrlBuilder(base: Uint8Array): UrlBuilderInstance
|
|
316
|
+
/** Higher-order JWT signer: HS256 key + ttl compiled once. */
|
|
317
|
+
createJwtSigner(secret: Uint8Array, ttlSeconds?: number): JwtSignerInstance
|
|
318
|
+
/** Higher-order AEAD cipher: algorithm + key compiled once. */
|
|
319
|
+
createAeadCipher(key: Uint8Array, algorithm?: string): AeadCipherInstance
|
|
320
|
+
/** Higher-order argon2id hasher: params compiled once. */
|
|
321
|
+
createArgon2Hasher(options?: PasswordHashOptions | null): Argon2HasherInstance
|
|
322
|
+
/** Higher-order media-type matcher: expected precompiled once. */
|
|
323
|
+
createMediaTypeMatcher(expected: Uint8Array): MediaTypeMatcherInstance
|
|
324
|
+
/**
|
|
325
|
+
* Sharded fixed-window per-key rate limiter. `maxEntries` clamps internally
|
|
326
|
+
* (default 1,048,576). Each instance owns an independent budget.
|
|
327
|
+
* @remarks Scalar per-check cost: ~3-14x SLOWER than the JS Map baseline —
|
|
328
|
+
* prefer the ingress pipeline (one FFI for all stages) for native rate
|
|
329
|
+
* limiting in a request path. [measured 2026-08]
|
|
330
|
+
*/
|
|
331
|
+
createRateLimiter(
|
|
332
|
+
limit: number,
|
|
333
|
+
windowMs: number,
|
|
334
|
+
maxEntries?: number | null,
|
|
335
|
+
): RateLimiterInstance
|
|
336
|
+
initThreadPool(rayonThreads?: number): void
|
|
337
|
+
rayonNumThreads(): number
|
|
338
|
+
|
|
339
|
+
// ── Backend-framework scalar features ──
|
|
340
|
+
/**
|
|
341
|
+
* @performance JWT sign: ~1.1x slower than the JS baseline [check:annotate]
|
|
342
|
+
* @deprecated Slower than the native JS baseline (~1.1x) — prefer the JS/Bun baseline. [check:annotate]
|
|
343
|
+
* @remarks Base64 + claims DOM marshal overhead. Use jwtSignBytes (claims as JSON bytes) which skips the napi Value marshal and flips to a ~1.07x win. [check:annotate]
|
|
344
|
+
*/
|
|
345
|
+
jwtSign(
|
|
346
|
+
claims: Record<string, unknown>,
|
|
347
|
+
secret: Uint8Array,
|
|
348
|
+
ttlSeconds?: number | null,
|
|
349
|
+
nowSeconds?: number,
|
|
350
|
+
): Uint8Array
|
|
351
|
+
/**
|
|
352
|
+
* Sign a JWT from pre-serialized claim JSON bytes — skips the napi
|
|
353
|
+
* `serde_json::Value` marshal of `jwtSign` for callers that already hold the
|
|
354
|
+
* bytes (same injection semantics).
|
|
355
|
+
* @performance JWT sign (bytes): ≈ parity with the JS baseline [check:annotate]
|
|
356
|
+
* @remarks Byte-JSON claims overload avoids the napi serde_json::Value DOM marshal; flips jwtSign from a ~1.3x loss to ~1.07x (parity). [check:annotate]
|
|
357
|
+
*/
|
|
358
|
+
jwtSignBytes(
|
|
359
|
+
claimsJson: Uint8Array,
|
|
360
|
+
secret: Uint8Array,
|
|
361
|
+
ttlSeconds?: number | null,
|
|
362
|
+
nowSeconds?: number,
|
|
363
|
+
): Uint8Array
|
|
364
|
+
/**
|
|
365
|
+
* @performance JWT verify: ~1.7x faster than the JS baseline (2/2 tasks won) [check:annotate]
|
|
366
|
+
*/
|
|
367
|
+
jwtVerify(token: Uint8Array, secret: Uint8Array, nowSeconds?: number): unknown
|
|
368
|
+
/**
|
|
369
|
+
* @performance Password hash: ~18.2x faster than the JS baseline (2/2 tasks won) [check:annotate]
|
|
370
|
+
* @remarks argon2id vs node:crypto scrypt. [check:annotate]
|
|
371
|
+
*/
|
|
372
|
+
passwordHash(
|
|
373
|
+
password: Uint8Array,
|
|
374
|
+
salt: Uint8Array,
|
|
375
|
+
options?: PasswordHashOptions | null,
|
|
376
|
+
): Uint8Array
|
|
377
|
+
passwordVerify(password: Uint8Array, phc: Uint8Array): boolean
|
|
378
|
+
/** bcrypt password hash → `$2b$` PHC string (cost clamped 4..=31). */
|
|
379
|
+
passwordHashBcrypt(password: Uint8Array, cost: number): string
|
|
380
|
+
/** Verify a password against a bcrypt `$2b$` PHC string. */
|
|
381
|
+
passwordVerifyBcrypt(password: Uint8Array, hash: string): boolean
|
|
382
|
+
/** PBKDF2-HMAC-SHA256 key derivation (dkLen clamped to 1 MiB). */
|
|
383
|
+
pbkdf2Sha256(password: Uint8Array, salt: Uint8Array, rounds: number, dkLen: number): Uint8Array
|
|
384
|
+
/**
|
|
385
|
+
* @performance AEAD encrypt: ~1.3x faster than the JS baseline [check:annotate]
|
|
386
|
+
*/
|
|
387
|
+
aeadEncrypt(
|
|
388
|
+
key: Uint8Array,
|
|
389
|
+
nonce: Uint8Array,
|
|
390
|
+
plaintext: Uint8Array,
|
|
391
|
+
algorithm?: string | null,
|
|
392
|
+
): Uint8Array
|
|
393
|
+
/**
|
|
394
|
+
* @performance AEAD decrypt: ~1.4x faster than the JS baseline [check:annotate]
|
|
395
|
+
*/
|
|
396
|
+
aeadDecrypt(
|
|
397
|
+
key: Uint8Array,
|
|
398
|
+
nonce: Uint8Array,
|
|
399
|
+
ciphertext: Uint8Array,
|
|
400
|
+
algorithm?: string | null,
|
|
401
|
+
): Uint8Array | null
|
|
402
|
+
/**
|
|
403
|
+
* @performance Gzip compress: ≈ parity with the JS baseline (1/2 tasks won) [check:annotate]
|
|
404
|
+
*/
|
|
405
|
+
gzipCompress(data: Uint8Array, level?: number | null): Uint8Array
|
|
406
|
+
/**
|
|
407
|
+
* @performance Gzip decompress: ~1.0x faster than the JS baseline [check:annotate]
|
|
408
|
+
* @remarks zlib-rs vs node zlib; swings 1.0-1.4x run-to-run. [check:annotate]
|
|
409
|
+
* @param maxDecompressed Caps decompressed output (default 64 MiB) as a
|
|
410
|
+
* decompression-bomb guard; a larger value errors.
|
|
411
|
+
*/
|
|
412
|
+
gzipDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array
|
|
413
|
+
/**
|
|
414
|
+
* @performance Brotli compress: ~1.7x slower than the JS baseline [check:annotate]
|
|
415
|
+
* @deprecated Slower than the native JS baseline (~1.7x) — prefer the JS/Bun baseline. [check:annotate]
|
|
416
|
+
* @remarks The native brotli baseline wins for small inputs. [check:annotate]
|
|
417
|
+
*/
|
|
418
|
+
brotliCompress(data: Uint8Array, quality?: number | null): Uint8Array
|
|
419
|
+
/**
|
|
420
|
+
* @performance Brotli decompress: ~1.4x slower than the JS baseline [check:annotate]
|
|
421
|
+
* @deprecated Slower than the native JS baseline (~1.4x) — prefer the JS/Bun baseline. [check:annotate]
|
|
422
|
+
* @remarks Auto-deprecated: 1/1 benchmarks failed in the latest run (static classification was proven). [check:annotate]
|
|
423
|
+
* @param maxDecompressed Caps decompressed output (default 64 MiB) as a
|
|
424
|
+
* decompression-bomb guard; a larger value errors.
|
|
425
|
+
*/
|
|
426
|
+
brotliDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array
|
|
427
|
+
/**
|
|
428
|
+
* @performance Multipart parse: ~1.9x faster than the JS baseline [check:annotate]
|
|
429
|
+
*/
|
|
430
|
+
multipartParse(body: Uint8Array, boundary: Uint8Array): MultipartPart[]
|
|
431
|
+
/** Zero-copy packed sibling of `multipartParse` (packed parts layout). */
|
|
432
|
+
multipartParsePacked(body: Uint8Array, boundary: Uint8Array): Uint8Array
|
|
433
|
+
/**
|
|
434
|
+
* @performance WS frame encode: ~2.0x faster than the JS baseline [check:annotate]
|
|
435
|
+
*/
|
|
436
|
+
wsFrameEncode(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean): Uint8Array
|
|
437
|
+
/**
|
|
438
|
+
* @performance WS frame decode: ~2.3x faster than the JS baseline [check:annotate]
|
|
439
|
+
*/
|
|
440
|
+
wsFrameDecode(data: Uint8Array): WsFrame | null
|
|
441
|
+
/**
|
|
442
|
+
* @performance SSE encode: ≈ parity with the JS baseline [check:annotate]
|
|
443
|
+
*/
|
|
444
|
+
sseEncodeEvent(
|
|
445
|
+
event: string | null,
|
|
446
|
+
data: Uint8Array,
|
|
447
|
+
id: string | null,
|
|
448
|
+
retry: number | null,
|
|
449
|
+
): Uint8Array
|
|
450
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/rust-ffi/scalar/json.ts — JSON scalar methods.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors rust/json/*: validate, DOM parse, id-sum and RFC 6902 patch.
|
|
4
|
+
|
|
5
|
+
import { getBunFFI } from '../../native/ffi'
|
|
6
|
+
import type { RustClientContext } from '../context'
|
|
7
|
+
import { asBigInt } from '../options'
|
|
8
|
+
|
|
9
|
+
/** JSON scalar methods (`Pick<RustScalar, ...>`). */
|
|
10
|
+
export function buildJson(ctx: RustClientContext) {
|
|
11
|
+
const { addon } = ctx
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
jsonValid(input: Uint8Array): boolean {
|
|
15
|
+
const ffi = getBunFFI()
|
|
16
|
+
if (ffi) return ffi.jsonValid(input)
|
|
17
|
+
return addon.jsonValid(input)
|
|
18
|
+
},
|
|
19
|
+
jsonParse(input: Uint8Array): unknown {
|
|
20
|
+
return addon.jsonParse(input)
|
|
21
|
+
},
|
|
22
|
+
jsonSumIds(input: Uint8Array): bigint {
|
|
23
|
+
// The C ABI returns a packed [u8 ok][i64 sum LE] output, so a legit
|
|
24
|
+
// zero-sum and invalid input are unambiguous — no napi re-dispatch.
|
|
25
|
+
const ffi = getBunFFI()
|
|
26
|
+
if (ffi) return ffi.jsonSumIds(input)
|
|
27
|
+
return asBigInt(addon.jsonSumIds(input) as unknown)
|
|
28
|
+
},
|
|
29
|
+
jsonPatch(doc: Uint8Array, patch: Uint8Array): Uint8Array {
|
|
30
|
+
const ffi = getBunFFI()
|
|
31
|
+
if (ffi) {
|
|
32
|
+
try {
|
|
33
|
+
return ffi.jsonPatch(doc, patch)
|
|
34
|
+
} catch {
|
|
35
|
+
// The C ABI's 0 is ambiguous (too-small buffer vs a real error) and
|
|
36
|
+
// carries no message. Re-dispatch to napi for exact error semantics:
|
|
37
|
+
// it either succeeds (too-small case) or throws the contextual
|
|
38
|
+
// "invalid document"/"invalid patch"/"apply failed" message.
|
|
39
|
+
return addon.jsonPatch(doc, patch)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return addon.jsonPatch(doc, patch)
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// src/rust-ffi/scalar/payload.ts — Output / streaming scalar methods.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors rust/payload/*: gzip/brotli compress, multipart parse, WebSocket
|
|
4
|
+
// frame codec and SSE event framing.
|
|
5
|
+
|
|
6
|
+
import type { MultipartPart, WsFrame } from '../../native'
|
|
7
|
+
import { getBunFFI } from '../../native/ffi'
|
|
8
|
+
import { isBun } from '../../shared/runtime'
|
|
9
|
+
import type { RustClientContext } from '../context'
|
|
10
|
+
|
|
11
|
+
/** Output / streaming scalar methods (`Pick<RustScalar, ...>`). */
|
|
12
|
+
export function buildPayload(ctx: RustClientContext) {
|
|
13
|
+
const { addon } = ctx
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
gzipCompress(data: Uint8Array, level?: number | null): Uint8Array {
|
|
17
|
+
// Optimal by default under Bun: `Bun.gzipSync` (native zlib) is ~2x
|
|
18
|
+
// faster than the rust+FFI crossing (decision matrix). Emits valid gzip
|
|
19
|
+
// (decompression-parity with the addon — only the header OS byte
|
|
20
|
+
// differs: 0xFF vs 0x03). `gzipCompressInto` (pooled) stays native.
|
|
21
|
+
if (isBun()) {
|
|
22
|
+
const opts = level != null ? ({ level } as Parameters<typeof Bun.gzipSync>[1]) : undefined
|
|
23
|
+
return Bun.gzipSync(data as unknown as Uint8Array<ArrayBuffer>, opts)
|
|
24
|
+
}
|
|
25
|
+
const ffi = getBunFFI()
|
|
26
|
+
if (ffi) return ffi.gzipCompress(data, level ?? undefined)
|
|
27
|
+
return addon.gzipCompress(data, level ?? null)
|
|
28
|
+
},
|
|
29
|
+
gzipCompressInto(data: Uint8Array, output: Uint8Array, level?: number | null): number {
|
|
30
|
+
const ffi = getBunFFI()
|
|
31
|
+
if (ffi) return ffi.gzipCompressInto(data, output, level ?? undefined)
|
|
32
|
+
const bytes = addon.gzipCompress(data, level ?? null)
|
|
33
|
+
if (output.length < bytes.length) throw new Error('gzip compress: output buffer too small')
|
|
34
|
+
output.set(bytes)
|
|
35
|
+
return bytes.length
|
|
36
|
+
},
|
|
37
|
+
gzipDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array {
|
|
38
|
+
const ffi = getBunFFI()
|
|
39
|
+
if (ffi) return ffi.gzipDecompress(data, maxDecompressed ?? undefined)
|
|
40
|
+
return addon.gzipDecompress(data, maxDecompressed ?? null)
|
|
41
|
+
},
|
|
42
|
+
brotliCompress(data: Uint8Array, quality?: number | null): Uint8Array {
|
|
43
|
+
const ffi = getBunFFI()
|
|
44
|
+
if (ffi) return ffi.brotliCompress(data, quality ?? undefined)
|
|
45
|
+
return addon.brotliCompress(data, quality ?? null)
|
|
46
|
+
},
|
|
47
|
+
brotliCompressInto(data: Uint8Array, output: Uint8Array, quality?: number | null): number {
|
|
48
|
+
const ffi = getBunFFI()
|
|
49
|
+
if (ffi) return ffi.brotliCompressInto(data, output, quality ?? undefined)
|
|
50
|
+
const bytes = addon.brotliCompress(data, quality ?? null)
|
|
51
|
+
if (output.length < bytes.length) throw new Error('brotli compress: output buffer too small')
|
|
52
|
+
output.set(bytes)
|
|
53
|
+
return bytes.length
|
|
54
|
+
},
|
|
55
|
+
brotliDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array {
|
|
56
|
+
const ffi = getBunFFI()
|
|
57
|
+
if (ffi) return ffi.brotliDecompress(data, maxDecompressed ?? undefined)
|
|
58
|
+
return addon.brotliDecompress(data, maxDecompressed ?? null)
|
|
59
|
+
},
|
|
60
|
+
multipartParse(body: Uint8Array, boundary: Uint8Array): MultipartPart[] {
|
|
61
|
+
// Normalize napi `Option<String>` (undefined) → null and expose the
|
|
62
|
+
// camelCase `contentType` key (napi renames `content_type` to camelCase).
|
|
63
|
+
return addon.multipartParse(body, boundary).map((p) => ({
|
|
64
|
+
name: p.name,
|
|
65
|
+
filename: p.filename ?? null,
|
|
66
|
+
contentType: p.contentType ?? null,
|
|
67
|
+
data: p.data,
|
|
68
|
+
}))
|
|
69
|
+
},
|
|
70
|
+
multipartParsePacked(body: Uint8Array, boundary: Uint8Array): Uint8Array {
|
|
71
|
+
const ffi = getBunFFI()
|
|
72
|
+
if (ffi) return ffi.multipartParsePacked(body, boundary)
|
|
73
|
+
// Zero-copy packed sibling — no JS objects per part (see native types).
|
|
74
|
+
return addon.multipartParsePacked(body, boundary)
|
|
75
|
+
},
|
|
76
|
+
wsFrameEncode(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean): Uint8Array {
|
|
77
|
+
const ffi = getBunFFI()
|
|
78
|
+
if (ffi) return ffi.wsFrameEncode(opcode, payload, mask, fin)
|
|
79
|
+
return addon.wsFrameEncode(opcode, payload, mask, fin)
|
|
80
|
+
},
|
|
81
|
+
wsFrameEncodeInto(
|
|
82
|
+
opcode: number,
|
|
83
|
+
payload: Uint8Array,
|
|
84
|
+
mask: boolean,
|
|
85
|
+
fin: boolean,
|
|
86
|
+
output: Uint8Array,
|
|
87
|
+
): number {
|
|
88
|
+
const ffi = getBunFFI()
|
|
89
|
+
if (ffi) return ffi.wsFrameEncodeInto(opcode, payload, mask, fin, output)
|
|
90
|
+
const bytes = addon.wsFrameEncode(opcode, payload, mask, fin)
|
|
91
|
+
if (output.length < bytes.length) throw new Error('ws frame encode: output buffer too small')
|
|
92
|
+
output.set(bytes)
|
|
93
|
+
return bytes.length
|
|
94
|
+
},
|
|
95
|
+
wsFrameDecode(data: Uint8Array): WsFrame | null {
|
|
96
|
+
const ffi = getBunFFI()
|
|
97
|
+
if (ffi) {
|
|
98
|
+
// C ABI returns packed [flags][opcode][u32 len][payload]; decode here.
|
|
99
|
+
const packed = ffi.wsFrameDecodePacked(data)
|
|
100
|
+
if (packed === null) return null
|
|
101
|
+
const payloadLen =
|
|
102
|
+
((packed[2] ?? 0) |
|
|
103
|
+
((packed[3] ?? 0) << 8) |
|
|
104
|
+
((packed[4] ?? 0) << 16) |
|
|
105
|
+
((packed[5] ?? 0) << 24)) >>>
|
|
106
|
+
0
|
|
107
|
+
return {
|
|
108
|
+
fin: (packed[0] ?? 0) === 1,
|
|
109
|
+
opcode: packed[1] ?? 0,
|
|
110
|
+
payload: packed.subarray(6, 6 + payloadLen),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return addon.wsFrameDecode(data)
|
|
114
|
+
},
|
|
115
|
+
sseEncodeEvent(
|
|
116
|
+
event: string | null,
|
|
117
|
+
data: Uint8Array,
|
|
118
|
+
id: string | null,
|
|
119
|
+
retry: number | null,
|
|
120
|
+
): Uint8Array {
|
|
121
|
+
return addon.sseEncodeEvent(event ?? null, data, id ?? null, retry ?? null)
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
}
|