castrum 0.7.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -368
  3. package/castrum.linux-x64-gnu.node +0 -0
  4. package/dist/index.d.ts +2763 -0
  5. package/dist/index.js +8707 -0
  6. package/docs/API.md +337 -0
  7. package/docs/ARCHITECTURE.md +507 -0
  8. package/docs/BENCHMARKS.md +343 -0
  9. package/docs/CASE_STUDY.md +240 -0
  10. package/docs/ENVIRONMENT.md +81 -0
  11. package/docs/FFI_BUN_GUIDE.md +327 -0
  12. package/docs/GETTING_STARTED.md +192 -0
  13. package/docs/INGRESS-ROUTER.md +81 -0
  14. package/docs/INGRESS.md +746 -0
  15. package/docs/NATIVE-ROUTE.md +53 -0
  16. package/docs/REPO_MAP.md +279 -0
  17. package/docs/adr/0001-two-wire-formats.md +39 -0
  18. package/docs/adr/0002-zero-dom-fast-schema.md +38 -0
  19. package/docs/adr/0003-bun-builtins-delegation.md +43 -0
  20. package/docs/bun-builtins-decision-matrix.md +115 -0
  21. package/index.ts +44 -8
  22. package/package.json +98 -33
  23. package/src/ingress/body.ts +289 -0
  24. package/src/ingress/constants.ts +220 -48
  25. package/src/ingress/context.ts +136 -0
  26. package/src/ingress/decode/baked-result.ts +121 -0
  27. package/src/ingress/decode/fast-result.ts +160 -0
  28. package/src/ingress/decode/packed-sections.ts +56 -0
  29. package/src/ingress/decode/result-base.ts +172 -0
  30. package/src/ingress/errors.ts +72 -0
  31. package/src/ingress/fast.ts +152 -1021
  32. package/src/ingress/handlers.ts +639 -1001
  33. package/src/ingress/headers/baked-templates.ts +102 -0
  34. package/src/ingress/headers/cors.ts +44 -0
  35. package/src/ingress/headers/fast-templates.ts +238 -0
  36. package/src/ingress/headers/hsts.ts +83 -0
  37. package/src/ingress/health.ts +68 -0
  38. package/src/ingress/index.ts +142 -405
  39. package/src/ingress/metrics.ts +132 -0
  40. package/src/ingress/options.ts +197 -0
  41. package/src/ingress/packing/gather-raw-headers.ts +139 -0
  42. package/src/ingress/packing/header-packing.ts +75 -0
  43. package/src/ingress/packing/input-packer.ts +164 -0
  44. package/src/ingress/packing/scratch.ts +50 -0
  45. package/src/ingress/packing/select-headers.ts +102 -0
  46. package/src/ingress/response/error-bodies.ts +75 -0
  47. package/src/ingress/response/terminal.ts +59 -0
  48. package/src/ingress/router.ts +237 -0
  49. package/src/ingress/routes/common.ts +79 -0
  50. package/src/ingress/routes/delete.ts +14 -0
  51. package/src/ingress/routes/echo.ts +107 -0
  52. package/src/ingress/routes/fallback.ts +23 -0
  53. package/src/ingress/routes/head.ts +27 -0
  54. package/src/ingress/routes/index.ts +11 -0
  55. package/src/ingress/routes/json-write.ts +129 -0
  56. package/src/ingress/routes/options.ts +26 -0
  57. package/src/ingress/routes/read.ts +35 -0
  58. package/src/ingress/routes/responder.ts +159 -0
  59. package/src/ingress/server-node.ts +376 -0
  60. package/src/ingress/server.ts +475 -0
  61. package/src/ingress/shared.ts +132 -0
  62. package/src/ingress/status.ts +97 -0
  63. package/src/ingress/types.ts +191 -0
  64. package/src/integration/batch.ts +58 -0
  65. package/src/integration/index.ts +29 -0
  66. package/src/integration/pipeline.ts +220 -0
  67. package/src/integration/streaming.ts +78 -0
  68. package/src/integration/websocket.ts +93 -0
  69. package/src/loader/batch.ts +137 -0
  70. package/src/loader/cost.ts +105 -0
  71. package/src/loader/index.ts +745 -0
  72. package/src/loader/ops.ts +657 -0
  73. package/src/loader/state.ts +84 -0
  74. package/src/loader/types.ts +145 -0
  75. package/src/native/ffi/constants.ts +58 -0
  76. package/src/native/ffi/selftest.ts +729 -0
  77. package/src/native/ffi/types.ts +474 -0
  78. package/src/native/ffi.ts +1775 -0
  79. package/src/native/index.ts +8 -207
  80. package/src/native/loader.ts +240 -0
  81. package/src/native/types/instances.ts +274 -0
  82. package/src/native/types.ts +401 -0
  83. package/src/runtime/builtins.ts +131 -0
  84. package/src/runtime/codec.ts +147 -0
  85. package/src/runtime/detect.ts +36 -0
  86. package/src/runtime/index.ts +28 -0
  87. package/src/runtime/native.ts +35 -0
  88. package/src/runtime/server.ts +31 -0
  89. package/src/runtime/transport.ts +64 -0
  90. package/src/runtime/types.ts +173 -0
  91. package/src/runtime/uuid.ts +45 -0
  92. package/src/runtime/websocket.ts +30 -0
  93. package/src/rust-ffi/addon.ts +10 -0
  94. package/src/rust-ffi/batch/build.ts +155 -0
  95. package/src/rust-ffi/batch/index.ts +8 -0
  96. package/src/rust-ffi/batch/types.ts +92 -0
  97. package/src/rust-ffi/client.ts +87 -0
  98. package/src/rust-ffi/context.ts +193 -0
  99. package/src/rust-ffi/index.ts +51 -541
  100. package/src/rust-ffi/into.ts +21 -0
  101. package/src/rust-ffi/options.ts +113 -0
  102. package/src/rust-ffi/packed.ts +253 -0
  103. package/src/rust-ffi/proven.ts +28 -0
  104. package/src/rust-ffi/scalar/crypto.ts +346 -0
  105. package/src/rust-ffi/scalar/factories.ts +459 -0
  106. package/src/rust-ffi/scalar/hashing.ts +74 -0
  107. package/src/rust-ffi/scalar/http.ts +337 -0
  108. package/src/rust-ffi/scalar/index.ts +31 -0
  109. package/src/rust-ffi/scalar/interface.ts +325 -0
  110. package/src/rust-ffi/scalar/json-packed.ts +175 -0
  111. package/src/rust-ffi/scalar/json.ts +61 -0
  112. package/src/rust-ffi/scalar/payload.ts +241 -0
  113. package/src/rust-ffi/text.ts +121 -0
  114. package/src/selection.json +194 -0
  115. package/src/selection.ts +73 -0
  116. package/src/shared/adaptive.ts +67 -0
  117. package/src/shared/buffer-pool.ts +264 -0
  118. package/src/shared/bytes.ts +108 -9
  119. package/src/shared/codec.ts +78 -0
  120. package/src/shared/env.ts +20 -0
  121. package/src/shared/log.ts +75 -0
  122. package/src/shared/metrics.ts +247 -0
  123. package/src/shared/packed/index.ts +10 -0
  124. package/src/shared/packed/parsers.ts +31 -0
  125. package/src/shared/packed/schema.ts +23 -0
  126. package/src/shared/packed/wire.ts +504 -0
  127. package/src/shared/proven.ts +289 -0
  128. package/src/shared/request-id.ts +67 -0
  129. package/src/shared/response.ts +104 -0
  130. package/src/shared/runtime.ts +29 -0
  131. package/src/shared/trace.ts +81 -0
  132. package/src/shared/uuid.ts +15 -0
  133. package/Cargo.toml +0 -73
  134. package/bench.ts +0 -3
  135. package/rust/batch.rs +0 -195
  136. package/rust/cookie_parser.rs +0 -70
  137. package/rust/cors.rs +0 -263
  138. package/rust/hashing.rs +0 -164
  139. package/rust/headers.rs +0 -234
  140. package/rust/hmac_sha256.rs +0 -113
  141. package/rust/http_parser.rs +0 -132
  142. package/rust/ingress.rs +0 -616
  143. package/rust/ingress_constants.rs +0 -89
  144. package/rust/ip_trust.rs +0 -185
  145. package/rust/json_ops.rs +0 -66
  146. package/rust/json_patch_ops.rs +0 -25
  147. package/rust/json_schema.rs +0 -151
  148. package/rust/json_ser.rs +0 -429
  149. package/rust/lib.rs +0 -48
  150. package/rust/method.rs +0 -141
  151. package/rust/mime_lookup.rs +0 -76
  152. package/rust/output.rs +0 -238
  153. package/rust/proxy.rs +0 -90
  154. package/rust/query_parser.rs +0 -141
  155. package/rust/random_token.rs +0 -52
  156. package/rust/rate_limit.rs +0 -203
  157. package/rust/terminal.rs +0 -95
  158. package/rust/unit_tests.rs +0 -843
  159. package/rust/url_codec.rs +0 -133
  160. package/rust/util.rs +0 -537
  161. package/rust/validation.rs +0 -107
  162. package/rust/websocket.rs +0 -23
  163. package/src/baseline/index.ts +0 -12
  164. package/src/baseline/tasks/cookie.ts +0 -22
  165. package/src/baseline/tasks/hashing.ts +0 -18
  166. package/src/baseline/tasks/hmac.ts +0 -38
  167. package/src/baseline/tasks/http.ts +0 -62
  168. package/src/baseline/tasks/json-patch.ts +0 -12
  169. package/src/baseline/tasks/json.ts +0 -50
  170. package/src/baseline/tasks/mime.ts +0 -5
  171. package/src/baseline/tasks/query.ts +0 -14
  172. package/src/baseline/tasks/token.ts +0 -15
  173. package/src/baseline/tasks/url.ts +0 -11
  174. package/src/baseline/tasks/validation.ts +0 -19
  175. package/src/baseline/tasks/websocket.ts +0 -14
  176. package/src/bench/assert.ts +0 -70
  177. package/src/bench/checks.ts +0 -227
  178. package/src/bench/checksum.ts +0 -27
  179. package/src/bench/comparisons.ts +0 -70
  180. package/src/bench/concurrent-worker.ts +0 -135
  181. package/src/bench/fixtures.ts +0 -233
  182. package/src/bench/index.ts +0 -2
  183. package/src/bench/measure.ts +0 -177
  184. package/src/bench/now.ts +0 -3
  185. package/src/bench/report.ts +0 -79
  186. package/src/bench/run.ts +0 -57
  187. package/src/bench/tasks/complex.ts +0 -232
  188. package/src/bench/tasks/concurrent.ts +0 -213
  189. package/src/bench/tasks/cookie.ts +0 -44
  190. package/src/bench/tasks/hashing.ts +0 -33
  191. package/src/bench/tasks/hmac.ts +0 -36
  192. package/src/bench/tasks/http.ts +0 -50
  193. package/src/bench/tasks/index.ts +0 -52
  194. package/src/bench/tasks/json-patch.ts +0 -21
  195. package/src/bench/tasks/json.ts +0 -33
  196. package/src/bench/tasks/mime.ts +0 -21
  197. package/src/bench/tasks/query.ts +0 -44
  198. package/src/bench/tasks/stress.ts +0 -142
  199. package/src/bench/tasks/token.ts +0 -20
  200. package/src/bench/tasks/url.ts +0 -34
  201. package/src/bench/tasks/validation.ts +0 -57
  202. package/src/bench/tasks/websocket.ts +0 -21
  203. package/src/bench/types.ts +0 -60
  204. package/src/data/json-rows.ts +0 -33
  205. package/src/ingress/packed-input.ts +0 -59
  206. package/src/shared/json.ts +0 -18
  207. package/src/shared/packed.ts +0 -316
@@ -1,10 +1,19 @@
1
1
  // src/rust-ffi/index.ts — Flat, complete public API for the Rust utilities.
2
2
  //
3
- // This is the SINGLE implementation module for all Rust FFI functions. It wraps
4
- // the raw NAPI addon directly (no intermediate wrapper layer), exposes every
5
- // native function with normalized, easy-to-consume types, and selects optimized
6
- // defaults that can be overridden per-instance via createRust() or on the
7
- // shared `rust` instance via rust.configure().
3
+ // Barrel module. The implementation is decomposed into task-focused modules:
4
+ // - options.ts RustOptions + input-normalization helpers
5
+ // - addon.ts shared lazy addon proxy
6
+ // - context.ts per-instance state + shared namespace helpers
7
+ // - scalar/ scalar/feature methods (hashing, json, http, crypto, payload, factories)
8
+ // - text.ts string namespace
9
+ // - batch.ts array-of-bytes namespace
10
+ // - packed.ts raw packed-wire namespace
11
+ // - client.ts createRust() factory + default `rust` instance
12
+ //
13
+ // This barrel re-exports the exact public surface of the original single-file
14
+ // implementation so existing imports (`rust`, `createRust`, all types) keep
15
+ // working unchanged. The deprecated `rustBatch` alias was removed — use
16
+ // `rust.batch` instead.
8
17
  //
9
18
  // import { rust } from "castrum";
10
19
  //
@@ -16,540 +25,41 @@
16
25
  // rust.batch.jsonValid(docs); // Uint8Array bitset
17
26
  // rust.packed.jsonValidBatchPacked(p); // raw packed in/out
18
27
  // rust.configure({ rayonThreads: 8 }); // override defaults
19
- //
20
- // Back-compat alias (non-breaking):
21
- // rustBatch === rust.batch
22
-
23
- import addon, {
24
- type HmacSignerInstance,
25
- type SchemaValidatorInstance,
26
- } from "../native";
27
- import { decoder, encoder } from "../shared/bytes";
28
- import {
29
- packBatch,
30
- schemaValidateBatch,
31
- schemaValidateBatchCount,
32
- unpackBitset,
33
- unpackByteResults,
34
- unpackI64ArrayAsBigInt,
35
- unpackU32Array,
36
- type SchemaValidator,
37
- } from "../shared/packed";
38
-
39
- export type { HmacSignerInstance, SchemaValidatorInstance };
40
- export type { SchemaValidator };
41
-
42
- // ── Options & defaults ─────────────────────────────────────────
43
-
44
- export interface RustOptions {
45
- /**
46
- * Rayon thread pool size.
47
- *
48
- * The rayon pool is process-wide and initialized **once** (native
49
- * `OnceLock`) — the first initialization wins. Importing this module
50
- * initializes the pool with defaults, so this option only takes effect when
51
- * the pool has not yet been created. For reliable control set
52
- * `CASTRUM_RAYON_THREADS` / `RUST_RAYON_THREADS` before import.
53
- * Default: max(1, hardwareConcurrency - 1).
54
- */
55
- rayonThreads?: number;
56
- /** Cache MIME lookups keyed by normalized extension. Default: true. */
57
- mimeCache?: boolean;
58
- /** Reuse HMAC signers keyed by key-buffer identity. Default: true. */
59
- hmacCache?: boolean;
60
- }
61
-
62
- const FALLBACK_CORES = 4;
63
-
64
- function resolveRayonThreads(explicit?: number): number {
65
- const cores = navigator.hardwareConcurrency || FALLBACK_CORES;
66
-
67
- if (explicit !== undefined && Number.isFinite(explicit) && explicit > 0) {
68
- return Math.max(1, Math.min(cores, Math.floor(explicit)));
69
- }
70
-
71
- const envThreads = Number(
72
- process.env.CASTRUM_RAYON_THREADS ??
73
- process.env.RUST_BENCH_RAYON_THREADS ??
74
- process.env.RUST_RAYON_THREADS,
75
- );
76
-
77
- if (Number.isFinite(envThreads) && envThreads > 0) {
78
- return Math.max(1, Math.min(cores, Math.floor(envThreads)));
79
- }
80
-
81
- return Math.max(1, cores - 1);
82
- }
83
-
84
- function asBigInt(value: unknown): bigint {
85
- if (typeof value === "bigint") {
86
- return value;
87
- }
88
-
89
- if (typeof value === "number" && Number.isFinite(value)) {
90
- return BigInt(Math.trunc(value));
91
- }
92
-
93
- if (typeof value === "string" && value.trim().length > 0) {
94
- return BigInt(value);
95
- }
96
-
97
- throw new TypeError(
98
- `Expected bigint-compatible value, got ${typeof value}: ${String(value)}`,
99
- );
100
- }
101
-
102
- function asNumber(value: unknown): number {
103
- if (typeof value === "number") {
104
- return value;
105
- }
106
-
107
- if (typeof value === "bigint") {
108
- return Number(value);
109
- }
110
-
111
- if (typeof value === "string" && value.trim().length > 0) {
112
- return Number(value);
113
- }
114
-
115
- return 0;
116
- }
117
-
118
- function normalizeExt(ext: Uint8Array): string {
119
- let s: string;
120
-
121
- try {
122
- s = decoder.decode(ext);
123
- } catch {
124
- return "";
125
- }
126
-
127
- if (s.charCodeAt(0) === 46) {
128
- s = s.slice(1);
129
- }
130
-
131
- return s.toLowerCase();
132
- }
133
-
134
- // ── Namespace types ────────────────────────────────────────────
135
-
136
- export interface RustText {
137
- mimeFromExtension(ext: string): string;
138
- urlEncode(input: string): string;
139
- urlDecode(input: string): string;
140
- wsAcceptKey(key: string): string;
141
- validateEmail(input: string): boolean;
142
- validateUuid(input: string): boolean;
143
- validateIpv4(input: string): boolean;
144
- validateIpv6(input: string): boolean;
145
- }
146
-
147
- export interface RustBatch {
148
- jsonValid(items: Uint8Array[]): Uint8Array;
149
- crc32(items: Uint8Array[]): Uint32Array;
150
- validateEmail(items: Uint8Array[]): Uint8Array;
151
- validateUuid(items: Uint8Array[]): Uint8Array;
152
- validateIpv4(items: Uint8Array[]): Uint8Array;
153
- validateIpv6(items: Uint8Array[]): Uint8Array;
154
- jsonSumIds(items: Uint8Array[]): BigInt64Array;
155
- queryParse(items: Uint8Array[]): Uint8Array[];
156
- cookieParse(items: Uint8Array[]): Uint8Array[];
157
- httpParseRequest(items: Uint8Array[]): Uint8Array[];
158
- schemaValidate(validator: SchemaValidator, items: Uint8Array[]): Uint8Array;
159
- schemaValidateCount(validator: SchemaValidator, items: Uint8Array[]): number;
160
- }
161
-
162
- export interface RustPacked {
163
- // ── Sync batch (packed in → packed out) ──
164
- crc32BatchPacked(input: Uint8Array): Uint8Array;
165
- jsonValidBatchPacked(input: Uint8Array): Uint8Array;
166
- validateEmailBatchPacked(input: Uint8Array): Uint8Array;
167
- validateUuidBatchPacked(input: Uint8Array): Uint8Array;
168
- validateIpv4BatchPacked(input: Uint8Array): Uint8Array;
169
- validateIpv6BatchPacked(input: Uint8Array): Uint8Array;
170
- jsonSumBatchPacked(input: Uint8Array): Uint8Array;
171
- queryParseBatchPacked(input: Uint8Array): Uint8Array;
172
- cookieParseBatchPacked(input: Uint8Array): Uint8Array;
173
- httpParseRequestBatchPacked(input: Uint8Array): Uint8Array;
174
-
175
- // ── Batch metadata / counts ──
176
- jsonValidBatchCountPacked(input: Uint8Array): number;
177
- validateEmailBatchCountPacked(input: Uint8Array): number;
178
- validateUuidBatchCountPacked(input: Uint8Array): number;
179
- validateIpv4BatchCountPacked(input: Uint8Array): number;
180
- validateIpv6BatchCountPacked(input: Uint8Array): number;
181
- jsonSumBatchTotalPacked(input: Uint8Array): number;
182
- queryParseBatchTotalLenPacked(input: Uint8Array): number;
183
- cookieParseBatchTotalLenPacked(input: Uint8Array): number;
184
- httpParseRequestBatchTotalLenPacked(input: Uint8Array): number;
185
- }
186
-
187
- export interface RustClient {
188
- // ── Scalar utilities (bytes in → normalized out) ──
189
- crc32(input: Uint8Array): number;
190
- fnv1a64(input: Uint8Array): bigint;
191
- hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array;
192
- hmacSha256Verify(
193
- key: Uint8Array,
194
- data: Uint8Array,
195
- sig: Uint8Array,
196
- ): boolean;
197
- jsonValid(input: Uint8Array): boolean;
198
- jsonSumIds(input: Uint8Array): bigint;
199
- jsonPatch(doc: Uint8Array, patch: Uint8Array): Uint8Array;
200
- mimeFromExtension(ext: Uint8Array): Uint8Array;
201
- randomToken(byteLen: number): Uint8Array;
202
- urlEncode(input: Uint8Array): Uint8Array;
203
- urlDecode(input: Uint8Array): Uint8Array;
204
- /** Strict percent-decode without UTF-8 validation. */
205
- urlDecodeBytes(input: Uint8Array): Uint8Array;
206
- validateEmail(input: Uint8Array): boolean;
207
- validateUuid(input: Uint8Array): boolean;
208
- validateIpv4(input: Uint8Array): boolean;
209
- validateIpv6(input: Uint8Array): boolean;
210
- wsAcceptKey(key: Uint8Array): Uint8Array;
211
-
212
- // ── Low-level / packed scalar ──
213
- httpParseRequestPacked(input: Uint8Array): Uint8Array;
214
- queryParsePacked(input: Uint8Array): Uint8Array;
215
- cookieParsePacked(input: Uint8Array): Uint8Array;
216
-
217
- // ── Factories / runtime ──
218
- createSchemaValidator(schema: Uint8Array): SchemaValidatorInstance;
219
- createHmacSigner(key: Uint8Array): HmacSignerInstance;
220
- initThreadPool(rayonThreads?: number): void;
221
- rayonNumThreads(): number;
222
-
223
- // ── Namespaces ──
224
- text: RustText;
225
- batch: RustBatch;
226
- packed: RustPacked;
227
-
228
- // ── Configuration ──
229
- /**
230
- * Update defaults on this instance. `mimeCache` / `hmacCache` apply
231
- * immediately (per-instance). `rayonThreads` is applied only if the
232
- * process-wide pool is not yet initialized (see {@link RustOptions}).
233
- */
234
- configure(options: RustOptions): void;
235
- }
236
-
237
- // ── Implementation ─────────────────────────────────────────────
238
-
239
- /**
240
- * Create an isolated Rust client with the given defaults.
241
- *
242
- * `mimeCache` / `hmacCache` are per-instance. `rayonThreads` only applies
243
- * before the process-wide rayon pool is initialized (once per process — see
244
- * {@link RustOptions.rayonThreads}); the pool defaults to the env var or
245
- * `max(1, hardwareConcurrency - 1)` otherwise.
246
- */
247
- export function createRust(options: RustOptions = {}): RustClient {
248
- const mimeByText = new Map<string, Uint8Array>();
249
- const hmacSigners = new WeakMap<Uint8Array, HmacSignerInstance>();
250
- const state = {
251
- mimeCache: options.mimeCache !== false,
252
- hmacCache: options.hmacCache !== false,
253
- };
254
-
255
- // ── Lazy rayon pool initialization ─────────────────────────────
256
- // The native rayon pool is a process-wide OnceLock: the FIRST call wins.
257
- // We therefore defer init until the first batch/packed operation so that
258
- // rust.configure({ rayonThreads }) called at startup actually takes effect
259
- // (previously the pool was initialized eagerly at module load, silently
260
- // ignoring later rayonThreads tuning).
261
- let poolInitialized = false;
262
- let pendingThreads = options.rayonThreads;
263
-
264
- function ensurePool(): void {
265
- if (poolInitialized) return;
266
- poolInitialized = true;
267
- addon.initThreadPool(resolveRayonThreads(pendingThreads));
268
- }
269
-
270
- // Any access to a rayon-backed namespace triggers pool init exactly once.
271
- function withPoolInit<T extends object>(target: T): T {
272
- return new Proxy(target, {
273
- get(obj, prop, receiver) {
274
- ensurePool();
275
- return Reflect.get(obj, prop, receiver);
276
- },
277
- });
278
- }
279
-
280
- function cachedMime(ext: Uint8Array): Uint8Array {
281
- if (!state.mimeCache) {
282
- return addon.mimeFromExtension(ext);
283
- }
284
-
285
- const key = normalizeExt(ext);
286
-
287
- let val = mimeByText.get(key);
288
- if (!val) {
289
- val = addon.mimeFromExtension(ext);
290
- mimeByText.set(key, val);
291
- }
292
-
293
- // Return a copy so callers cannot mutate cached bytes.
294
- return val.slice();
295
- }
296
-
297
- function hmacSigner(key: Uint8Array): HmacSignerInstance {
298
- if (!state.hmacCache) {
299
- return new addon.HmacSigner(key);
300
- }
301
-
302
- let signer = hmacSigners.get(key);
303
-
304
- if (!signer) {
305
- signer = new addon.HmacSigner(key);
306
- hmacSigners.set(key, signer);
307
- }
308
-
309
- return signer;
310
- }
311
-
312
- const text: RustText = {
313
- mimeFromExtension(ext) {
314
- return decoder.decode(cachedMime(encoder.encode(ext)));
315
- },
316
- urlEncode(input) {
317
- return decoder.decode(addon.urlEncode(encoder.encode(input)));
318
- },
319
- urlDecode(input) {
320
- return decoder.decode(addon.urlDecode(encoder.encode(input)));
321
- },
322
- wsAcceptKey(key) {
323
- return decoder.decode(addon.wsAcceptKey(encoder.encode(key)));
324
- },
325
- validateEmail(input) {
326
- return addon.validateEmail(encoder.encode(input));
327
- },
328
- validateUuid(input) {
329
- return addon.validateUuid(encoder.encode(input));
330
- },
331
- validateIpv4(input) {
332
- return addon.validateIpv4(encoder.encode(input));
333
- },
334
- validateIpv6(input) {
335
- return addon.validateIpv6(encoder.encode(input));
336
- },
337
- };
338
-
339
- const batch: RustBatch = withPoolInit<RustBatch>({
340
- jsonValid(items) {
341
- return unpackBitset(addon.jsonValidBatchPacked(packBatch(items)));
342
- },
343
- crc32(items) {
344
- return unpackU32Array(addon.crc32BatchPacked(packBatch(items)));
345
- },
346
- validateEmail(items) {
347
- return unpackBitset(addon.validateEmailBatchPacked(packBatch(items)));
348
- },
349
- validateUuid(items) {
350
- return unpackBitset(addon.validateUuidBatchPacked(packBatch(items)));
351
- },
352
- validateIpv4(items) {
353
- return unpackBitset(addon.validateIpv4BatchPacked(packBatch(items)));
354
- },
355
- validateIpv6(items) {
356
- return unpackBitset(addon.validateIpv6BatchPacked(packBatch(items)));
357
- },
358
- jsonSumIds(items) {
359
- return unpackI64ArrayAsBigInt(addon.jsonSumBatchPacked(packBatch(items)));
360
- },
361
- queryParse(items) {
362
- return unpackByteResults(addon.queryParseBatchPacked(packBatch(items)));
363
- },
364
- cookieParse(items) {
365
- return unpackByteResults(addon.cookieParseBatchPacked(packBatch(items)));
366
- },
367
- httpParseRequest(items) {
368
- return unpackByteResults(
369
- addon.httpParseRequestBatchPacked(packBatch(items)),
370
- );
371
- },
372
- schemaValidate(validator, items) {
373
- return schemaValidateBatch(validator, items);
374
- },
375
- schemaValidateCount(validator, items) {
376
- return schemaValidateBatchCount(validator, items);
377
- },
378
- });
379
-
380
- const packed: RustPacked = withPoolInit<RustPacked>({
381
- crc32BatchPacked(input) {
382
- return addon.crc32BatchPacked(input);
383
- },
384
- jsonValidBatchPacked(input) {
385
- return addon.jsonValidBatchPacked(input);
386
- },
387
- validateEmailBatchPacked(input) {
388
- return addon.validateEmailBatchPacked(input);
389
- },
390
- validateUuidBatchPacked(input) {
391
- return addon.validateUuidBatchPacked(input);
392
- },
393
- validateIpv4BatchPacked(input) {
394
- return addon.validateIpv4BatchPacked(input);
395
- },
396
- validateIpv6BatchPacked(input) {
397
- return addon.validateIpv6BatchPacked(input);
398
- },
399
- jsonSumBatchPacked(input) {
400
- return addon.jsonSumBatchPacked(input);
401
- },
402
- queryParseBatchPacked(input) {
403
- return addon.queryParseBatchPacked(input);
404
- },
405
- cookieParseBatchPacked(input) {
406
- return addon.cookieParseBatchPacked(input);
407
- },
408
- httpParseRequestBatchPacked(input) {
409
- return addon.httpParseRequestBatchPacked(input);
410
- },
411
-
412
- jsonValidBatchCountPacked(input) {
413
- return asNumber(addon.jsonValidBatchCountPacked(input));
414
- },
415
- validateEmailBatchCountPacked(input) {
416
- return asNumber(addon.validateEmailBatchCountPacked(input));
417
- },
418
- validateUuidBatchCountPacked(input) {
419
- return asNumber(addon.validateUuidBatchCountPacked(input));
420
- },
421
- validateIpv4BatchCountPacked(input) {
422
- return asNumber(addon.validateIpv4BatchCountPacked(input));
423
- },
424
- validateIpv6BatchCountPacked(input) {
425
- return asNumber(addon.validateIpv6BatchCountPacked(input));
426
- },
427
- jsonSumBatchTotalPacked(input) {
428
- return asNumber(addon.jsonSumBatchTotalPacked(input));
429
- },
430
- queryParseBatchTotalLenPacked(input) {
431
- return asNumber(addon.queryParseBatchTotalLenPacked(input));
432
- },
433
- cookieParseBatchTotalLenPacked(input) {
434
- return asNumber(addon.cookieParseBatchTotalLenPacked(input));
435
- },
436
- httpParseRequestBatchTotalLenPacked(input) {
437
- return asNumber(addon.httpParseRequestBatchTotalLenPacked(input));
438
- },
439
- });
440
-
441
- const client: RustClient = {
442
- // ── Scalar ──
443
- crc32(bytes) {
444
- return asNumber(addon.crc32(bytes) as unknown) >>> 0;
445
- },
446
- fnv1a64(bytes) {
447
- return asBigInt(addon.fnv1a64(bytes) as unknown);
448
- },
449
- hmacSha256(key, data) {
450
- return hmacSigner(key).sign(data);
451
- },
452
- hmacSha256Verify(key, data, sig) {
453
- return hmacSigner(key).verify(data, sig);
454
- },
455
- jsonValid(bytes) {
456
- return addon.jsonValid(bytes);
457
- },
458
- jsonSumIds(bytes) {
459
- return asBigInt(addon.jsonSumIds(bytes) as unknown);
460
- },
461
- jsonPatch(doc, patch) {
462
- return addon.jsonPatch(doc, patch);
463
- },
464
- mimeFromExtension(ext) {
465
- return cachedMime(ext);
466
- },
467
- randomToken(byteLen) {
468
- return addon.randomToken(byteLen);
469
- },
470
- urlEncode(bytes) {
471
- return addon.urlEncode(bytes);
472
- },
473
- urlDecode(bytes) {
474
- return addon.urlDecode(bytes);
475
- },
476
- urlDecodeBytes(bytes) {
477
- return addon.urlDecodeBytes(bytes);
478
- },
479
- validateEmail(bytes) {
480
- return addon.validateEmail(bytes);
481
- },
482
- validateUuid(bytes) {
483
- return addon.validateUuid(bytes);
484
- },
485
- validateIpv4(bytes) {
486
- return addon.validateIpv4(bytes);
487
- },
488
- validateIpv6(bytes) {
489
- return addon.validateIpv6(bytes);
490
- },
491
- wsAcceptKey(key) {
492
- return addon.wsAcceptKey(key);
493
- },
494
-
495
- // ── Low-level / packed scalar ──
496
- httpParseRequestPacked(bytes) {
497
- return addon.httpParseRequestPacked(bytes);
498
- },
499
- queryParsePacked(bytes) {
500
- return addon.queryParsePacked(bytes);
501
- },
502
- cookieParsePacked(bytes) {
503
- return addon.cookieParsePacked(bytes);
504
- },
505
-
506
- // ── Factories / runtime ──
507
- createSchemaValidator(schema) {
508
- return new addon.SchemaValidator(schema);
509
- },
510
- createHmacSigner(key) {
511
- return new addon.HmacSigner(key);
512
- },
513
- initThreadPool(threads) {
514
- // Explicit user call also establishes the pool state locally.
515
- poolInitialized = true;
516
- if (threads !== undefined) pendingThreads = threads;
517
- return addon.initThreadPool(threads);
518
- },
519
- rayonNumThreads() {
520
- return asNumber(addon.rayonNumThreads() as unknown);
521
- },
522
-
523
- // ── Namespaces ──
524
- text,
525
- batch,
526
- packed,
527
-
528
- // ── Configuration ──
529
- configure(next) {
530
- // rayonThreads only takes effect BEFORE the pool is first used.
531
- if (next.rayonThreads !== undefined && !poolInitialized) {
532
- pendingThreads = next.rayonThreads;
533
- }
534
-
535
- if (next.mimeCache !== undefined) {
536
- state.mimeCache = next.mimeCache;
537
- }
538
-
539
- if (next.hmacCache !== undefined) {
540
- state.hmacCache = next.hmacCache;
541
- }
542
- },
543
- };
544
-
545
- return client;
546
- }
547
-
548
- // ── Default instance (optimized defaults) ─────────────────────
549
-
550
- export const rust = createRust();
551
-
552
- // ── Back-compat alias (non-breaking) ───────────────────────────
553
28
 
554
- /** @deprecated Use `rust.batch` (same object). */
555
- export const rustBatch = rust.batch;
29
+ export type {
30
+ AcceptNegotiatorInstance,
31
+ Base64CodecInstance,
32
+ ConditionalRequestInstance,
33
+ CookieSignerInstance,
34
+ CsrfProtectorInstance,
35
+ EncodingPrefResult,
36
+ FormParserInstance,
37
+ HmacSignerInstance,
38
+ MediaTypeParserInstance,
39
+ MediaTypeResult,
40
+ MultipartPart,
41
+ PasswordHashOptions,
42
+ SchemaError,
43
+ SchemaValidatorInstance,
44
+ TemplateRendererInstance,
45
+ UrlBuilderInstance,
46
+ WsFrame,
47
+ } from '../native'
48
+ export type { SchemaValidator } from '../shared/packed'
49
+ export type { RustBatch } from './batch'
50
+ export type { RustClient } from './client'
51
+ export { createRust, rust } from './client'
52
+ export { proven } from './proven'
53
+ export {
54
+ PROVEN_SELECTION,
55
+ isProven,
56
+ provenEntry,
57
+ provenImpl,
58
+ provenStatus,
59
+ provenSummary,
60
+ provenSurface,
61
+ } from '../shared/proven'
62
+ export type { ProvenEntry, ProvenImpl, ProvenStatus } from '../shared/proven'
63
+ export type { RustOptions } from './options'
64
+ export type { RustPacked } from './packed'
65
+ export type { RustText } from './text'
@@ -0,0 +1,21 @@
1
+ // src/rust-ffi/into.ts — pooled `*Into` output-write helper (napi fallback).
2
+
3
+ /**
4
+ * Write `bytes` into a caller-owned `output` buffer, or throw when it does not
5
+ * fit.
6
+ *
7
+ * Shared tail of the pooled (`*Into`) napi-fallback paths: every op copies the
8
+ * allocating result into the caller buffer with the same capacity check, error
9
+ * message, and byte-length return. The bun:ffi `_into` path never reaches this
10
+ * helper — the C ABI streams straight into `output`.
11
+ *
12
+ * @param op Label for the error message (e.g. `"gzip compress"`).
13
+ * @param output The caller-owned destination buffer.
14
+ * @param bytes The allocating op result to copy in.
15
+ * @returns The number of bytes written (`bytes.length`).
16
+ */
17
+ export function writeInto(op: string, output: Uint8Array, bytes: Uint8Array): number {
18
+ if (output.length < bytes.length) throw new Error(`${op}: output buffer too small`)
19
+ output.set(bytes)
20
+ return bytes.length
21
+ }