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.
Files changed (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +322 -59
  3. package/castrum.linux-x64-gnu.node +0 -0
  4. package/dist/index.d.ts +3161 -0
  5. package/dist/index.js +7306 -0
  6. package/docs/ARCHITECTURE.md +415 -0
  7. package/docs/BENCHMARKS.md +307 -0
  8. package/docs/ENVIRONMENT.md +79 -0
  9. package/docs/GETTING_STARTED.md +209 -0
  10. package/docs/INGRESS.md +745 -0
  11. package/docs/REPO_MAP.md +259 -0
  12. package/docs/adr/0001-two-wire-formats.md +39 -0
  13. package/docs/adr/0002-zero-dom-fast-schema.md +38 -0
  14. package/docs/adr/0003-bun-builtins-delegation.md +42 -0
  15. package/docs/bun-builtins-decision-matrix.md +108 -0
  16. package/index.ts +51 -8
  17. package/package.json +73 -20
  18. package/src/data/json-rows.ts +13 -13
  19. package/src/ingress/body.ts +149 -0
  20. package/src/ingress/constants.ts +220 -48
  21. package/src/ingress/context.ts +136 -0
  22. package/src/ingress/decode/baked-result.ts +121 -0
  23. package/src/ingress/decode/fast-result.ts +160 -0
  24. package/src/ingress/decode/packed-sections.ts +56 -0
  25. package/src/ingress/decode/result-base.ts +172 -0
  26. package/src/ingress/errors.ts +72 -0
  27. package/src/ingress/fast.ts +152 -1021
  28. package/src/ingress/handlers.ts +563 -1001
  29. package/src/ingress/headers/baked-templates.ts +102 -0
  30. package/src/ingress/headers/cors.ts +44 -0
  31. package/src/ingress/headers/fast-templates.ts +238 -0
  32. package/src/ingress/headers/hsts.ts +83 -0
  33. package/src/ingress/health.ts +67 -0
  34. package/src/ingress/index.ts +130 -405
  35. package/src/ingress/metrics.ts +132 -0
  36. package/src/ingress/options.ts +197 -0
  37. package/src/ingress/packing/gather-raw-headers.ts +75 -0
  38. package/src/ingress/packing/header-packing.ts +75 -0
  39. package/src/ingress/packing/input-packer.ts +150 -0
  40. package/src/ingress/packing/scratch.ts +50 -0
  41. package/src/ingress/packing/select-headers.ts +96 -0
  42. package/src/ingress/response/error-bodies.ts +75 -0
  43. package/src/ingress/response/terminal.ts +59 -0
  44. package/src/ingress/routes/common.ts +38 -0
  45. package/src/ingress/routes/delete.ts +14 -0
  46. package/src/ingress/routes/echo.ts +107 -0
  47. package/src/ingress/routes/fallback.ts +23 -0
  48. package/src/ingress/routes/head.ts +37 -0
  49. package/src/ingress/routes/index.ts +10 -0
  50. package/src/ingress/routes/json-write.ts +153 -0
  51. package/src/ingress/routes/options.ts +36 -0
  52. package/src/ingress/routes/read.ts +45 -0
  53. package/src/ingress/server-node.ts +376 -0
  54. package/src/ingress/server.ts +443 -0
  55. package/src/ingress/shared.ts +132 -0
  56. package/src/ingress/status.ts +97 -0
  57. package/src/ingress/types.ts +157 -0
  58. package/src/integration/batch.ts +58 -0
  59. package/src/integration/index.ts +29 -0
  60. package/src/integration/pipeline.ts +217 -0
  61. package/src/integration/streaming.ts +79 -0
  62. package/src/integration/websocket.ts +92 -0
  63. package/src/loader/batch.ts +133 -0
  64. package/src/loader/cost.ts +104 -0
  65. package/src/loader/index.ts +930 -0
  66. package/src/loader/ops.ts +657 -0
  67. package/src/native/ffi.ts +1541 -0
  68. package/src/native/index.ts +8 -207
  69. package/src/native/loader.ts +194 -0
  70. package/src/native/types.ts +598 -0
  71. package/src/rust-ffi/addon.ts +10 -0
  72. package/src/rust-ffi/batch.ts +431 -0
  73. package/src/rust-ffi/client.ts +88 -0
  74. package/src/rust-ffi/context.ts +164 -0
  75. package/src/rust-ffi/index.ts +40 -541
  76. package/src/rust-ffi/options.ts +113 -0
  77. package/src/rust-ffi/packed.ts +253 -0
  78. package/src/rust-ffi/proven.ts +42 -0
  79. package/src/rust-ffi/scalar/crypto.ts +265 -0
  80. package/src/rust-ffi/scalar/factories.ts +233 -0
  81. package/src/rust-ffi/scalar/hashing.ts +72 -0
  82. package/src/rust-ffi/scalar/http.ts +206 -0
  83. package/src/rust-ffi/scalar/index.ts +31 -0
  84. package/src/rust-ffi/scalar/interface.ts +450 -0
  85. package/src/rust-ffi/scalar/json.ts +45 -0
  86. package/src/rust-ffi/scalar/payload.ts +124 -0
  87. package/src/rust-ffi/text.ts +95 -0
  88. package/src/selection.json +194 -0
  89. package/src/selection.ts +77 -0
  90. package/src/shared/adaptive.ts +68 -0
  91. package/src/shared/bench-classify.ts +209 -0
  92. package/src/shared/buffer-pool.ts +209 -0
  93. package/src/shared/bytes.ts +35 -5
  94. package/src/shared/env.ts +20 -0
  95. package/src/shared/log.ts +74 -0
  96. package/src/shared/metrics.ts +247 -0
  97. package/src/shared/packed.ts +350 -154
  98. package/src/shared/proven.ts +596 -0
  99. package/src/shared/request-id.ts +67 -0
  100. package/src/shared/response.ts +104 -0
  101. package/src/shared/runtime.ts +34 -0
  102. package/src/shared/trace.ts +81 -0
  103. package/src/shared/uuid.ts +31 -0
  104. package/Cargo.toml +0 -73
  105. package/bench.ts +0 -3
  106. package/rust/batch.rs +0 -195
  107. package/rust/cookie_parser.rs +0 -70
  108. package/rust/cors.rs +0 -263
  109. package/rust/hashing.rs +0 -164
  110. package/rust/headers.rs +0 -234
  111. package/rust/hmac_sha256.rs +0 -113
  112. package/rust/http_parser.rs +0 -132
  113. package/rust/ingress.rs +0 -616
  114. package/rust/ingress_constants.rs +0 -89
  115. package/rust/ip_trust.rs +0 -185
  116. package/rust/json_ops.rs +0 -66
  117. package/rust/json_patch_ops.rs +0 -25
  118. package/rust/json_schema.rs +0 -151
  119. package/rust/json_ser.rs +0 -429
  120. package/rust/lib.rs +0 -48
  121. package/rust/method.rs +0 -141
  122. package/rust/mime_lookup.rs +0 -76
  123. package/rust/output.rs +0 -238
  124. package/rust/proxy.rs +0 -90
  125. package/rust/query_parser.rs +0 -141
  126. package/rust/random_token.rs +0 -52
  127. package/rust/rate_limit.rs +0 -203
  128. package/rust/terminal.rs +0 -95
  129. package/rust/unit_tests.rs +0 -843
  130. package/rust/url_codec.rs +0 -133
  131. package/rust/util.rs +0 -537
  132. package/rust/validation.rs +0 -107
  133. package/rust/websocket.rs +0 -23
  134. package/src/baseline/index.ts +0 -12
  135. package/src/baseline/tasks/cookie.ts +0 -22
  136. package/src/baseline/tasks/hashing.ts +0 -18
  137. package/src/baseline/tasks/hmac.ts +0 -38
  138. package/src/baseline/tasks/http.ts +0 -62
  139. package/src/baseline/tasks/json-patch.ts +0 -12
  140. package/src/baseline/tasks/json.ts +0 -50
  141. package/src/baseline/tasks/mime.ts +0 -5
  142. package/src/baseline/tasks/query.ts +0 -14
  143. package/src/baseline/tasks/token.ts +0 -15
  144. package/src/baseline/tasks/url.ts +0 -11
  145. package/src/baseline/tasks/validation.ts +0 -19
  146. package/src/baseline/tasks/websocket.ts +0 -14
  147. package/src/bench/assert.ts +0 -70
  148. package/src/bench/checks.ts +0 -227
  149. package/src/bench/checksum.ts +0 -27
  150. package/src/bench/comparisons.ts +0 -70
  151. package/src/bench/concurrent-worker.ts +0 -135
  152. package/src/bench/fixtures.ts +0 -233
  153. package/src/bench/index.ts +0 -2
  154. package/src/bench/measure.ts +0 -177
  155. package/src/bench/now.ts +0 -3
  156. package/src/bench/report.ts +0 -79
  157. package/src/bench/run.ts +0 -57
  158. package/src/bench/tasks/complex.ts +0 -232
  159. package/src/bench/tasks/concurrent.ts +0 -213
  160. package/src/bench/tasks/cookie.ts +0 -44
  161. package/src/bench/tasks/hashing.ts +0 -33
  162. package/src/bench/tasks/hmac.ts +0 -36
  163. package/src/bench/tasks/http.ts +0 -50
  164. package/src/bench/tasks/index.ts +0 -52
  165. package/src/bench/tasks/json-patch.ts +0 -21
  166. package/src/bench/tasks/json.ts +0 -33
  167. package/src/bench/tasks/mime.ts +0 -21
  168. package/src/bench/tasks/query.ts +0 -44
  169. package/src/bench/tasks/stress.ts +0 -142
  170. package/src/bench/tasks/token.ts +0 -20
  171. package/src/bench/tasks/url.ts +0 -34
  172. package/src/bench/tasks/validation.ts +0 -57
  173. package/src/bench/tasks/websocket.ts +0 -21
  174. package/src/bench/types.ts +0 -60
  175. package/src/ingress/packed-input.ts +0 -59
  176. package/src/shared/json.ts +0 -18
@@ -0,0 +1,598 @@
1
+ // src/native/types.ts — Type declarations for the native addon surface.
2
+ //
3
+ // The `NativeAddon` interface mirrors the napi-rs generated declaration
4
+ // (index.d.ts) and types every class + function + layout constant exported by
5
+ // the Rust cdylib. Keeping it separate from the loader (./loader.ts) means
6
+ // type-only consumers never pull in the file-system loading logic.
7
+
8
+ /** The native Ingress class instance (hot-path pipeline). */
9
+ export interface IngressInstance {
10
+ handleRequestPacked(input: Uint8Array, body: Uint8Array | null, output: Uint8Array): number
11
+
12
+ handleRequestFullSync(
13
+ methodKind: number,
14
+ url: string,
15
+ ip: string,
16
+ requestId: string,
17
+ headers: Array<[string, string]>,
18
+ body: Uint8Array | null,
19
+ outputBufferSize?: number,
20
+ ): Uint8Array
21
+
22
+ /**
23
+ * Reusable-output variant of `handleRequestFullSync`: packs the request in
24
+ * Rust and writes the packed decision into `output`, returning the number of
25
+ * bytes written. Pool the output buffer across requests to avoid per-request
26
+ * allocation (see `src/shared/buffer-pool.ts`).
27
+ */
28
+ handleRequestFullSyncInto(
29
+ methodKind: number,
30
+ url: string,
31
+ ip: string,
32
+ requestId: string,
33
+ headers: Array<[string, string]>,
34
+ body: Uint8Array | null,
35
+ output: Uint8Array,
36
+ ): number
37
+
38
+ /**
39
+ * Opaque handle to the inner pipeline state for the `bun:ffi` fast path.
40
+ * Internal only — the returned pointer is valid only while THIS instance is
41
+ * alive and must never be dereferenced from JS. Present only on addons built
42
+ * with the `castrum_ingress_handle_packed` C-ABI export; guard with
43
+ * `typeof handler.ingressInnerPtr === 'function'`.
44
+ */
45
+ ingressInnerPtr?(): bigint
46
+ }
47
+
48
+ /** A single JSON Schema validation error (fast path + DOM fallback). */
49
+ export interface SchemaError {
50
+ /** RFC 6901 JSON pointer to the failing instance value ("" = root). */
51
+ instancePath: string
52
+ /** JSON pointer into the schema at the failing keyword. */
53
+ schemaPath: string
54
+ /** The failing keyword (e.g. "type", "pattern", "required"). */
55
+ keyword: string
56
+ /** Human-readable failure message. */
57
+ message: string
58
+ }
59
+
60
+ /** The native `SchemaValidator` class instance. */
61
+ export interface SchemaValidatorInstance {
62
+ /** Validate a single JSON document. */
63
+ validate(input: Uint8Array): boolean
64
+ /** Validate a single JSON document, returning detailed errors (empty = valid). */
65
+ validateDetailed(input: Uint8Array): SchemaError[]
66
+ /** Validate a single JSON document, returning only the first error (null = valid). */
67
+ validateFirstError(input: Uint8Array): SchemaError | null
68
+ validateBatchPackedCount(packed: Uint8Array): number
69
+ validateBatchPackedBitset(packed: Uint8Array): Uint8Array
70
+ validateBatchStreaming(batchBytes: Uint8Array): number
71
+ /**
72
+ * One-pass validate + extract: validate `input` against the schema and
73
+ * capture scalar values / array lengths at `paths` during the same native
74
+ * walk (no `JSON.parse`, no DOM). For "derive" routes (response built from a
75
+ * handful of body fields) this replaces `JSON.parse` + Ajv on the happy path
76
+ * and rejects invalid bodies with zero DOM/GC.
77
+ *
78
+ * `paths` are RFC 6901 JSON pointers of OBJECT KEYS; a trailing `/-`
79
+ * captures the ARRAY LENGTH at that path (e.g. `"/totalCents"`,
80
+ * `"/lineItems/-"`). Array-index steps are not supported.
81
+ */
82
+ derive(input: Uint8Array, paths: string[]): JsonDeriveResult
83
+ }
84
+
85
+ /** A single derived value captured during one-pass validation. */
86
+ export interface JsonDeriveValue {
87
+ /** `"int" | "number" | "string" | "bool" | "null"`. */
88
+ kind: string
89
+ int: number | null
90
+ number: number | null
91
+ text: string | null
92
+ boolean: boolean | null
93
+ }
94
+
95
+ /** Result of a one-pass `validate + derive`. */
96
+ export interface JsonDeriveResult {
97
+ /** `true` when the document is schema-valid; `false` → caller rejects. */
98
+ ok: boolean
99
+ /** One entry per requested path (`null` = path absent from the document). */
100
+ values: Array<JsonDeriveValue | null>
101
+ }
102
+
103
+ /** The native `HmacSigner` class instance. */
104
+ export interface HmacSignerInstance {
105
+ sign(data: Uint8Array): Uint8Array
106
+ verify(data: Uint8Array, sig: Uint8Array): boolean
107
+ }
108
+
109
+ /** The native `FormParser` class instance (reusable output buffer). */
110
+ export interface FormParserInstance {
111
+ /** Parse an x-www-form-urlencoded body into packed pairs (reuses the buffer). */
112
+ parse(input: Uint8Array): Uint8Array
113
+ /** Zero-alloc parse into a caller-provided output buffer. */
114
+ parseInto(input: Uint8Array, output: Uint8Array): number
115
+ }
116
+
117
+ /** A parsed Content-Type / media type. */
118
+ export interface MediaTypeResult {
119
+ /** Lowercased `type/subtype`. */
120
+ mediaType: string
121
+ charset: string | null
122
+ boundary: string | null
123
+ params: Record<string, string>
124
+ }
125
+
126
+ /** The native `MediaTypeParser` class instance. */
127
+ export interface MediaTypeParserInstance {
128
+ parse(input: Uint8Array): MediaTypeResult
129
+ /** Wildcard match: any/any, type/any, or exact type/subtype. */
130
+ matches(actual: Uint8Array, expected: Uint8Array): boolean
131
+ }
132
+
133
+ /** The native `ConditionalRequest` class instance (per-resource 304 checks). */
134
+ export interface ConditionalRequestInstance {
135
+ isNotModified(ifNoneMatch: Uint8Array | null, ifModifiedSince: Uint8Array | null): boolean
136
+ }
137
+
138
+ /** One parsed Accept-Encoding preference. */
139
+ export interface EncodingPrefResult {
140
+ encoding: string
141
+ q: number
142
+ order: number
143
+ }
144
+
145
+ /** The native `AcceptNegotiator` class instance. */
146
+ export interface AcceptNegotiatorInstance {
147
+ /** Best supported encoding for `header`, or null for identity. */
148
+ negotiate(header: Uint8Array): string | null
149
+ }
150
+
151
+ /** The native `Base64Codec` class instance (config compiled once). */
152
+ export interface Base64CodecInstance {
153
+ encode(input: Uint8Array): Uint8Array
154
+ decode(input: Uint8Array): Uint8Array
155
+ }
156
+
157
+ /** Result of a native rate-limit check for one key at a point in time. */
158
+ export interface RateCheckResult {
159
+ allowed: boolean
160
+ remaining: number
161
+ resetMs: number
162
+ }
163
+
164
+ /** The native `RateLimiter` class instance (sharded fixed-window state). */
165
+ export interface RateLimiterInstance {
166
+ check(key: string, nowMs: number): RateCheckResult
167
+ checkKey(key: number, nowMs: number): RateCheckResult
168
+ }
169
+
170
+ /** The native `CookieSigner` class instance (HMAC key compiled once). */
171
+ export interface CookieSignerInstance {
172
+ sign(value: Uint8Array): Uint8Array
173
+ /** Returns the signed value without its signature, or null on invalid. */
174
+ verify(signed: Uint8Array): Uint8Array | null
175
+ }
176
+
177
+ /** The native `CsrfProtector` class instance (HMAC key compiled once). */
178
+ export interface CsrfProtectorInstance {
179
+ create(): Uint8Array
180
+ verify(token: Uint8Array): boolean
181
+ }
182
+
183
+ /** The native `UrlBuilder` class instance (base parsed once). */
184
+ export interface UrlBuilderInstance {
185
+ resolve(reference: Uint8Array): Uint8Array
186
+ }
187
+
188
+ /** Options for `passwordHash` (argon2id). */
189
+ export interface PasswordHashOptions {
190
+ mCost?: number
191
+ tCost?: number
192
+ pCost?: number
193
+ outLen?: number
194
+ }
195
+
196
+ /** A parsed multipart/form-data part. */
197
+ export interface MultipartPart {
198
+ name: string
199
+ filename: string | null
200
+ contentType: string | null
201
+ data: Uint8Array
202
+ }
203
+
204
+ /** A decoded RFC 6455 websocket frame. */
205
+ export interface WsFrame {
206
+ fin: boolean
207
+ opcode: number
208
+ payload: Uint8Array
209
+ }
210
+
211
+ /** The native `TemplateRenderer` class instance. */
212
+ export interface TemplateRendererInstance {
213
+ render(context: unknown): Uint8Array
214
+ /** Render from pre-serialized JSON context bytes (no napi Value marshal). */
215
+ renderBytes(contextJson: Uint8Array): Uint8Array
216
+ /** Parallel batch render: packed contexts in → packed rendered out. */
217
+ renderBatchPacked(data: Uint8Array): Uint8Array
218
+ }
219
+
220
+ /** The native `MediaTypeMatcher` class instance (expected precompiled once). */
221
+ export interface MediaTypeMatcherInstance {
222
+ /** Wildcard match against the precompiled expected type. */
223
+ matches(actual: Uint8Array): boolean
224
+ }
225
+
226
+ /** The native `JwtSigner` class instance (HS256 key + ttl compiled once). */
227
+ export interface JwtSignerInstance {
228
+ sign(claims: unknown, nowSeconds: number): Uint8Array
229
+ /** Sign from pre-serialized claim JSON bytes (no napi Value marshal). */
230
+ signBytes(claimsJson: Uint8Array, nowSeconds: number): Uint8Array
231
+ verify(token: Uint8Array, nowSeconds: number): unknown
232
+ }
233
+
234
+ /** The native `AeadCipher` class instance (algorithm + key compiled once). */
235
+ export interface AeadCipherInstance {
236
+ encrypt(nonce: Uint8Array, plaintext: Uint8Array): Uint8Array
237
+ decrypt(nonce: Uint8Array, ciphertext: Uint8Array): Uint8Array | null
238
+ }
239
+
240
+ /** The native `Argon2Hasher` class instance (params compiled once). */
241
+ export interface Argon2HasherInstance {
242
+ hash(password: Uint8Array, salt: Uint8Array): Uint8Array
243
+ verify(password: Uint8Array, phc: Uint8Array): boolean
244
+ }
245
+
246
+ /**
247
+ * Full shape of the native addon module: classes, scalar/batch functions and
248
+ * the ingress binary-layout constants (single source of truth:
249
+ * rust/ingress_constants.rs).
250
+ */
251
+ export interface NativeAddon {
252
+ HmacSigner: new (key: Uint8Array) => HmacSignerInstance
253
+ Ingress: new (options: Record<string, unknown>) => IngressInstance
254
+
255
+ /**
256
+ * Benchmark-driven native-vs-JS decision (single source of truth:
257
+ * `rust/selection.rs`, audited by `scripts/select-native.ts --check`).
258
+ * Returns `"native"` when the Rust addon is the recommended impl for `op`,
259
+ * `"js"` when a pure-JS implementation wins, or `null` for unknown ops.
260
+ * Consumers read this ONCE at load time and bind each op to a fixed impl.
261
+ */
262
+ opImpl(op: string): 'native' | 'js' | null
263
+
264
+ // ── Ingress binary-layout constants (single source of truth: rust/ingress_constants.rs) ──
265
+ INGRESS_OUT_VERDICT: number
266
+ INGRESS_OUT_ERROR_CODE: number
267
+ INGRESS_OUT_STATUS: number
268
+ INGRESS_OUT_FLAGS: number
269
+ INGRESS_OUT_RATE_LIMIT: number
270
+ INGRESS_OUT_RATE_REMAINING: number
271
+ INGRESS_OUT_RATE_RESET: number
272
+ INGRESS_OUT_RETRY_AFTER: number
273
+ INGRESS_OUT_COOKIES_JSON_LEN: number
274
+ INGRESS_OUT_QUERY_JSON_LEN: number
275
+ INGRESS_OUT_HEADER_VARIANT: number
276
+ INGRESS_OUT_BODY_JSON_LEN: number
277
+ INGRESS_OUT_DATA_START: number
278
+ INGRESS_FLAG_HAS_COOKIES: number
279
+ INGRESS_FLAG_HAS_QUERY: number
280
+ INGRESS_FLAG_BODY_VALID_JSON: number
281
+ INGRESS_FLAG_SCHEMA_VALID: number
282
+ INGRESS_FLAG_CORS_ALLOWED: number
283
+ INGRESS_FLAG_IS_PREFLIGHT: number
284
+ INGRESS_FLAG_RATE_LIMITED: number
285
+ INGRESS_FLAG_HTTPS: number
286
+ INGRESS_FLAG_TRUSTED_PROXY: number
287
+ INGRESS_FLAG_BODY_TRUNCATED: number
288
+ INGRESS_HV_JSON: number
289
+ INGRESS_HV_CORS_SIMPLE: number
290
+ INGRESS_HV_CORS_PREFLIGHT: number
291
+ INGRESS_HV_RATE_ACTIVE: number
292
+ INGRESS_HV_RATE_LIMITED: number
293
+ INGRESS_HV_COUNT: number
294
+ INGRESS_ERR_NONE: number
295
+ INGRESS_ERR_CORS_PREFLIGHT: number
296
+ INGRESS_ERR_RATE_LIMITED: number
297
+ INGRESS_ERR_BODY_TOO_LARGE: number
298
+ INGRESS_ERR_INVALID_JSON: number
299
+ INGRESS_ERR_SCHEMA_VALIDATION: number
300
+ INGRESS_ERR_BAD_REQUEST: number
301
+ INGRESS_ERR_REQUEST_TOO_LARGE: number
302
+ INGRESS_ERR_INTERNAL: number
303
+
304
+ crc32(input: Uint8Array): number
305
+ fnv1a64(input: Uint8Array): bigint
306
+ /** XXH3-64 (high-throughput non-cryptographic hash). */
307
+ xxh3(input: Uint8Array): bigint
308
+ /** Packed FNV-1a 64 batch (i64 per item). */
309
+ fnv1A64BatchPacked(input: Uint8Array): Uint8Array
310
+
311
+ hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array
312
+ hmacSha256Verify(key: Uint8Array, data: Uint8Array, sig: Uint8Array): boolean
313
+
314
+ jsonValid(input: Uint8Array): boolean
315
+ jsonParse(input: Uint8Array): unknown
316
+ jsonSumIds(input: Uint8Array): bigint
317
+
318
+ jsonPatch(doc: Uint8Array, patch: Uint8Array): Uint8Array
319
+ /** Batch RFC 6902 JSON Patch (two packed lists, zipped) → packed results. */
320
+ jsonPatchBatchPacked(docs: Uint8Array, patches: Uint8Array): Uint8Array
321
+
322
+ mimeFromExtension(ext: Uint8Array): Uint8Array
323
+ mimeFromExtensionBatchPacked(input: Uint8Array): Uint8Array
324
+ randomToken(byteLen: number): Uint8Array
325
+
326
+ urlEncode(input: Uint8Array): Uint8Array
327
+ /** String-input percent-encode (no Uint8Array round-trip). */
328
+ urlEncodeStr(input: string): string
329
+ /** String-input percent-decode; throws on invalid UTF-8 output. */
330
+ urlDecodeStr(input: string): string
331
+ urlDecode(input: Uint8Array): Uint8Array
332
+ urlDecodeBytes(input: Uint8Array): Uint8Array
333
+ urlEncodeBatchPacked(input: Uint8Array): Uint8Array
334
+ urlDecodeBatchPacked(input: Uint8Array): Uint8Array
335
+ urlDecodeBytesBatchPacked(input: Uint8Array): Uint8Array
336
+ /** Reusable-output percent-encode; returns bytes written. Throws if `output` is too small. */
337
+ urlEncodeInto(input: Uint8Array, output: Uint8Array): number
338
+ /** Reusable-output percent-decode; returns bytes written. Throws if `output` is too small. */
339
+ urlDecodeInto(input: Uint8Array, output: Uint8Array): number
340
+
341
+ validateEmail(input: Uint8Array): boolean
342
+ validateUuid(input: Uint8Array): boolean
343
+ validateIpv4(input: Uint8Array): boolean
344
+ validateIpv6(input: Uint8Array): boolean
345
+
346
+ wsAcceptKey(key: Uint8Array): Uint8Array
347
+ wsAcceptKeyBatchPacked(input: Uint8Array): Uint8Array
348
+
349
+ // ── Backend-framework features ──
350
+ jwtSign(
351
+ claims: unknown,
352
+ secret: Uint8Array,
353
+ ttlSeconds: number | null,
354
+ nowSeconds: number,
355
+ ): Uint8Array
356
+ /** Sign from pre-serialized claim JSON bytes (no napi Value marshal). */
357
+ jwtSignBytes(
358
+ claimsJson: Uint8Array,
359
+ secret: Uint8Array,
360
+ ttlSeconds: number | null,
361
+ nowSeconds: number,
362
+ ): Uint8Array
363
+ jwtVerify(token: Uint8Array, secret: Uint8Array, nowSeconds: number): unknown
364
+
365
+ passwordHash(
366
+ password: Uint8Array,
367
+ salt: Uint8Array,
368
+ options?: PasswordHashOptions | null,
369
+ ): Uint8Array
370
+ passwordVerify(password: Uint8Array, phc: Uint8Array): boolean
371
+ /** bcrypt password hash → `$2b$` PHC string (cost clamped 4..=31). */
372
+ passwordHashBcrypt(password: Uint8Array, cost: number): string
373
+ /** Verify a password against a bcrypt `$2b$` PHC string. */
374
+ passwordVerifyBcrypt(password: Uint8Array, hash: string): boolean
375
+ /** PBKDF2-HMAC-SHA256 key derivation (dkLen clamped to 1 MiB). */
376
+ pbkdf2Sha256(password: Uint8Array, salt: Uint8Array, rounds: number, dkLen: number): Uint8Array
377
+ /** Packed password-verify batch (two packed lists, zipped) → bitset. */
378
+ passwordVerifyBatchPacked(passwords: Uint8Array, phcs: Uint8Array): Uint8Array
379
+ passwordHashBatchPacked(
380
+ data: Uint8Array,
381
+ salt: Uint8Array,
382
+ options?: PasswordHashOptions | null,
383
+ ): Uint8Array
384
+
385
+ aeadEncrypt(
386
+ key: Uint8Array,
387
+ nonce: Uint8Array,
388
+ plaintext: Uint8Array,
389
+ algorithm?: string | null,
390
+ ): Uint8Array
391
+ aeadDecrypt(
392
+ key: Uint8Array,
393
+ nonce: Uint8Array,
394
+ ciphertext: Uint8Array,
395
+ algorithm?: string | null,
396
+ ): Uint8Array | null
397
+ aeadEncryptBatchPacked(
398
+ data: Uint8Array,
399
+ key: Uint8Array,
400
+ nonce: Uint8Array,
401
+ algorithm?: string | null,
402
+ ): Uint8Array
403
+ aeadDecryptBatchPacked(
404
+ data: Uint8Array,
405
+ key: Uint8Array,
406
+ nonce: Uint8Array,
407
+ algorithm?: string | null,
408
+ ): Uint8Array
409
+
410
+ gzipCompress(data: Uint8Array, level?: number | null): Uint8Array
411
+ gzipDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array
412
+ brotliCompress(data: Uint8Array, quality?: number | null): Uint8Array
413
+ brotliDecompress(data: Uint8Array, maxDecompressed?: number | null): Uint8Array
414
+ gzipCompressBatchPacked(data: Uint8Array, level?: number | null): Uint8Array
415
+ gzipDecompressBatchPacked(data: Uint8Array, maxDecompressed?: number | null): Uint8Array
416
+ brotliCompressBatchPacked(data: Uint8Array, quality?: number | null): Uint8Array
417
+ brotliDecompressBatchPacked(data: Uint8Array, maxDecompressed?: number | null): Uint8Array
418
+
419
+ multipartParse(body: Uint8Array, boundary: Uint8Array): MultipartPart[]
420
+ multipartParseBatchPacked(data: Uint8Array, boundary: Uint8Array): Uint8Array
421
+ /** Zero-copy scalar sibling of `multipartParse` — returns the packed parts
422
+ * layout (`[u32 count]{[u32 len][name][has_filename][filename][ct][data]}`)
423
+ * instead of JS objects, skipping the per-part String alloc + data copy. */
424
+ multipartParsePacked(body: Uint8Array, boundary: Uint8Array): Uint8Array
425
+ jwtSignBatchPacked(
426
+ data: Uint8Array,
427
+ secret: Uint8Array,
428
+ ttlSeconds: number | null,
429
+ nowSeconds: number,
430
+ ): Uint8Array
431
+ jwtVerifyBatchPacked(data: Uint8Array, secret: Uint8Array, nowSeconds: number): Uint8Array
432
+
433
+ TemplateRenderer: new (source: string) => TemplateRendererInstance
434
+ templateRenderBatchPacked(data: Uint8Array, source: string): Uint8Array
435
+ JwtSigner: new (secret: Uint8Array, ttlSeconds?: number | null) => JwtSignerInstance
436
+ AeadCipher: new (key: Uint8Array, algorithm?: string | null) => AeadCipherInstance
437
+ Argon2Hasher: new (options?: PasswordHashOptions | null) => Argon2HasherInstance
438
+ MediaTypeMatcher: new (expected: Uint8Array) => MediaTypeMatcherInstance
439
+ RateLimiter: new (
440
+ limit: number,
441
+ windowMs: number,
442
+ maxEntries?: number | null,
443
+ ) => RateLimiterInstance
444
+
445
+ wsFrameEncode(opcode: number, payload: Uint8Array, mask: boolean, fin: boolean): Uint8Array
446
+ wsFrameDecode(data: Uint8Array): WsFrame | null
447
+ wsFrameEncodeBatchPacked(
448
+ data: Uint8Array,
449
+ opcode: number,
450
+ mask: boolean,
451
+ fin: boolean,
452
+ ): Uint8Array
453
+ wsFrameDecodeBatchPacked(data: Uint8Array): Uint8Array
454
+
455
+ sseEncodeEvent(
456
+ event: string | null,
457
+ data: Uint8Array,
458
+ id: string | null,
459
+ retry: number | null,
460
+ ): Uint8Array
461
+ sseEncodeBatchPacked(
462
+ data: Uint8Array,
463
+ event: string | null,
464
+ id: string | null,
465
+ retry: number | null,
466
+ ): Uint8Array
467
+
468
+ SchemaValidator: new (schema: Uint8Array) => SchemaValidatorInstance
469
+ FormParser: new (capacity?: number) => FormParserInstance
470
+ MediaTypeParser: new () => MediaTypeParserInstance
471
+ ConditionalRequest: new (
472
+ etagValue: Uint8Array,
473
+ lastModifiedSecs?: number,
474
+ ) => ConditionalRequestInstance
475
+ AcceptNegotiator: new (supported: string[]) => AcceptNegotiatorInstance
476
+ Base64Codec: new (urlSafe?: boolean, padding?: boolean) => Base64CodecInstance
477
+ CookieSigner: new (secret: Uint8Array) => CookieSignerInstance
478
+ CsrfProtector: new (secret: Uint8Array) => CsrfProtectorInstance
479
+ UrlBuilder: new (base: Uint8Array) => UrlBuilderInstance
480
+ parseAcceptEncoding(input: Uint8Array): EncodingPrefResult[]
481
+ urlResolve(base: Uint8Array, reference: Uint8Array): Uint8Array
482
+ /** Packed URL-resolve batch (two packed lists, zipped) → packed results. */
483
+ urlResolveBatchPacked(bases: Uint8Array, references: Uint8Array): Uint8Array
484
+ urlEncodeQuery(params: Record<string, string>): Uint8Array
485
+ csrfToken(secret: Uint8Array): Uint8Array
486
+ csrfVerify(token: Uint8Array, secret: Uint8Array): boolean
487
+ csrfVerifyBatchPacked(data: Uint8Array, secret: Uint8Array): Uint8Array
488
+ signCookie(value: Uint8Array, secret: Uint8Array): Uint8Array
489
+ verifyCookie(signed: Uint8Array, secret: Uint8Array): Uint8Array | null
490
+ signCookieBatchPacked(data: Uint8Array, secret: Uint8Array): Uint8Array
491
+ verifyCookieBatchPacked(data: Uint8Array, secret: Uint8Array): Uint8Array
492
+ /** Batch HMAC-SHA256 sign (packed in → hex-signature byte results). */
493
+ hmacSha256BatchPacked(input: Uint8Array, key: Uint8Array): Uint8Array
494
+ /** Batch HMAC-SHA256 verify (two packed lists: data + hex sigs, zipped) → bitset. */
495
+ hmacSha256VerifyBatchPacked(input: Uint8Array, sigs: Uint8Array, key: Uint8Array): Uint8Array
496
+ base64Encode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array
497
+ base64Decode(input: Uint8Array, urlSafe?: boolean, padding?: boolean): Uint8Array
498
+ /** Reusable-output base64 encode; returns bytes written. Throws if `output` is too small. */
499
+ base64EncodeInto(
500
+ input: Uint8Array,
501
+ output: Uint8Array,
502
+ urlSafe?: boolean,
503
+ padding?: boolean,
504
+ ): number
505
+ /** Reusable-output base64 decode; returns bytes written. Throws on invalid input or if `output` is too small. */
506
+ base64DecodeInto(
507
+ input: Uint8Array,
508
+ output: Uint8Array,
509
+ urlSafe?: boolean,
510
+ padding?: boolean,
511
+ ): number
512
+ base64UrlEncode(input: Uint8Array): Uint8Array
513
+ base64UrlDecode(input: Uint8Array): Uint8Array
514
+ hexEncode(input: Uint8Array): Uint8Array
515
+ hexDecode(input: Uint8Array): Uint8Array
516
+ /** Reusable-output lowercase-hex encode; returns bytes written. Throws if `output` is too small. */
517
+ hexEncodeInto(input: Uint8Array, output: Uint8Array): number
518
+ /** Reusable-output hex decode; returns bytes written. Throws on bad input or if `output` is too small. */
519
+ hexDecodeInto(input: Uint8Array, output: Uint8Array): number
520
+ parseMediaType(input: Uint8Array): MediaTypeResult
521
+ etag(input: Uint8Array, weak?: boolean): Uint8Array
522
+ /** Reusable-output etag; returns bytes written (10 strong / 12 weak). Throws if `output` is too small. */
523
+ etagInto(input: Uint8Array, output: Uint8Array, weak?: boolean): number
524
+ /** Packed ETag batch (10 strong / 12 weak bytes per item). */
525
+ etagBatchPacked(input: Uint8Array, weak?: boolean | null): Uint8Array
526
+ httpDate(secs?: number): Uint8Array
527
+ /** Reusable-output http-date; returns bytes written (29). Throws if `output` is too small or the year is out of the fixed-width range. */
528
+ httpDateInto(secs: number | undefined, output: Uint8Array): number
529
+ parseHttpDate(input: Uint8Array): bigint | null
530
+
531
+ initThreadPool(rayonThreads?: number): void
532
+ rayonNumThreads(): number
533
+
534
+ jsonValidBatchPacked(input: Uint8Array): Uint8Array
535
+ validateEmailBatchPacked(input: Uint8Array): Uint8Array
536
+ validateUuidBatchPacked(input: Uint8Array): Uint8Array
537
+ validateIpv4BatchPacked(input: Uint8Array): Uint8Array
538
+ validateIpv6BatchPacked(input: Uint8Array): Uint8Array
539
+
540
+ jsonSumBatchPacked(input: Uint8Array): Uint8Array
541
+ queryParseBatchPacked(input: Uint8Array): Uint8Array
542
+ cookieParseBatchPacked(input: Uint8Array): Uint8Array
543
+ formParseBatchPacked(input: Uint8Array): Uint8Array
544
+ httpParseRequestBatchPacked(input: Uint8Array): Uint8Array
545
+ hexEncodeBatchPacked(input: Uint8Array): Uint8Array
546
+ hexDecodeBatchPacked(input: Uint8Array): Uint8Array
547
+ base64EncodeBatchPacked(
548
+ input: Uint8Array,
549
+ urlSafe?: boolean | null,
550
+ padding?: boolean | null,
551
+ ): Uint8Array
552
+ base64DecodeBatchPacked(
553
+ input: Uint8Array,
554
+ urlSafe?: boolean | null,
555
+ padding?: boolean | null,
556
+ ): Uint8Array
557
+
558
+ httpParseRequestPacked(input: Uint8Array): Uint8Array
559
+ /** Reusable-output packed HTTP parse; returns bytes written. Throws if `output` is too small. */
560
+ httpParseRequestPackedInto(input: Uint8Array, output: Uint8Array): number
561
+
562
+ queryParsePacked(input: Uint8Array): Uint8Array
563
+ /** Reusable-output packed query parse; returns bytes written. Throws if `output` is too small. */
564
+ queryParsePackedInto(input: Uint8Array, output: Uint8Array): number
565
+
566
+ cookieParsePacked(input: Uint8Array): Uint8Array
567
+ /** Reusable-output packed cookie parse; returns bytes written. Throws if `output` is too small. */
568
+ cookieParsePackedInto(input: Uint8Array, output: Uint8Array): number
569
+
570
+ formParsePacked(input: Uint8Array): Uint8Array
571
+
572
+ crc32BatchPacked(input: Uint8Array): Uint8Array
573
+
574
+ // ── Reusable-output (`_into`) packed batch variants ──
575
+ // Write the packed batch result into a caller-provided `output` buffer and
576
+ // return the number of bytes written (0 on empty). Wire format is identical
577
+ // to the allocating `*BatchPacked` variants. Throws when `output` is too
578
+ // small (the JS loader sizes these from a pool before calling).
579
+ jsonValidBatchPackedInto(input: Uint8Array, output: Uint8Array): number
580
+ validateEmailBatchPackedInto(input: Uint8Array, output: Uint8Array): number
581
+ validateUuidBatchPackedInto(input: Uint8Array, output: Uint8Array): number
582
+ validateIpv4BatchPackedInto(input: Uint8Array, output: Uint8Array): number
583
+ validateIpv6BatchPackedInto(input: Uint8Array, output: Uint8Array): number
584
+ jsonSumBatchPackedInto(input: Uint8Array, output: Uint8Array): number
585
+ fnv1A64BatchPackedInto(input: Uint8Array, output: Uint8Array): number
586
+ crc32BatchPackedInto(input: Uint8Array, output: Uint8Array): number
587
+
588
+ // ── Batch metadata / counts ──
589
+ jsonValidBatchCountPacked(input: Uint8Array): number
590
+ validateEmailBatchCountPacked(input: Uint8Array): number
591
+ validateUuidBatchCountPacked(input: Uint8Array): number
592
+ validateIpv4BatchCountPacked(input: Uint8Array): number
593
+ validateIpv6BatchCountPacked(input: Uint8Array): number
594
+ jsonSumBatchTotalPacked(input: Uint8Array): number
595
+ queryParseBatchTotalLenPacked(input: Uint8Array): number
596
+ cookieParseBatchTotalLenPacked(input: Uint8Array): number
597
+ httpParseRequestBatchTotalLenPacked(input: Uint8Array): number
598
+ }
@@ -0,0 +1,10 @@
1
+ // src/rust-ffi/addon.ts — Shared lazy addon accessor for the FFI layer.
2
+ //
3
+ // All rust-ffi namespaces share a single lazily-loading proxy: importing this
4
+ // module does NOT dlopen the addon — the first native call triggers loading
5
+ // exactly once.
6
+
7
+ import { lazyAddon, getAddon, type NativeAddon } from '../native'
8
+
9
+ /** The shared lazy addon proxy (typed as the full native surface). */
10
+ export const addon: NativeAddon = lazyAddon(getAddon)