castrum 0.9.3 → 0.9.6

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 (206) hide show
  1. package/Cargo.lock +2242 -0
  2. package/Cargo.toml +155 -0
  3. package/README.md +14 -6
  4. package/build.rs +5 -0
  5. package/castrum.darwin-arm64.node +0 -0
  6. package/castrum.darwin-x64.node +0 -0
  7. package/castrum.linux-arm64-gnu.node +0 -0
  8. package/castrum.linux-arm64-musl.node +0 -0
  9. package/castrum.linux-x64-gnu.node +0 -0
  10. package/castrum.linux-x64-musl.node +0 -0
  11. package/castrum.linux-x64-v3-gnu.node +0 -0
  12. package/castrum.win32-x64-msvc.node +0 -0
  13. package/dist/index.d.ts +1508 -1291
  14. package/dist/index.js +3391 -2069
  15. package/docs/API.md +2 -1
  16. package/docs/ARCHITECTURE.md +3 -2
  17. package/docs/CASE_STUDY.md +5 -3
  18. package/docs/ENVIRONMENT.md +25 -2
  19. package/docs/FFI_BUN_GUIDE.md +242 -14
  20. package/docs/HOW_TO_ADD_AN_OP.md +10 -0
  21. package/docs/REPO_MAP.md +17 -6
  22. package/docs/RND-CONCURRENCY.md +772 -0
  23. package/docs/bun-builtins-decision-matrix.md +4 -1
  24. package/index.ts +24 -25
  25. package/package.json +173 -156
  26. package/rust/crypto/aead.rs +378 -0
  27. package/rust/crypto/argon2.rs +265 -0
  28. package/rust/crypto/base64.rs +420 -0
  29. package/rust/crypto/bcrypt.rs +89 -0
  30. package/rust/crypto/cookie_sign.rs +169 -0
  31. package/rust/crypto/csrf.rs +106 -0
  32. package/rust/crypto/ed25519.rs +215 -0
  33. package/rust/crypto/hashing.rs +140 -0
  34. package/rust/crypto/hmac_sha256.rs +88 -0
  35. package/rust/crypto/jwt/api.rs +286 -0
  36. package/rust/crypto/jwt/mod.rs +22 -0
  37. package/rust/crypto/jwt/tests.rs +312 -0
  38. package/rust/crypto/jwt/token.rs +360 -0
  39. package/rust/crypto/mod.rs +27 -0
  40. package/rust/crypto/pbkdf2.rs +105 -0
  41. package/rust/crypto/random_token.rs +85 -0
  42. package/rust/crypto/session.rs +222 -0
  43. package/rust/ffi/crypto.rs +708 -0
  44. package/rust/ffi/hashing.rs +285 -0
  45. package/rust/ffi/http.rs +752 -0
  46. package/rust/ffi/ingress.rs +303 -0
  47. package/rust/ffi/json.rs +517 -0
  48. package/rust/ffi/jwt.rs +419 -0
  49. package/rust/ffi/metrics.rs +373 -0
  50. package/rust/ffi/mod.rs +69 -0
  51. package/rust/ffi/payload.rs +275 -0
  52. package/rust/ffi/probe.rs +60 -0
  53. package/rust/ffi/rate_limit.rs +77 -0
  54. package/rust/ffi/route.rs +85 -0
  55. package/rust/ffi/task.rs +172 -0
  56. package/rust/ffi/tests.rs +2333 -0
  57. package/rust/ffi/util.rs +152 -0
  58. package/rust/ffi/validators.rs +123 -0
  59. package/rust/http/accept.rs +606 -0
  60. package/rust/http/cookie_parser.rs +140 -0
  61. package/rust/http/etag.rs +289 -0
  62. package/rust/http/form.rs +136 -0
  63. package/rust/http/headers.rs +267 -0
  64. package/rust/http/http_date.rs +286 -0
  65. package/rust/http/http_parser.rs +252 -0
  66. package/rust/http/media_type.rs +355 -0
  67. package/rust/http/method.rs +200 -0
  68. package/rust/http/mime_lookup.rs +130 -0
  69. package/rust/http/mod.rs +32 -0
  70. package/rust/http/multipart.rs +685 -0
  71. package/rust/http/query_parser.rs +211 -0
  72. package/rust/http/url_codec.rs +296 -0
  73. package/rust/http/url_join.rs +324 -0
  74. package/rust/ingress/api.rs +355 -0
  75. package/rust/ingress/cors.rs +403 -0
  76. package/rust/ingress/ingress_constants.rs +209 -0
  77. package/rust/ingress/ip_trust.rs +312 -0
  78. package/rust/ingress/mod.rs +48 -0
  79. package/rust/ingress/native_route.rs +770 -0
  80. package/rust/ingress/options.rs +176 -0
  81. package/rust/ingress/output.rs +406 -0
  82. package/rust/ingress/packed.rs +191 -0
  83. package/rust/ingress/pipeline.rs +534 -0
  84. package/rust/ingress/proxy.rs +138 -0
  85. package/rust/ingress/rate_limit.rs +538 -0
  86. package/rust/ingress/terminal.rs +114 -0
  87. package/rust/ingress/tests.rs +567 -0
  88. package/rust/ingress/time.rs +70 -0
  89. package/rust/json/fast_schema/capture.rs +336 -0
  90. package/rust/json/fast_schema/compile.rs +556 -0
  91. package/rust/json/fast_schema/cursor.rs +368 -0
  92. package/rust/json/fast_schema/email.rs +282 -0
  93. package/rust/json/fast_schema/errors.rs +247 -0
  94. package/rust/json/fast_schema/mod.rs +57 -0
  95. package/rust/json/fast_schema/tests.rs +1143 -0
  96. package/rust/json/fast_schema/types.rs +140 -0
  97. package/rust/json/fast_schema/validate.rs +931 -0
  98. package/rust/json/json_ops.rs +121 -0
  99. package/rust/json/json_schema.rs +837 -0
  100. package/rust/json/json_ser.rs +883 -0
  101. package/rust/json/mod.rs +19 -0
  102. package/rust/json/napi_marshal.rs +318 -0
  103. package/rust/json/patch/api.rs +60 -0
  104. package/rust/json/patch/engine.rs +153 -0
  105. package/rust/json/patch/mod.rs +47 -0
  106. package/rust/json/patch/ops.rs +270 -0
  107. package/rust/json/patch/pointer.rs +49 -0
  108. package/rust/json/patch/tests.rs +499 -0
  109. package/rust/lib.rs +73 -0
  110. package/rust/metrics/api.rs +148 -0
  111. package/rust/metrics/mod.rs +21 -0
  112. package/rust/metrics/registry.rs +961 -0
  113. package/rust/panic_safety.rs +40 -0
  114. package/rust/payload/compress.rs +512 -0
  115. package/rust/payload/mod.rs +13 -0
  116. package/rust/payload/sse.rs +214 -0
  117. package/rust/payload/template.rs +253 -0
  118. package/rust/payload/websocket.rs +69 -0
  119. package/rust/payload/ws_frames.rs +354 -0
  120. package/rust/proptest_suite.rs +139 -0
  121. package/rust/selection.rs +95 -0
  122. package/rust/src/bin/native-bench.rs +495 -0
  123. package/rust/task/completion.rs +119 -0
  124. package/rust/task/mod.rs +65 -0
  125. package/rust/task/ops.rs +503 -0
  126. package/rust/task/runtime.rs +220 -0
  127. package/rust/task/tests.rs +231 -0
  128. package/rust/test_support.rs +71 -0
  129. package/rust/util/batch/api.rs +266 -0
  130. package/rust/util/batch/core.rs +473 -0
  131. package/rust/util/batch/mod.rs +23 -0
  132. package/rust/util/batch/tests.rs +445 -0
  133. package/rust/util/batch_core.rs +184 -0
  134. package/rust/util/bytes.rs +594 -0
  135. package/rust/util/mod.rs +34 -0
  136. package/rust/util/packed.rs +473 -0
  137. package/rust/util/text.rs +130 -0
  138. package/rust/util/threadpool.rs +128 -0
  139. package/rust/util/validation.rs +419 -0
  140. package/rust-toolchain.toml +17 -0
  141. package/scripts/postinstall.mjs +199 -0
  142. package/src/ingress/constants.ts +1 -1
  143. package/src/ingress/context.ts +2 -2
  144. package/src/ingress/decode/baked-result.ts +13 -13
  145. package/src/ingress/decode/fast-result.ts +17 -17
  146. package/src/ingress/errors.ts +7 -7
  147. package/src/ingress/headers/baked-templates.ts +1 -1
  148. package/src/ingress/headers/fast-templates.ts +3 -3
  149. package/src/ingress/health.ts +1 -2
  150. package/src/ingress/index.ts +1 -1
  151. package/src/ingress/native-route.ts +9 -5
  152. package/src/ingress/options.ts +1 -1
  153. package/src/ingress/packing/gather-raw-headers.ts +1 -1
  154. package/src/ingress/packing/route-wire.ts +1 -5
  155. package/src/ingress/response/error-bodies.ts +5 -5
  156. package/src/ingress/response/terminal.ts +1 -1
  157. package/src/ingress/routes/echo.ts +4 -4
  158. package/src/ingress/routes/fallback.ts +2 -2
  159. package/src/ingress/routes/head.ts +2 -2
  160. package/src/ingress/routes/native.ts +2 -1
  161. package/src/ingress/routes/options.ts +1 -1
  162. package/src/ingress/routes/read.ts +2 -2
  163. package/src/ingress/server.ts +10 -6
  164. package/src/ingress/status.ts +7 -7
  165. package/src/integration/batch.ts +1 -1
  166. package/src/integration/index.ts +9 -9
  167. package/src/integration/pipeline.ts +6 -6
  168. package/src/integration/websocket.ts +2 -2
  169. package/src/loader/index.ts +1 -5
  170. package/src/loader/ops.ts +1 -1
  171. package/src/native/ffi/build/codecs.ts +130 -12
  172. package/src/native/ffi/build/compress.ts +22 -10
  173. package/src/native/ffi/build/instances.ts +46 -4
  174. package/src/native/ffi/build/metrics.ts +247 -0
  175. package/src/native/ffi/build/parse.ts +59 -23
  176. package/src/native/ffi/build/task.ts +79 -0
  177. package/src/native/ffi/build/util.ts +32 -1
  178. package/src/native/ffi/build.ts +4 -0
  179. package/src/native/ffi/selftest.ts +12 -3
  180. package/src/native/ffi/types.ts +151 -9
  181. package/src/native/ffi.ts +160 -15
  182. package/src/native/index.ts +1 -1
  183. package/src/native/loader.ts +55 -5
  184. package/src/native/types/instances.ts +34 -0
  185. package/src/native/types.ts +11 -0
  186. package/src/runtime/codec.ts +53 -9
  187. package/src/runtime/types.ts +7 -0
  188. package/src/rust-ffi/batch/build.ts +16 -9
  189. package/src/rust-ffi/index.ts +7 -6
  190. package/src/rust-ffi/options.ts +1 -3
  191. package/src/rust-ffi/proven.ts +3 -3
  192. package/src/rust-ffi/scalar/crypto.ts +1 -1
  193. package/src/rust-ffi/scalar/factories.ts +73 -0
  194. package/src/rust-ffi/scalar/hashing.ts +1 -1
  195. package/src/rust-ffi/scalar/http.ts +71 -27
  196. package/src/rust-ffi/scalar/index.ts +2 -0
  197. package/src/rust-ffi/scalar/interface.ts +20 -0
  198. package/src/rust-ffi/scalar/payload.ts +11 -3
  199. package/src/rust-ffi/scalar/validation.ts +121 -0
  200. package/src/rust-ffi/text.ts +25 -3
  201. package/src/selection.ts +1 -1
  202. package/src/shared/bytes.ts +24 -4
  203. package/src/shared/codec.ts +18 -3
  204. package/src/task/index.ts +24 -0
  205. package/src/task/op.ts +171 -0
  206. package/src/task/runtime.ts +568 -0
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+ // scripts/postinstall.mjs — cross-platform native addon fallback builder.
3
+ //
4
+ // castrum ships PREBUILT addons for every `napi.targets` platform (the CI
5
+ // "build" job compiles them per-platform and the "publish" job stages all of
6
+ // them into one tarball), so on a supported platform this script is a fast
7
+ // no-op. It exists to keep the core CROSS-COMPATIBLE when that prebuilt path
8
+ // is NOT available:
9
+ //
10
+ // - the host platform / libc is not in napi.targets (e.g. linux-armv7,
11
+ // freebsd, android, x86 32-bit, …),
12
+ // - a partial tarball shipped without this platform's artifact
13
+ // (CASTRUM_PUBLISH_ALLOW_PARTIAL local publish), or
14
+ // - the artifact failed to load and the user wants a matching rebuild.
15
+ //
16
+ // In those cases it compiles the addon FROM SOURCE in the consumer's
17
+ // node_modules (`cargo build --release`), so the core keeps working anywhere a
18
+ // Rust toolchain exists.
19
+ //
20
+ // Behaviour (in order):
21
+ // 1. If a loadable artifact for this host already exists next to the script
22
+ // (a shipped prebuilt), do nothing — installs stay fast. Forced rebuild
23
+ // with `npm install --build-from-source`.
24
+ // 2. Else if CASTRUM_SKIP_BUILD is set, or CI is detected (GitHub Actions
25
+ // builds the addon explicitly), warn and skip.
26
+ // 3. Else if no `cargo`/`rustc` is on PATH, warn and skip (the runtime
27
+ // loader then fails with a clear "build it" error if the addon is needed).
28
+ // 4. Else run `cargo build --release` and copy the resulting cdylib to every
29
+ // artifact name the loader probes for this host, so whichever libc/name
30
+ // variant it tries first, it finds a correct file.
31
+ //
32
+ // CASTRUM_REQUIRE_BUILD=1 turns every "warn and skip" above into a hard install
33
+ // failure — for strict environments that must guarantee a loadable addon.
34
+ //
35
+ // No dependency on the `@napi-rs/cli`: `build.rs` runs `napi_build::setup()`
36
+ // during a plain `cargo build`, and the produced cdylib IS the `.node` file the
37
+ // napi CLI would rename — so this works in a consumer's node_modules where the
38
+ // CLI (a devDependency) is not installed.
39
+ //
40
+ // The pure helpers are exported for tests (test/unit/native/postinstall.test.ts);
41
+ // the script only auto-runs when invoked directly (npm/bun postinstall hook).
42
+
43
+ import { spawnSync } from 'node:child_process'
44
+ import { copyFileSync, existsSync, statSync } from 'node:fs'
45
+ import { dirname, join } from 'node:path'
46
+ import { fileURLToPath, pathToFileURL } from 'node:url'
47
+
48
+ const ROOT = dirname(dirname(fileURLToPath(import.meta.url)))
49
+ const IS_MAIN = process.argv[1] != null && import.meta.url === pathToFileURL(process.argv[1]).href
50
+
51
+ const log = (msg) => console.log(`postinstall: ${msg}`)
52
+ const warn = (msg) => console.warn(`postinstall: WARNING: ${msg}`)
53
+
54
+ /**
55
+ * Artifact names `src/native/loader.ts` probes for a host, mirroring its
56
+ * `libcVariants` logic. A source build always produces the portable BASELINE
57
+ * (not the optional x86-64-v3 SIMD variant), so this omits the v3 name — the
58
+ * loader falls through from the absent v3 file to the baseline names below.
59
+ *
60
+ * @param platform `process.platform`-style value ('linux' | 'darwin' | 'win32' | …)
61
+ * @param arch `process.arch`-style value ('x64' | 'arm64' | …)
62
+ * @returns artifact file names, in loader probe order
63
+ */
64
+ export function napiArtifactCandidates(platform, arch) {
65
+ const libcVariants =
66
+ platform === 'win32' ? ['msvc', 'gnu'] : platform === 'linux' ? ['gnu', 'musl'] : ['']
67
+ const names = libcVariants.map(
68
+ (libc) => `castrum.${platform}-${arch}${libc ? `-${libc}` : ''}.node`,
69
+ )
70
+ names.push('castrum.node')
71
+ return names
72
+ }
73
+
74
+ /**
75
+ * Path of the cdylib `cargo build --release` produces for the current host
76
+ * (crate [lib] name = "castrum"; build.rs runs napi_build::setup()).
77
+ *
78
+ * @param root package root directory
79
+ * @param platform `process.platform`-style value
80
+ * @returns absolute path to the built cdylib
81
+ */
82
+ export function cdylibArtifact(root, platform) {
83
+ const release = join(root, 'target', 'release')
84
+ if (platform === 'win32') return join(release, 'castrum.dll')
85
+ if (platform === 'darwin') return join(release, 'libcastrum.dylib')
86
+ return join(release, 'libcastrum.so')
87
+ }
88
+
89
+ function commandExists(cmd, env) {
90
+ const probe =
91
+ process.platform === 'win32'
92
+ ? spawnSync('where', [cmd], { stdio: 'ignore', env })
93
+ : spawnSync('which', [cmd], { stdio: 'ignore', env })
94
+ return probe.status === 0
95
+ }
96
+
97
+ /**
98
+ * Ensure a loadable native addon exists for this host, building from source as
99
+ * a fallback when no prebuilt ships. Pure-ish and testable: every input
100
+ * (`root`, `platform`, `arch`, `env`) can be injected, and `cargo` is only ever
101
+ * invoked after all short-circuit checks pass.
102
+ *
103
+ * @param root package root (where the .node artifacts live / where to build)
104
+ * @param opts overrides for testability: `{ platform, arch, env }`
105
+ * @returns `{ status: 'skip', reason }` or `{ status: 'built', candidates }`
106
+ */
107
+ export function ensureNativeAddon(root = ROOT, opts = {}) {
108
+ const platform = opts.platform ?? process.platform
109
+ const arch = opts.arch ?? process.arch
110
+ const env = opts.env ?? process.env
111
+
112
+ const candidates = napiArtifactCandidates(platform, arch)
113
+ const prebuilt = candidates.find((name) => {
114
+ try {
115
+ return statSync(join(root, name)).size > 0
116
+ } catch {
117
+ return false
118
+ }
119
+ })
120
+ const force = env.npm_config_build_from_source === 'true'
121
+
122
+ if (prebuilt && !force) {
123
+ log(`prebuilt addon present (${prebuilt}) — nothing to build`)
124
+ return { status: 'skip', reason: 'prebuilt' }
125
+ }
126
+ if (force) log('npm_config_build_from_source set — forcing a source rebuild')
127
+
128
+ if (env.CASTRUM_SKIP_BUILD) {
129
+ warn(
130
+ 'CASTRUM_SKIP_BUILD is set — skipping the source build. If no prebuilt ' +
131
+ 'ships for this platform the native addon will be missing at runtime.',
132
+ )
133
+ return { status: 'skip', reason: 'env' }
134
+ }
135
+ if (env.CI) {
136
+ // GitHub Actions etc. install deps first, then build the addon explicitly
137
+ // (bun run build / napi build) — don't double-build during bun install.
138
+ if (!prebuilt) {
139
+ warn('CI detected — skipping the source build (CI builds the addon explicitly).')
140
+ }
141
+ return { status: 'skip', reason: 'ci' }
142
+ }
143
+
144
+ if (!commandExists('cargo', env) || !commandExists('rustc', env)) {
145
+ const msg =
146
+ `no Rust toolchain (cargo/rustc) found — cannot build the castrum addon for ` +
147
+ `${platform}/${arch}. Install Rust from https://rustup.rs and re-run ` +
148
+ `npm install, or set CASTRUM_SKIP_BUILD=1 to silence this warning.`
149
+ if (env.CASTRUM_REQUIRE_BUILD) throw new Error(msg)
150
+ warn(msg)
151
+ return { status: 'skip', reason: 'no-toolchain' }
152
+ }
153
+
154
+ log(
155
+ `no prebuilt addon for ${platform}/${arch} — building from source ` +
156
+ '(cargo build --release; requires network to crates.io, a few minutes on first run)',
157
+ )
158
+ // Windows needs the shell so `cargo` resolves via PATH/PATHEXT (cargo.exe).
159
+ const run =
160
+ process.platform === 'win32'
161
+ ? spawnSync('cargo build --release', { cwd: root, stdio: 'inherit', shell: true })
162
+ : spawnSync('cargo', ['build', '--release'], { cwd: root, stdio: 'inherit' })
163
+ if (run.status !== 0) {
164
+ const msg =
165
+ `cargo build --release failed (exit ${run.status ?? 'unknown'}); the castrum ` +
166
+ `native addon could not be built for ${platform}/${arch}.`
167
+ if (env.CASTRUM_REQUIRE_BUILD) throw new Error(msg)
168
+ warn(msg)
169
+ return { status: 'skip', reason: 'build-failed' }
170
+ }
171
+
172
+ const built = cdylibArtifact(root, platform)
173
+ if (!existsSync(built)) {
174
+ const msg = `build finished but no cdylib found at ${built}`
175
+ if (env.CASTRUM_REQUIRE_BUILD) throw new Error(msg)
176
+ warn(msg)
177
+ return { status: 'skip', reason: 'artifact-missing' }
178
+ }
179
+
180
+ // Copy the SAME correctly-built binary to every candidate name the loader
181
+ // probes. Only one is ever loaded (the first existing candidate), and since
182
+ // the file was built on this exact host its content always matches — so a
183
+ // glibc build copied under the musl name is still the right binary for this
184
+ // machine, and the loader finds a working file regardless of its probe order.
185
+ for (const name of candidates) {
186
+ copyFileSync(built, join(root, name))
187
+ }
188
+ log(`built + staged native addon for ${platform}/${arch}: ${candidates.join(', ')}`)
189
+ return { status: 'built', candidates }
190
+ }
191
+
192
+ if (IS_MAIN) {
193
+ try {
194
+ ensureNativeAddon()
195
+ } catch (err) {
196
+ console.error(`\npostinstall: ERROR: ${err instanceof Error ? err.message : String(err)}\n`)
197
+ process.exit(1)
198
+ }
199
+ }
@@ -11,7 +11,7 @@
11
11
  // they cannot drift.
12
12
 
13
13
  import { getAddon } from '../native'
14
- import { getBunFFI, type BunFFI } from '../native/ffi'
14
+ import { type BunFFI, getBunFFI } from '../native/ffi'
15
15
 
16
16
  // Transport: bun:ffi is PRIMARY on Bun, so the layout is read through the
17
17
  // C-ABI blob WITHOUT dlopening the napi addon. Node — or forced
@@ -1,10 +1,10 @@
1
1
  // src/ingress/context.ts — Result snapshotting + synthetic/error context
2
2
  // builders for the async ingress API.
3
3
 
4
+ import { ERR_CODE_INTERNAL, ERR_CODE_RATE_LIMITED, HV_CORS_SIMPLE, HV_JSON } from './constants'
4
5
  import type { ResponseBuildContext } from './headers/fast-templates'
5
6
  import { buildTerminalResponse } from './response/terminal'
6
- import { ERR_CODE_INTERNAL, ERR_CODE_RATE_LIMITED, HV_JSON, HV_CORS_SIMPLE } from './constants'
7
- import type { IngressOptions, IngressResult, IngressContext } from './types'
7
+ import type { IngressContext, IngressOptions, IngressResult } from './types'
8
8
 
9
9
  const EMPTY_BODY = new Uint8Array(0)
10
10
 
@@ -11,27 +11,27 @@
11
11
  // via ./result-base.ts.
12
12
 
13
13
  import {
14
- OUT_VERDICT,
15
- OUT_FLAGS,
16
- OUT_RATE_LIMIT,
17
- OUT_RATE_REMAINING,
18
- OUT_COOKIES_JSON_LEN,
19
- OUT_QUERY_JSON_LEN,
20
- OUT_HEADER_VARIANT,
21
- OUT_BODY_JSON_LEN,
22
- OUT_DATA_START,
14
+ ERR_CODE_INTERNAL as ERROR_CODE_INTERNAL,
15
+ FLAG_BODY_TRUNCATED,
23
16
  FLAG_BODY_VALID_JSON,
24
- FLAG_SCHEMA_VALID,
25
17
  FLAG_CORS_ALLOWED,
26
18
  FLAG_IS_PREFLIGHT,
27
19
  FLAG_RATE_LIMITED,
20
+ FLAG_SCHEMA_VALID,
28
21
  FLAG_TRUSTED_PROXY,
29
- FLAG_BODY_TRUNCATED,
30
22
  HV_JSON,
31
- ERR_CODE_INTERNAL as ERROR_CODE_INTERNAL,
23
+ OUT_BODY_JSON_LEN,
24
+ OUT_COOKIES_JSON_LEN,
25
+ OUT_DATA_START,
26
+ OUT_FLAGS,
27
+ OUT_HEADER_VARIANT,
28
+ OUT_QUERY_JSON_LEN,
29
+ OUT_RATE_LIMIT,
30
+ OUT_RATE_REMAINING,
31
+ OUT_VERDICT,
32
32
  } from '../constants'
33
- import { sectionLayout } from './packed-sections'
34
33
  import { isValidResponseStatus } from '../status'
34
+ import { sectionLayout } from './packed-sections'
35
35
  import { IngressResultBase } from './result-base'
36
36
 
37
37
  /** Zero-alloc, reusable result decoder for the pre-baked handler path. */
@@ -12,28 +12,28 @@
12
12
 
13
13
  import { viewForArrayBuffer } from '../../shared/bytes'
14
14
  import {
15
- OUT_VERDICT,
16
- OUT_ERROR_CODE,
17
- OUT_STATUS,
18
- OUT_FLAGS,
19
- OUT_RATE_LIMIT,
20
- OUT_RATE_REMAINING,
21
- OUT_COOKIES_JSON_LEN,
22
- OUT_QUERY_JSON_LEN,
23
- OUT_HEADER_VARIANT,
24
- OUT_BODY_JSON_LEN,
25
- OUT_DATA_START,
26
- FLAG_HAS_COOKIES,
27
- FLAG_HAS_QUERY,
15
+ ERR_CODE_INTERNAL,
16
+ FLAG_BODY_TRUNCATED,
28
17
  FLAG_BODY_VALID_JSON,
29
- FLAG_SCHEMA_VALID,
30
18
  FLAG_CORS_ALLOWED,
19
+ FLAG_HAS_COOKIES,
20
+ FLAG_HAS_QUERY,
21
+ FLAG_HTTPS,
31
22
  FLAG_IS_PREFLIGHT,
32
23
  FLAG_RATE_LIMITED,
33
- FLAG_HTTPS,
24
+ FLAG_SCHEMA_VALID,
34
25
  FLAG_TRUSTED_PROXY,
35
- FLAG_BODY_TRUNCATED,
36
- ERR_CODE_INTERNAL,
26
+ OUT_BODY_JSON_LEN,
27
+ OUT_COOKIES_JSON_LEN,
28
+ OUT_DATA_START,
29
+ OUT_ERROR_CODE,
30
+ OUT_FLAGS,
31
+ OUT_HEADER_VARIANT,
32
+ OUT_QUERY_JSON_LEN,
33
+ OUT_RATE_LIMIT,
34
+ OUT_RATE_REMAINING,
35
+ OUT_STATUS,
36
+ OUT_VERDICT,
37
37
  } from '../constants'
38
38
  import { normalizeResponseStatus } from '../status'
39
39
  import { sectionLayout } from './packed-sections'
@@ -5,16 +5,16 @@
5
5
  // the two mappings are intentionally not unified (see AGENTS.md).
6
6
 
7
7
  import {
8
- ERR_CODE_NONE,
9
- ERR_CODE_CORS_PREFLIGHT,
10
- ERR_CODE_RATE_LIMITED,
8
+ ERR_CODE_BAD_REQUEST,
11
9
  ERR_CODE_BODY_TOO_LARGE,
10
+ ERR_CODE_CORS_PREFLIGHT,
11
+ ERR_CODE_INTERNAL,
12
12
  ERR_CODE_INVALID_JSON,
13
- ERR_CODE_SCHEMA_VALIDATION,
14
- ERR_CODE_BAD_REQUEST,
15
- ERR_CODE_REQUEST_TOO_LARGE,
13
+ ERR_CODE_NONE,
14
+ ERR_CODE_RATE_LIMITED,
16
15
  ERR_CODE_REQUEST_TIMEOUT,
17
- ERR_CODE_INTERNAL,
16
+ ERR_CODE_REQUEST_TOO_LARGE,
17
+ ERR_CODE_SCHEMA_VALIDATION,
18
18
  } from './constants'
19
19
 
20
20
  /** Map a native error code to its stable string name. */
@@ -8,7 +8,7 @@
8
8
  // builder (./fast-templates.ts) with a different wire format (x-ratelimit-*) —
9
9
  // do not unify them (see AGENTS.md).
10
10
 
11
- import { HV_JSON, HV_CORS_SIMPLE, HV_CORS_PREFLIGHT, HV_RATE_ACTIVE } from '../constants'
11
+ import { HV_CORS_PREFLIGHT, HV_CORS_SIMPLE, HV_JSON, HV_RATE_ACTIVE } from '../constants'
12
12
 
13
13
  /** Inputs needed to build the pre-baked header templates. */
14
14
  export interface BakedTemplateParams {
@@ -9,15 +9,15 @@
9
9
  // a different wire format — do not unify them (see AGENTS.md).
10
10
 
11
11
  import {
12
- HV_COUNT,
13
- HV_CORS_SIMPLE,
14
12
  HV_CORS_PREFLIGHT,
13
+ HV_CORS_SIMPLE,
14
+ HV_COUNT,
15
15
  HV_RATE_ACTIVE,
16
16
  HV_RATE_LIMITED,
17
17
  } from '../constants'
18
+ import { secondsFromMs } from '../shared'
18
19
  import { buildCorsStaticStrings, type CorsOptions, type CorsStaticStrings } from './cors'
19
20
  import { buildHstsValue, buildSecurityPairs, type SecurityHeadersOptions } from './hsts'
20
- import { secondsFromMs } from '../shared'
21
21
 
22
22
  /** A single precomputed header-variant template. */
23
23
  export interface HeaderTemplate {
@@ -49,10 +49,9 @@ export function readinessHandler(check?: () => boolean | Promise<boolean>) {
49
49
  return ok
50
50
  ? jsonResponse(200, { status: 'ready' })
51
51
  : jsonResponse(503, { status: 'not_ready' })
52
- } catch (err) {
52
+ } catch (_err) {
53
53
  return jsonResponse(503, {
54
54
  status: 'not_ready',
55
- error: err instanceof Error ? err.message : String(err),
56
55
  })
57
56
  }
58
57
  }
@@ -36,9 +36,9 @@ export type { IngressMetrics } from './metrics'
36
36
  export { createIngressMetrics, metricsHandler } from './metrics'
37
37
  export type { NativeRoute, NativeRoutePlan } from './native-route'
38
38
  export { createNativeRoute } from './native-route'
39
+ export type { IngressFastHandler, IngressFastOptions } from './options'
39
40
  // ── Route-wire v3 byte contract (PURE wire helpers) ───────────────
40
41
  export * from './packing/route-wire'
41
- export type { IngressFastHandler, IngressFastOptions } from './options'
42
42
  export { buildTerminalResponse } from './response/terminal'
43
43
  export type { CreateIngressRouterOptions, IngressRouter, RouterRouteSpec } from './router'
44
44
  export { createIngressRouter } from './router'
@@ -14,15 +14,15 @@
14
14
  // `handlers.ts`/`server.ts`; the byte wire helpers it uses are the PURE
15
15
  // `src/ingress/packing/route-wire.ts`.
16
16
 
17
- import { getBunFFI } from '../native/ffi'
18
17
  import { getAddon } from '../native'
18
+ import { getBunFFI } from '../native/ffi'
19
19
  import type { BunFFI } from '../native/ffi/types'
20
20
  import {
21
- ROUTE_PART,
22
- ROUTE_STAGE,
23
21
  decodeRouteResult,
24
22
  encodeRouteDescriptor,
25
23
  packRouteFrame,
24
+ ROUTE_PART,
25
+ ROUTE_STAGE,
26
26
  type RouteStageTag,
27
27
  type RouteWireLimits,
28
28
  type RouteWireResult,
@@ -168,9 +168,13 @@ function compileFfi(bunFFI: BunFFI, descriptor: Uint8Array): number {
168
168
  }
169
169
 
170
170
  /** Compile through the napi `Route` class (Node / fallback transport). */
171
- function compileNapi(descriptor: Uint8Array): { run: (frame: Uint8Array, out: Uint8Array) => number } | null {
171
+ function compileNapi(
172
+ descriptor: Uint8Array,
173
+ ): { run: (frame: Uint8Array, out: Uint8Array) => number } | null {
172
174
  const addon = getAddon()
173
- const Route = (addon as { Route?: new (d: Uint8Array) => { run: (f: Uint8Array, o: Uint8Array) => number } }).Route
175
+ const Route = (
176
+ addon as { Route?: new (d: Uint8Array) => { run: (f: Uint8Array, o: Uint8Array) => number } }
177
+ ).Route
174
178
  if (typeof Route !== 'function') {
175
179
  return null // addon without the route stack (pre-rebuild) — caller falls back
176
180
  }
@@ -78,9 +78,9 @@ export interface IngressFastHandler {
78
78
  */
79
79
  export type IngressHandlerOptions = Omit<IngressFastOptions, 'onError'>
80
80
 
81
+ import type { FastIngressResult } from './decode/fast-result'
81
82
  import type { CorsOptions } from './headers/cors'
82
83
  import type { SecurityHeadersOptions } from './headers/hsts'
83
- import type { FastIngressResult } from './decode/fast-result'
84
84
 
85
85
  const KNOWN_INGRESS_OPTION_KEYS: ReadonlySet<string> = new Set([
86
86
  'trustProxy',
@@ -9,8 +9,8 @@
9
9
  import type { HeaderPlan } from '../shared'
10
10
  import { METHOD_KIND } from '../shared'
11
11
  import { writeHeaderPair } from './header-packing'
12
- import { forEachSelectedHeader, HDR_ORIGIN } from './select-headers'
13
12
  import { getHeaderBuf, MAX_SMALL_HEADER_BYTES } from './scratch'
13
+ import { forEachSelectedHeader, HDR_ORIGIN } from './select-headers'
14
14
 
15
15
  // Shared per-header size guards (single source of truth in scratch.ts).
16
16
  // Re-exported for back-compat with imports that referenced them here.
@@ -112,11 +112,7 @@ export function encodeRouteDescriptor(
112
112
  * Encode a request frame: `[flags u32][qLen][query][cLen][cookie]([bLen][body])`.
113
113
  * The `flags` word currently only carries `ROUTE_FRAME_FLAG_HAS_BODY`.
114
114
  */
115
- export function packRouteFrame(
116
- query: string,
117
- cookie: string,
118
- body: Uint8Array | null,
119
- ): Uint8Array {
115
+ export function packRouteFrame(query: string, cookie: string, body: Uint8Array | null): Uint8Array {
120
116
  const q = encoder.encode(query)
121
117
  const c = encoder.encode(cookie)
122
118
  const hasBody = body !== null && body.byteLength > 0
@@ -8,14 +8,14 @@
8
8
 
9
9
  import { encoder } from '../../shared/bytes'
10
10
  import {
11
- ERR_CODE_CORS_PREFLIGHT,
12
- ERR_CODE_RATE_LIMITED,
11
+ ERR_CODE_BAD_REQUEST,
13
12
  ERR_CODE_BODY_TOO_LARGE,
13
+ ERR_CODE_CORS_PREFLIGHT,
14
+ ERR_CODE_INTERNAL,
14
15
  ERR_CODE_INVALID_JSON,
15
- ERR_CODE_SCHEMA_VALIDATION,
16
- ERR_CODE_BAD_REQUEST,
16
+ ERR_CODE_RATE_LIMITED,
17
17
  ERR_CODE_REQUEST_TOO_LARGE,
18
- ERR_CODE_INTERNAL,
18
+ ERR_CODE_SCHEMA_VALIDATION,
19
19
  } from '../constants'
20
20
 
21
21
  function staticErrorBody(code: string, message: string): Uint8Array {
@@ -6,9 +6,9 @@
6
6
  // the pre-baked path has its own builders (see ./handlers.ts) — do not unify
7
7
  // them (see AGENTS.md).
8
8
 
9
+ import { errorCodeName, errorMessage } from '../errors'
9
10
  import { headersForResult, type ResponseBuildContext } from '../headers/fast-templates'
10
11
  import { safeTerminalStatus } from '../status'
11
- import { errorCodeName, errorMessage } from '../errors'
12
12
 
13
13
  /**
14
14
  * Build a terminal response for a result, or `null` when the result is not
@@ -1,11 +1,11 @@
1
1
  // src/ingress/routes/echo.ts — Pre-baked echo handler.
2
2
 
3
- import type { OptimizedIngressHandler } from '../types'
4
- import { resolveIp, type BakedHandlerOptions } from './common'
5
- import { DEFAULT_MAX_BODY_BYTES, DEFAULT_BODY_TIMEOUT_MS, secondsFromMs } from '../shared'
3
+ import { readBodyWithLimit } from '../body'
6
4
  import { HV_JSON } from '../constants'
7
5
  import { ERROR_BODIES } from '../response/error-bodies'
8
- import { readBodyWithLimit } from '../body'
6
+ import { DEFAULT_BODY_TIMEOUT_MS, DEFAULT_MAX_BODY_BYTES, secondsFromMs } from '../shared'
7
+ import type { OptimizedIngressHandler } from '../types'
8
+ import { type BakedHandlerOptions, resolveIp } from './common'
9
9
 
10
10
  /**
11
11
  * Pre-baked echo handler: streams the request body back with the client's
@@ -1,8 +1,8 @@
1
1
  // src/ingress/routes/fallback.ts — Pre-baked fallback handler.
2
2
 
3
- import type { BakedContext, OptimizedIngressHandler } from '../types'
4
3
  import type { BakedIngressResult } from '../decode/baked-result'
5
- import { runBaked, type BakedHandlerOptions } from './common'
4
+ import type { BakedContext, OptimizedIngressHandler } from '../types'
5
+ import { type BakedHandlerOptions, runBaked } from './common'
6
6
 
7
7
  /**
8
8
  * Pre-baked fallback handler: 404 for unmatched routes / OPTIONS.
@@ -1,8 +1,8 @@
1
1
  // src/ingress/routes/head.ts — Pre-baked HEAD read handler.
2
2
 
3
- import type { BakedContext, OptimizedIngressHandler } from '../types'
4
3
  import type { BakedIngressResult } from '../decode/baked-result'
5
- import { buildSuccessInit, runBaked, type BakedHandlerOptions } from './common'
4
+ import type { BakedContext, OptimizedIngressHandler } from '../types'
5
+ import { type BakedHandlerOptions, buildSuccessInit, runBaked } from './common'
6
6
 
7
7
  /**
8
8
  * Pre-baked HEAD read handler: headers only, no body.
@@ -23,6 +23,7 @@ import { decoder } from '../../shared/bytes'
23
23
  import { generateRequestId } from '../../shared/request-id'
24
24
  import { readBodyWithLimit } from '../body'
25
25
  import type { NativeRoute } from '../native-route'
26
+ import type { RouteWireResult } from '../packing/route-wire'
26
27
  import type { RouteHandler } from '../server'
27
28
  import { DEFAULT_BODY_TIMEOUT_MS, DEFAULT_MAX_BODY_BYTES } from '../shared'
28
29
  import type { NativeRequestContext, NativeResponder } from '../types'
@@ -97,7 +98,7 @@ export function nativeRouteHandler(
97
98
  const queryStr = qIndex >= 0 ? url.slice(qIndex + 1) : ''
98
99
  const cookieStr = req.headers.get('cookie') ?? ''
99
100
 
100
- let result
101
+ let result: RouteWireResult
101
102
  try {
102
103
  result = route.run(queryStr, cookieStr, body)
103
104
  } catch {
@@ -1,7 +1,7 @@
1
1
  // src/ingress/routes/options.ts — Pre-baked OPTIONS handler (CORS preflight).
2
2
 
3
3
  import type { OptimizedIngressHandler } from '../types'
4
- import { buildSuccessInit, resolveIp, type BakedHandlerOptions } from './common'
4
+ import { type BakedHandlerOptions, buildSuccessInit, resolveIp } from './common'
5
5
 
6
6
  /**
7
7
  * Pre-baked OPTIONS handler: serves the native pipeline's CORS preflight
@@ -1,8 +1,8 @@
1
1
  // src/ingress/routes/read.ts — Pre-baked GET read handler.
2
2
 
3
- import type { BakedContext, OptimizedIngressHandler } from '../types'
4
3
  import type { BakedIngressResult } from '../decode/baked-result'
5
- import { buildSuccessInit, runBaked, type BakedHandlerOptions } from './common'
4
+ import type { BakedContext, OptimizedIngressHandler } from '../types'
5
+ import { type BakedHandlerOptions, buildSuccessInit, runBaked } from './common'
6
6
 
7
7
  /**
8
8
  * Pre-baked GET read handler: returns the ingress body JSON on success.
@@ -1,6 +1,8 @@
1
1
  // src/ingress/server.ts — Bun.serve builder over pre-baked route handlers.
2
2
 
3
3
  import { isBun } from '../shared/runtime'
4
+ import type { NativeRoutePlan } from './native-route'
5
+ import { createNativeRoute } from './native-route'
4
6
  import {
5
7
  deleteHandler,
6
8
  echoHandler,
@@ -13,8 +15,6 @@ import {
13
15
  import type { BakedHandlerOptions } from './routes/common'
14
16
  import { nativeRouteHandler } from './routes/native'
15
17
  import { nativeResponderRoute } from './routes/responder'
16
- import { createNativeRoute } from './native-route'
17
- import type { NativeRoutePlan } from './native-route'
18
18
  import type { NativeResponder, OptimizedIngressHandler, TerminalStyle } from './types'
19
19
 
20
20
  /** Server-level default for the socket request-body cap (16 MiB). */
@@ -304,10 +304,14 @@ export function buildRouteHandlers(options: BuildRouteHandlersOptions): {
304
304
  // LEAN native-stack responder route: route-wire v3 stack, no envelope.
305
305
  // The compiled route is injected into the pure route factory (DI across
306
306
  // the purity boundary — the compile touches the dlopen layer here).
307
- const nativeRoute = nativeRouteHandler(createNativeRoute(spec.native.plan), spec.native.handler, {
308
- ...routeOpts,
309
- readBody: spec.native.readBody,
310
- })
307
+ const nativeRoute = nativeRouteHandler(
308
+ createNativeRoute(spec.native.plan),
309
+ spec.native.handler,
310
+ {
311
+ ...routeOpts,
312
+ readBody: spec.native.readBody,
313
+ },
314
+ )
311
315
  const nativeMethods = spec.native.methods ?? ['GET']
312
316
  for (const m of nativeMethods) {
313
317
  methods[m] = nativeRoute
@@ -6,16 +6,16 @@
6
6
  // ./constants.ts, so they live in one place and cannot drift.
7
7
 
8
8
  import {
9
- ERR_CODE_NONE,
10
- ERR_CODE_CORS_PREFLIGHT,
11
- ERR_CODE_RATE_LIMITED,
9
+ ERR_CODE_BAD_REQUEST,
12
10
  ERR_CODE_BODY_TOO_LARGE,
11
+ ERR_CODE_CORS_PREFLIGHT,
12
+ ERR_CODE_INTERNAL,
13
13
  ERR_CODE_INVALID_JSON,
14
- ERR_CODE_SCHEMA_VALIDATION,
15
- ERR_CODE_BAD_REQUEST,
16
- ERR_CODE_REQUEST_TOO_LARGE,
14
+ ERR_CODE_NONE,
15
+ ERR_CODE_RATE_LIMITED,
17
16
  ERR_CODE_REQUEST_TIMEOUT,
18
- ERR_CODE_INTERNAL,
17
+ ERR_CODE_REQUEST_TOO_LARGE,
18
+ ERR_CODE_SCHEMA_VALIDATION,
19
19
  } from './constants'
20
20
 
21
21
  /**
@@ -13,12 +13,12 @@
13
13
  // checks) — exactly the pattern these helpers exist for.
14
14
 
15
15
  import {
16
- loader,
17
16
  type LoaderBulk,
18
17
  type LoaderBulkArgs,
19
18
  type LoaderOpArgs,
20
19
  type LoaderOpName,
21
20
  type LoaderScalar,
21
+ loader,
22
22
  } from '../loader'
23
23
  import type { SchemaValidator } from '../shared/packed'
24
24