castrum 0.7.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +154 -368
- package/castrum.linux-x64-gnu.node +0 -0
- package/dist/index.d.ts +2763 -0
- package/dist/index.js +8707 -0
- package/docs/API.md +337 -0
- package/docs/ARCHITECTURE.md +507 -0
- package/docs/BENCHMARKS.md +343 -0
- package/docs/CASE_STUDY.md +240 -0
- package/docs/ENVIRONMENT.md +81 -0
- package/docs/FFI_BUN_GUIDE.md +327 -0
- package/docs/GETTING_STARTED.md +192 -0
- package/docs/INGRESS-ROUTER.md +81 -0
- package/docs/INGRESS.md +746 -0
- package/docs/NATIVE-ROUTE.md +53 -0
- package/docs/REPO_MAP.md +279 -0
- package/docs/adr/0001-two-wire-formats.md +39 -0
- package/docs/adr/0002-zero-dom-fast-schema.md +38 -0
- package/docs/adr/0003-bun-builtins-delegation.md +43 -0
- package/docs/bun-builtins-decision-matrix.md +115 -0
- package/index.ts +44 -8
- package/package.json +98 -33
- package/src/ingress/body.ts +289 -0
- package/src/ingress/constants.ts +220 -48
- package/src/ingress/context.ts +136 -0
- package/src/ingress/decode/baked-result.ts +121 -0
- package/src/ingress/decode/fast-result.ts +160 -0
- package/src/ingress/decode/packed-sections.ts +56 -0
- package/src/ingress/decode/result-base.ts +172 -0
- package/src/ingress/errors.ts +72 -0
- package/src/ingress/fast.ts +152 -1021
- package/src/ingress/handlers.ts +639 -1001
- package/src/ingress/headers/baked-templates.ts +102 -0
- package/src/ingress/headers/cors.ts +44 -0
- package/src/ingress/headers/fast-templates.ts +238 -0
- package/src/ingress/headers/hsts.ts +83 -0
- package/src/ingress/health.ts +68 -0
- package/src/ingress/index.ts +142 -405
- package/src/ingress/metrics.ts +132 -0
- package/src/ingress/options.ts +197 -0
- package/src/ingress/packing/gather-raw-headers.ts +139 -0
- package/src/ingress/packing/header-packing.ts +75 -0
- package/src/ingress/packing/input-packer.ts +164 -0
- package/src/ingress/packing/scratch.ts +50 -0
- package/src/ingress/packing/select-headers.ts +102 -0
- package/src/ingress/response/error-bodies.ts +75 -0
- package/src/ingress/response/terminal.ts +59 -0
- package/src/ingress/router.ts +237 -0
- package/src/ingress/routes/common.ts +79 -0
- package/src/ingress/routes/delete.ts +14 -0
- package/src/ingress/routes/echo.ts +107 -0
- package/src/ingress/routes/fallback.ts +23 -0
- package/src/ingress/routes/head.ts +27 -0
- package/src/ingress/routes/index.ts +11 -0
- package/src/ingress/routes/json-write.ts +129 -0
- package/src/ingress/routes/options.ts +26 -0
- package/src/ingress/routes/read.ts +35 -0
- package/src/ingress/routes/responder.ts +159 -0
- package/src/ingress/server-node.ts +376 -0
- package/src/ingress/server.ts +475 -0
- package/src/ingress/shared.ts +132 -0
- package/src/ingress/status.ts +97 -0
- package/src/ingress/types.ts +191 -0
- package/src/integration/batch.ts +58 -0
- package/src/integration/index.ts +29 -0
- package/src/integration/pipeline.ts +220 -0
- package/src/integration/streaming.ts +78 -0
- package/src/integration/websocket.ts +93 -0
- package/src/loader/batch.ts +137 -0
- package/src/loader/cost.ts +105 -0
- package/src/loader/index.ts +745 -0
- package/src/loader/ops.ts +657 -0
- package/src/loader/state.ts +84 -0
- package/src/loader/types.ts +145 -0
- package/src/native/ffi/constants.ts +58 -0
- package/src/native/ffi/selftest.ts +729 -0
- package/src/native/ffi/types.ts +474 -0
- package/src/native/ffi.ts +1775 -0
- package/src/native/index.ts +8 -207
- package/src/native/loader.ts +240 -0
- package/src/native/types/instances.ts +274 -0
- package/src/native/types.ts +401 -0
- package/src/runtime/builtins.ts +131 -0
- package/src/runtime/codec.ts +147 -0
- package/src/runtime/detect.ts +36 -0
- package/src/runtime/index.ts +28 -0
- package/src/runtime/native.ts +35 -0
- package/src/runtime/server.ts +31 -0
- package/src/runtime/transport.ts +64 -0
- package/src/runtime/types.ts +173 -0
- package/src/runtime/uuid.ts +45 -0
- package/src/runtime/websocket.ts +30 -0
- package/src/rust-ffi/addon.ts +10 -0
- package/src/rust-ffi/batch/build.ts +155 -0
- package/src/rust-ffi/batch/index.ts +8 -0
- package/src/rust-ffi/batch/types.ts +92 -0
- package/src/rust-ffi/client.ts +87 -0
- package/src/rust-ffi/context.ts +193 -0
- package/src/rust-ffi/index.ts +51 -541
- package/src/rust-ffi/into.ts +21 -0
- package/src/rust-ffi/options.ts +113 -0
- package/src/rust-ffi/packed.ts +253 -0
- package/src/rust-ffi/proven.ts +28 -0
- package/src/rust-ffi/scalar/crypto.ts +346 -0
- package/src/rust-ffi/scalar/factories.ts +459 -0
- package/src/rust-ffi/scalar/hashing.ts +74 -0
- package/src/rust-ffi/scalar/http.ts +337 -0
- package/src/rust-ffi/scalar/index.ts +31 -0
- package/src/rust-ffi/scalar/interface.ts +325 -0
- package/src/rust-ffi/scalar/json-packed.ts +175 -0
- package/src/rust-ffi/scalar/json.ts +61 -0
- package/src/rust-ffi/scalar/payload.ts +241 -0
- package/src/rust-ffi/text.ts +121 -0
- package/src/selection.json +194 -0
- package/src/selection.ts +73 -0
- package/src/shared/adaptive.ts +67 -0
- package/src/shared/buffer-pool.ts +264 -0
- package/src/shared/bytes.ts +108 -9
- package/src/shared/codec.ts +78 -0
- package/src/shared/env.ts +20 -0
- package/src/shared/log.ts +75 -0
- package/src/shared/metrics.ts +247 -0
- package/src/shared/packed/index.ts +10 -0
- package/src/shared/packed/parsers.ts +31 -0
- package/src/shared/packed/schema.ts +23 -0
- package/src/shared/packed/wire.ts +504 -0
- package/src/shared/proven.ts +289 -0
- package/src/shared/request-id.ts +67 -0
- package/src/shared/response.ts +104 -0
- package/src/shared/runtime.ts +29 -0
- package/src/shared/trace.ts +81 -0
- package/src/shared/uuid.ts +15 -0
- package/Cargo.toml +0 -73
- package/bench.ts +0 -3
- package/rust/batch.rs +0 -195
- package/rust/cookie_parser.rs +0 -70
- package/rust/cors.rs +0 -263
- package/rust/hashing.rs +0 -164
- package/rust/headers.rs +0 -234
- package/rust/hmac_sha256.rs +0 -113
- package/rust/http_parser.rs +0 -132
- package/rust/ingress.rs +0 -616
- package/rust/ingress_constants.rs +0 -89
- package/rust/ip_trust.rs +0 -185
- package/rust/json_ops.rs +0 -66
- package/rust/json_patch_ops.rs +0 -25
- package/rust/json_schema.rs +0 -151
- package/rust/json_ser.rs +0 -429
- package/rust/lib.rs +0 -48
- package/rust/method.rs +0 -141
- package/rust/mime_lookup.rs +0 -76
- package/rust/output.rs +0 -238
- package/rust/proxy.rs +0 -90
- package/rust/query_parser.rs +0 -141
- package/rust/random_token.rs +0 -52
- package/rust/rate_limit.rs +0 -203
- package/rust/terminal.rs +0 -95
- package/rust/unit_tests.rs +0 -843
- package/rust/url_codec.rs +0 -133
- package/rust/util.rs +0 -537
- package/rust/validation.rs +0 -107
- package/rust/websocket.rs +0 -23
- package/src/baseline/index.ts +0 -12
- package/src/baseline/tasks/cookie.ts +0 -22
- package/src/baseline/tasks/hashing.ts +0 -18
- package/src/baseline/tasks/hmac.ts +0 -38
- package/src/baseline/tasks/http.ts +0 -62
- package/src/baseline/tasks/json-patch.ts +0 -12
- package/src/baseline/tasks/json.ts +0 -50
- package/src/baseline/tasks/mime.ts +0 -5
- package/src/baseline/tasks/query.ts +0 -14
- package/src/baseline/tasks/token.ts +0 -15
- package/src/baseline/tasks/url.ts +0 -11
- package/src/baseline/tasks/validation.ts +0 -19
- package/src/baseline/tasks/websocket.ts +0 -14
- package/src/bench/assert.ts +0 -70
- package/src/bench/checks.ts +0 -227
- package/src/bench/checksum.ts +0 -27
- package/src/bench/comparisons.ts +0 -70
- package/src/bench/concurrent-worker.ts +0 -135
- package/src/bench/fixtures.ts +0 -233
- package/src/bench/index.ts +0 -2
- package/src/bench/measure.ts +0 -177
- package/src/bench/now.ts +0 -3
- package/src/bench/report.ts +0 -79
- package/src/bench/run.ts +0 -57
- package/src/bench/tasks/complex.ts +0 -232
- package/src/bench/tasks/concurrent.ts +0 -213
- package/src/bench/tasks/cookie.ts +0 -44
- package/src/bench/tasks/hashing.ts +0 -33
- package/src/bench/tasks/hmac.ts +0 -36
- package/src/bench/tasks/http.ts +0 -50
- package/src/bench/tasks/index.ts +0 -52
- package/src/bench/tasks/json-patch.ts +0 -21
- package/src/bench/tasks/json.ts +0 -33
- package/src/bench/tasks/mime.ts +0 -21
- package/src/bench/tasks/query.ts +0 -44
- package/src/bench/tasks/stress.ts +0 -142
- package/src/bench/tasks/token.ts +0 -20
- package/src/bench/tasks/url.ts +0 -34
- package/src/bench/tasks/validation.ts +0 -57
- package/src/bench/tasks/websocket.ts +0 -21
- package/src/bench/types.ts +0 -60
- package/src/data/json-rows.ts +0 -33
- package/src/ingress/packed-input.ts +0 -59
- package/src/shared/json.ts +0 -18
- package/src/shared/packed.ts +0 -316
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// src/shared/proven.ts — baked "proven selection" registry (PURE DATA, no addon).
|
|
2
|
+
//
|
|
3
|
+
// The proven selection answers: "for op X, which implementation is the
|
|
4
|
+
// benchmark-proven winner, and is that what we wire as the default?"
|
|
5
|
+
//
|
|
6
|
+
// - 'native' → the rust addon wins (bun:ffi on Bun / napi on Node)
|
|
7
|
+
// - 'js' → the pure-TS implementation wins
|
|
8
|
+
// - 'bun' → a Bun built-in wins UNDER BUN and is delegated there
|
|
9
|
+
// (see src/runtime/builtins.ts)
|
|
10
|
+
//
|
|
11
|
+
// This is BAKED (committed), benchmark-derived data — the winners were
|
|
12
|
+
// measured by `scripts/select-native.ts` (the `nativeRatio` in
|
|
13
|
+
// `src/selection.json`) and the Bun built-in decision matrix
|
|
14
|
+
// (`docs/bun-builtins-decision-matrix.md`). It is PURE DATA: no addon import,
|
|
15
|
+
// no dlopen, no live benchmark audit. That live audit was the friction of the
|
|
16
|
+
// old `check:proven`/`check:annotate` machinery (flaky on noisy hosts) — it
|
|
17
|
+
// is intentionally NOT re-added. Instead `test/unit/shared/proven.test.ts`
|
|
18
|
+
// verifies every entry is wired to the impl it claims (`opImpl`, the Bun
|
|
19
|
+
// built-in registry, and the addon's embedded selection.json), so drift is
|
|
20
|
+
// caught deterministically instead of by re-running benchmarks.
|
|
21
|
+
//
|
|
22
|
+
// `typicalRatio` is the addon-vs-pure-TS-baseline ratio (rust faster = >1) for
|
|
23
|
+
// native/js entries, and the Bun-built-in-vs-addon ratio for 'bun' entries
|
|
24
|
+
// (higher = the built-in wins by more).
|
|
25
|
+
|
|
26
|
+
/** The benchmark-proven default implementation for an op. */
|
|
27
|
+
export type ProvenImpl = 'native' | 'js' | 'bun'
|
|
28
|
+
|
|
29
|
+
/** How decisive the baked winner is. */
|
|
30
|
+
export type ProvenStatus = 'proven' | 'parity' | 'unmeasured'
|
|
31
|
+
|
|
32
|
+
/** One entry in the baked proven-selection registry. */
|
|
33
|
+
export interface ProvenEntry {
|
|
34
|
+
/** Selection op name (matches `opImpl(op)` / `src/selection.json` keys). */
|
|
35
|
+
name: string
|
|
36
|
+
/** The baked default implementation: the benchmark-proven winner. */
|
|
37
|
+
impl: ProvenImpl
|
|
38
|
+
/** `proven` = decisive win, `parity` = borderline, `unmeasured` = pinned by policy. */
|
|
39
|
+
status: ProvenStatus
|
|
40
|
+
/** Measured ratio behind the decision (see module doc for the axis). */
|
|
41
|
+
typicalRatio?: number
|
|
42
|
+
/** Why this classification. */
|
|
43
|
+
note?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Baked proven-winner registry. Covering the FULL selection surface
|
|
48
|
+
* (`src/selection.json` ops + every Bun built-in delegation) so nothing can be
|
|
49
|
+
* silently dropped. Classifications are based on release/perf-build results;
|
|
50
|
+
* `test/unit/shared/proven.test.ts` re-checks them against the LIVE wiring.
|
|
51
|
+
*/
|
|
52
|
+
export const PROVEN_SELECTION: readonly ProvenEntry[] = [
|
|
53
|
+
// ── Bun built-in delegation — 'bun' (wins under Bun; consumer binding = 'js') ──
|
|
54
|
+
{
|
|
55
|
+
name: 'crc32',
|
|
56
|
+
impl: 'bun',
|
|
57
|
+
status: 'proven',
|
|
58
|
+
typicalRatio: 4,
|
|
59
|
+
note: 'Bun.hash.crc32 wins 2.8–8.4×; selection.json records js (0.659)',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'xxh3',
|
|
63
|
+
impl: 'bun',
|
|
64
|
+
status: 'proven',
|
|
65
|
+
typicalRatio: 4.15,
|
|
66
|
+
note: 'Bun.hash.xxHash3 ~4.15×; addon kept as the Node / non-Bun path',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'hmacSha256',
|
|
70
|
+
impl: 'bun',
|
|
71
|
+
status: 'parity',
|
|
72
|
+
typicalRatio: 1.2,
|
|
73
|
+
note: 'Bun.CryptoHasher mild 1.1–1.4×; the addon still beats the pure-JS baseline (1.212)',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'randomToken',
|
|
77
|
+
impl: 'bun',
|
|
78
|
+
status: 'proven',
|
|
79
|
+
typicalRatio: 1.62,
|
|
80
|
+
note: 'Bun.randomUUIDv7 / crypto.getRandomValues for token-sized output',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'gzipCompress',
|
|
84
|
+
impl: 'bun',
|
|
85
|
+
status: 'proven',
|
|
86
|
+
typicalRatio: 2.02,
|
|
87
|
+
note: 'Bun.gzipSync ~2×; gzipDecompress deliberately NOT delegated (64 MiB bomb cap)',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'urlEncode',
|
|
91
|
+
impl: 'bun',
|
|
92
|
+
status: 'proven',
|
|
93
|
+
typicalRatio: 11.5,
|
|
94
|
+
note: 'encodeURIComponent ~11.5×, zero alloc',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'urlDecode',
|
|
98
|
+
impl: 'bun',
|
|
99
|
+
status: 'proven',
|
|
100
|
+
typicalRatio: 6,
|
|
101
|
+
note: 'decodeURIComponent ~4–8×',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'base64Encode',
|
|
105
|
+
impl: 'bun',
|
|
106
|
+
status: 'proven',
|
|
107
|
+
typicalRatio: 2,
|
|
108
|
+
note: 'Buffer.toString("base64") ~2×; url-safe / unpadded fall through to native',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'base64UrlEncode',
|
|
112
|
+
impl: 'bun',
|
|
113
|
+
status: 'proven',
|
|
114
|
+
typicalRatio: 2,
|
|
115
|
+
note: 'Buffer.toString("base64url")',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'hexEncode',
|
|
119
|
+
impl: 'bun',
|
|
120
|
+
status: 'proven',
|
|
121
|
+
typicalRatio: 1.35,
|
|
122
|
+
note: 'Buffer.toString("hex")',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'httpDate',
|
|
126
|
+
impl: 'bun',
|
|
127
|
+
status: 'proven',
|
|
128
|
+
typicalRatio: 3.7,
|
|
129
|
+
note: 'Date.toUTCString() ~3.7×',
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// ── Native winners — 'native' (addon beats the pure-TS baseline) ──
|
|
133
|
+
{ name: 'fnv1a64', impl: 'native', status: 'proven', typicalRatio: 12.603 },
|
|
134
|
+
{ name: 'hmacSha256Verify', impl: 'native', status: 'proven', typicalRatio: 9.028 },
|
|
135
|
+
{ name: 'signCookie', impl: 'native', status: 'proven', typicalRatio: 3.64 },
|
|
136
|
+
{ name: 'verifyCookie', impl: 'native', status: 'proven', typicalRatio: 1.772 },
|
|
137
|
+
{ name: 'csrfToken', impl: 'native', status: 'proven', typicalRatio: 12.671 },
|
|
138
|
+
{ name: 'csrfVerify', impl: 'native', status: 'proven', typicalRatio: 12.458 },
|
|
139
|
+
{
|
|
140
|
+
name: 'passwordHash',
|
|
141
|
+
impl: 'native',
|
|
142
|
+
status: 'proven',
|
|
143
|
+
typicalRatio: 1.657,
|
|
144
|
+
note: 'argon2id — rust ~1.83× vs Bun.password at equal cost; never delegate',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'passwordVerify',
|
|
148
|
+
impl: 'native',
|
|
149
|
+
status: 'proven',
|
|
150
|
+
typicalRatio: 1.88,
|
|
151
|
+
note: 'argon2id verify — rust wins; no representative pure-TS baseline (pinned)',
|
|
152
|
+
},
|
|
153
|
+
{ name: 'aeadEncrypt', impl: 'native', status: 'proven', typicalRatio: 1.288 },
|
|
154
|
+
{
|
|
155
|
+
name: 'aeadDecrypt',
|
|
156
|
+
impl: 'native',
|
|
157
|
+
status: 'parity',
|
|
158
|
+
typicalRatio: 1.108,
|
|
159
|
+
note: 'borderline — kept native',
|
|
160
|
+
},
|
|
161
|
+
{ name: 'createAcceptNegotiator', impl: 'native', status: 'proven', typicalRatio: 2.01 },
|
|
162
|
+
{
|
|
163
|
+
name: 'gzipDecompress',
|
|
164
|
+
impl: 'native',
|
|
165
|
+
status: 'proven',
|
|
166
|
+
typicalRatio: 61.039,
|
|
167
|
+
note: 'deliberately NOT delegated — Bun.gunzipSync has no bomb cap; native 64 MiB cap kept',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'brotliCompress',
|
|
171
|
+
impl: 'native',
|
|
172
|
+
status: 'proven',
|
|
173
|
+
typicalRatio: 9.983,
|
|
174
|
+
note: 'no synchronous Bun API',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: 'brotliDecompress',
|
|
178
|
+
impl: 'native',
|
|
179
|
+
status: 'proven',
|
|
180
|
+
typicalRatio: 33.327,
|
|
181
|
+
note: 'no synchronous Bun API',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'jwtSign',
|
|
185
|
+
impl: 'native',
|
|
186
|
+
status: 'unmeasured',
|
|
187
|
+
note: 'pinned native — constant-time canonical (not pure-perf)',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'jwtVerify',
|
|
191
|
+
impl: 'native',
|
|
192
|
+
status: 'unmeasured',
|
|
193
|
+
note: 'pinned native — constant-time canonical',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: 'createTemplate',
|
|
197
|
+
impl: 'native',
|
|
198
|
+
status: 'unmeasured',
|
|
199
|
+
note: 'pinned native — compiled-once instance',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'createSchemaValidator',
|
|
203
|
+
impl: 'native',
|
|
204
|
+
status: 'unmeasured',
|
|
205
|
+
note: 'pinned native — compiled-once instance',
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// ── JS winners — 'js' (pure-TS beats the addon) ──
|
|
209
|
+
{ name: 'queryPairs', impl: 'js', status: 'proven', typicalRatio: 0.279 },
|
|
210
|
+
{ name: 'cookiePairs', impl: 'js', status: 'proven', typicalRatio: 0.105 },
|
|
211
|
+
{ name: 'formPairs', impl: 'js', status: 'proven', typicalRatio: 0.328 },
|
|
212
|
+
{ name: 'etag', impl: 'js', status: 'proven', typicalRatio: 0.187 },
|
|
213
|
+
{ name: 'parseMediaType', impl: 'js', status: 'proven', typicalRatio: 0.378 },
|
|
214
|
+
{ name: 'parseAcceptEncoding', impl: 'js', status: 'proven', typicalRatio: 0.367 },
|
|
215
|
+
{ name: 'createConditionalRequest', impl: 'js', status: 'proven', typicalRatio: 0.275 },
|
|
216
|
+
{
|
|
217
|
+
name: 'multipartParse',
|
|
218
|
+
impl: 'js',
|
|
219
|
+
status: 'unmeasured',
|
|
220
|
+
note: 'no ratio recorded — JS baseline kept',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'jsonValid',
|
|
224
|
+
impl: 'js',
|
|
225
|
+
status: 'parity',
|
|
226
|
+
typicalRatio: 0.749,
|
|
227
|
+
note: 'borderline — JSON.parse wins',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: 'jsonPatch',
|
|
231
|
+
impl: 'js',
|
|
232
|
+
status: 'proven',
|
|
233
|
+
typicalRatio: 0.014,
|
|
234
|
+
note: 'custom JSON.patch beats the addon at small sizes',
|
|
235
|
+
},
|
|
236
|
+
{ name: 'sseEncode', impl: 'js', status: 'proven', typicalRatio: 0.586 },
|
|
237
|
+
{ name: 'wsFrameEncode', impl: 'js', status: 'proven', typicalRatio: 0.152 },
|
|
238
|
+
{
|
|
239
|
+
name: 'wsFrameDecode',
|
|
240
|
+
impl: 'js',
|
|
241
|
+
status: 'parity',
|
|
242
|
+
typicalRatio: 0.988,
|
|
243
|
+
note: 'parity boundary — JS baseline kept',
|
|
244
|
+
},
|
|
245
|
+
{ name: 'wsAcceptKey', impl: 'js', status: 'parity', typicalRatio: 0.939 },
|
|
246
|
+
{ name: 'validateEmail', impl: 'js', status: 'proven', typicalRatio: 0.007 },
|
|
247
|
+
{ name: 'validateUuid', impl: 'js', status: 'proven', typicalRatio: 0.008 },
|
|
248
|
+
{ name: 'validateIpv4', impl: 'js', status: 'proven', typicalRatio: 0.012 },
|
|
249
|
+
{ name: 'validateIpv6', impl: 'js', status: 'proven', typicalRatio: 0.078 },
|
|
250
|
+
{ name: 'createRateLimiter', impl: 'js', status: 'proven', typicalRatio: 0.061 },
|
|
251
|
+
{ name: 'renderTemplate', impl: 'js', status: 'proven', typicalRatio: 0.141 },
|
|
252
|
+
] as const satisfies readonly ProvenEntry[]
|
|
253
|
+
|
|
254
|
+
const INDEX = new Map<string, ProvenEntry>(PROVEN_SELECTION.map((e) => [e.name, e]))
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The baked {@link ProvenEntry} for `op`, or `undefined` for unknown ops.
|
|
258
|
+
* Pure data — never dlopens the addon.
|
|
259
|
+
*/
|
|
260
|
+
export function provenEntry(op: string): ProvenEntry | undefined {
|
|
261
|
+
return INDEX.get(op)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** The baked default implementation for `op` (`native` / `js` / `bun`), or `null`. */
|
|
265
|
+
export function provenImpl(op: string): ProvenImpl | null {
|
|
266
|
+
return INDEX.get(op)?.impl ?? null
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** The baked status for `op`, or `null` for unknown ops. */
|
|
270
|
+
export function provenStatus(op: string): ProvenStatus | null {
|
|
271
|
+
return INDEX.get(op)?.status ?? null
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Whether the op's baked winner is a decisive (`proven`) win. */
|
|
275
|
+
export function isProven(op: string): boolean {
|
|
276
|
+
return INDEX.get(op)?.status === 'proven'
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** The full baked registry (same array as {@link PROVEN_SELECTION}). */
|
|
280
|
+
export function provenSurface(): readonly ProvenEntry[] {
|
|
281
|
+
return PROVEN_SELECTION
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** A one-line summary of the baked selection (counts per implementation). */
|
|
285
|
+
export function provenSummary(): string {
|
|
286
|
+
const byImpl = { native: 0, js: 0, bun: 0 }
|
|
287
|
+
for (const e of PROVEN_SELECTION) byImpl[e.impl] += 1
|
|
288
|
+
return `proven selection: ${byImpl.native} native, ${byImpl.js} js, ${byImpl.bun} bun built-in (${PROVEN_SELECTION.length} ops)`
|
|
289
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/shared/request-id.ts — fast counter-based request ID generator.
|
|
2
|
+
//
|
|
3
|
+
// Used by both the fast and pre-baked ingress paths. Produces a 16-byte hex
|
|
4
|
+
// string (as bytes) from a per-process counter seeded with boot randomness and
|
|
5
|
+
// an optional worker ID — no crypto, no allocation per call (reuses buffers).
|
|
6
|
+
//
|
|
7
|
+
// SIDE-EFFECT NOTE (deliberate, kept for zero-alloc hot-path perf): the module
|
|
8
|
+
// holds mutable shared state — a counter and reusable BINARY/HEX scratch
|
|
9
|
+
// buffers. `generateRequestId()` returns a SINGLE shared HEX buffer that is
|
|
10
|
+
// overwritten on the next call. Callers must decode or copy the returned bytes
|
|
11
|
+
// before the next call; the alias is confined to this module so the hazard is
|
|
12
|
+
// local and documented here.
|
|
13
|
+
|
|
14
|
+
const HEX_LOOKUP = new Uint8Array(256 * 2)
|
|
15
|
+
for (let i = 0; i < 256; i++) {
|
|
16
|
+
HEX_LOOKUP[i * 2] = '0123456789abcdef'.charCodeAt(i >> 4)
|
|
17
|
+
HEX_LOOKUP[i * 2 + 1] = '0123456789abcdef'.charCodeAt(i & 0x0f)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const BINARY = new Uint8Array(8)
|
|
21
|
+
const HEX = new Uint8Array(16)
|
|
22
|
+
|
|
23
|
+
const WORKER_ID = Number(process.env.INGRESS_WORKER_ID || 0) & 0xffff
|
|
24
|
+
// Boot randomness: one webcrypto draw, falling back to Math.random for hosts
|
|
25
|
+
// (older Node) without a `crypto` global. `[0]` is `number | undefined` under
|
|
26
|
+
// noUncheckedIndexedAccess — the `??` supplies the fallback for that too.
|
|
27
|
+
const BOOT_RANDOM =
|
|
28
|
+
typeof crypto !== 'undefined'
|
|
29
|
+
? (crypto.getRandomValues(new Uint32Array(1))[0] ?? (Math.random() * 0xffffffff) >>> 0)
|
|
30
|
+
: (Math.random() * 0xffffffff) >>> 0
|
|
31
|
+
|
|
32
|
+
const COUNTER = {
|
|
33
|
+
hi: (BOOT_RANDOM ^ (WORKER_ID << 16)) >>> 0,
|
|
34
|
+
lo: 0,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Generate the next request ID as 16 lowercase hex bytes (zero-alloc).
|
|
39
|
+
*
|
|
40
|
+
* @returns A shared buffer aliased across calls — decode/copy before the next
|
|
41
|
+
* `generateRequestId()` invocation.
|
|
42
|
+
*/
|
|
43
|
+
export function generateRequestId(): Uint8Array {
|
|
44
|
+
if (COUNTER.lo === 0xffffffff) {
|
|
45
|
+
COUNTER.lo = 0
|
|
46
|
+
COUNTER.hi = (COUNTER.hi + 1) >>> 0
|
|
47
|
+
} else {
|
|
48
|
+
COUNTER.lo++
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
BINARY[0] = (COUNTER.hi >>> 24) & 0xff
|
|
52
|
+
BINARY[1] = (COUNTER.hi >>> 16) & 0xff
|
|
53
|
+
BINARY[2] = (COUNTER.hi >>> 8) & 0xff
|
|
54
|
+
BINARY[3] = COUNTER.hi & 0xff
|
|
55
|
+
BINARY[4] = (COUNTER.lo >>> 24) & 0xff
|
|
56
|
+
BINARY[5] = (COUNTER.lo >>> 16) & 0xff
|
|
57
|
+
BINARY[6] = (COUNTER.lo >>> 8) & 0xff
|
|
58
|
+
BINARY[7] = COUNTER.lo & 0xff
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < 8; i++) {
|
|
61
|
+
const b = BINARY[i] ?? 0
|
|
62
|
+
HEX[i * 2] = HEX_LOOKUP[b * 2] ?? 0
|
|
63
|
+
HEX[i * 2 + 1] = HEX_LOOKUP[b * 2 + 1] ?? 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return HEX
|
|
67
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// src/shared/response.ts — Response helpers for pooled (reusable) output buffers.
|
|
2
|
+
//
|
|
3
|
+
// When a response body is backed by a pooled buffer, the pool handle must not
|
|
4
|
+
// be released until the body has actually been consumed, otherwise a later
|
|
5
|
+
// request could overwrite bytes the in-flight response still references.
|
|
6
|
+
// `pooledBodyResponse` wraps the slice in a streaming body that returns the
|
|
7
|
+
// handle to its pool once the body is read (or the request is aborted).
|
|
8
|
+
|
|
9
|
+
import type { PooledBuffer } from './buffer-pool'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build a `Response` whose body is backed by a pooled byte slice, returning the
|
|
13
|
+
* pool handle once the body has been fully consumed (stream closed) or the
|
|
14
|
+
* request has been cancelled.
|
|
15
|
+
*
|
|
16
|
+
* The pooled buffer stays **in flight** (not reusable) until the response
|
|
17
|
+
* stream is closed/cancelled — this is what makes zero-copy pooling safe: a
|
|
18
|
+
* buffer is never handed to a second request while a previous response may
|
|
19
|
+
* still alias it.
|
|
20
|
+
*
|
|
21
|
+
* @param handle - The pooled-buffer handle to release on consumption.
|
|
22
|
+
* @param bytes - The (possibly shared) byte slice to serve as the body.
|
|
23
|
+
* @param init - Standard `ResponseInit` (status, headers, ...).
|
|
24
|
+
* @param timeoutMs - Optional abandonment guard (ms, default 0 = disabled):
|
|
25
|
+
* if the body is neither pulled nor cancelled within this window the pooled
|
|
26
|
+
* buffer is released and the stream closed. Opt-in — lets zero-copy
|
|
27
|
+
* responses bound memory under abandoned (unread) responses.
|
|
28
|
+
* @returns A `Response` that releases `handle` once its body is consumed.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* **Zero-copy caveat**: the body slice is served without copying — it aliases
|
|
32
|
+
* the pooled buffer. Consumers that hold a response open for a long time keep
|
|
33
|
+
* that buffer out of circulation. This helper is only intended for callers that
|
|
34
|
+
* explicitly opt into zero-copy (e.g. `INGRESS_UNSAFE_ZERO_COPY`); the default
|
|
35
|
+
* safe path copies the slice and releases the handle eagerly.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const handle = pool.acquire(size);
|
|
40
|
+
* const used = handle.buffer.subarray(0, written);
|
|
41
|
+
* return pooledBodyResponse(handle, used, { status: 200, headers });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function pooledBodyResponse(
|
|
45
|
+
handle: PooledBuffer,
|
|
46
|
+
bytes: Uint8Array,
|
|
47
|
+
init: ResponseInit = {},
|
|
48
|
+
timeoutMs = 0,
|
|
49
|
+
): Response {
|
|
50
|
+
if (bytes.byteLength === 0) {
|
|
51
|
+
// Nothing to serve: release immediately.
|
|
52
|
+
handle.release()
|
|
53
|
+
return new Response(null, init)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let released = false
|
|
57
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
58
|
+
const release = (): void => {
|
|
59
|
+
if (released) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
released = true
|
|
63
|
+
if (timer !== undefined) {
|
|
64
|
+
clearTimeout(timer)
|
|
65
|
+
}
|
|
66
|
+
handle.release()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const body = new ReadableStream<Uint8Array>({
|
|
70
|
+
start(controller) {
|
|
71
|
+
// Abandonment guard (opt-in): if the body is neither pulled nor
|
|
72
|
+
// cancelled within `timeoutMs`, release the pooled buffer so it can be
|
|
73
|
+
// reused and close the stream. Without this, an abandoned Response would
|
|
74
|
+
// hold the pooled buffer in flight forever (bounded only by maxInFlight).
|
|
75
|
+
// `pull()` re-checks `released` so a late read never serves the reused
|
|
76
|
+
// (potentially overwritten) bytes.
|
|
77
|
+
if (timeoutMs > 0) {
|
|
78
|
+
timer = setTimeout(() => {
|
|
79
|
+
if (released) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
release()
|
|
83
|
+
controller.close()
|
|
84
|
+
}, timeoutMs)
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
pull(controller) {
|
|
88
|
+
if (released) {
|
|
89
|
+
// Released by the abandonment guard (or a racing cancel): the pooled
|
|
90
|
+
// bytes may already be reused — never serve them. End-of-stream.
|
|
91
|
+
controller.close()
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
controller.enqueue(bytes)
|
|
95
|
+
controller.close()
|
|
96
|
+
release()
|
|
97
|
+
},
|
|
98
|
+
cancel() {
|
|
99
|
+
release()
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return new Response(body, init)
|
|
104
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/shared/runtime.ts — runtime detection helpers (facade).
|
|
2
|
+
//
|
|
3
|
+
// Facade over the cached detection in `src/runtime/detect.ts` (the ONLY place
|
|
4
|
+
// `typeof Bun` is checked — resolved ONCE at module load by the runtime
|
|
5
|
+
// adapter). Kept so existing `isBun()` / `isNode()` / `runtimeName()` /
|
|
6
|
+
// `nodeMajorVersion()` call sites work unchanged.
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
detectedRuntime,
|
|
10
|
+
isBunRuntime,
|
|
11
|
+
isNodeRuntime,
|
|
12
|
+
nodeMajorVersion as nodeMajorVersionDetected,
|
|
13
|
+
} from '../runtime/detect'
|
|
14
|
+
import type { RuntimeName as RuntimeNameType } from '../runtime/types'
|
|
15
|
+
|
|
16
|
+
/** Host runtime identifier. */
|
|
17
|
+
export type RuntimeName = RuntimeNameType
|
|
18
|
+
|
|
19
|
+
/** Whether the current runtime is Bun (the primary target). Cached once at load. */
|
|
20
|
+
export const isBun: () => boolean = isBunRuntime
|
|
21
|
+
|
|
22
|
+
/** Whether the current runtime is Node.js. */
|
|
23
|
+
export const isNode: () => boolean = isNodeRuntime
|
|
24
|
+
|
|
25
|
+
/** Best-effort name of the current runtime. */
|
|
26
|
+
export const runtimeName: () => RuntimeName = () => detectedRuntime
|
|
27
|
+
|
|
28
|
+
/** The Node.js major version when running under Node, otherwise `null`. */
|
|
29
|
+
export const nodeMajorVersion: () => number | null = nodeMajorVersionDetected
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/shared/trace.ts — W3C Trace Context helpers (zero-dep).
|
|
2
|
+
//
|
|
3
|
+
// Parses the `traceparent` request header and generates span ids so ingress
|
|
4
|
+
// logs/hooks can correlate requests across services. This is the "tracing"
|
|
5
|
+
// primitive — wire `parseTraceContext(req)` in an `onRequest` hook and pass the
|
|
6
|
+
// traceId/spanId into your structured logger (pino/otel), or use it directly.
|
|
7
|
+
//
|
|
8
|
+
// Format: `version-traceid-spanid-flags` (e.g. `00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01`).
|
|
9
|
+
|
|
10
|
+
/** A parsed W3C trace context (valid per the spec's length/hex rules). */
|
|
11
|
+
export interface TraceContext {
|
|
12
|
+
/** Version, always `"00"` for accepted headers. */
|
|
13
|
+
version: string
|
|
14
|
+
/** 32 hex chars — the trace id (shared across the whole trace). */
|
|
15
|
+
traceId: string
|
|
16
|
+
/** 16 hex chars — the current span id. */
|
|
17
|
+
spanId: string
|
|
18
|
+
/** The flags byte (bit 0 = sampled). */
|
|
19
|
+
flags: number
|
|
20
|
+
/** True when the `sampled` (bit 0) flag is set. */
|
|
21
|
+
sampled: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const HEX = /^[0-9a-fA-F]+$/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parse a `traceparent` header value into a {@link TraceContext}, or `null`
|
|
28
|
+
* when malformed (wrong version / length / non-hex). Invalid contexts are
|
|
29
|
+
* ignored per the spec — callers should generate a fresh trace then.
|
|
30
|
+
*/
|
|
31
|
+
export function parseTraceParent(header: string | null | undefined): TraceContext | null {
|
|
32
|
+
if (!header) return null
|
|
33
|
+
const parts = header.trim().split('-')
|
|
34
|
+
if (parts.length !== 4) return null
|
|
35
|
+
const [version, traceId, spanId, flagsStr] = parts
|
|
36
|
+
// Only version 00 is currently defined.
|
|
37
|
+
if (version !== '00') return null
|
|
38
|
+
if (traceId === undefined || traceId.length !== 32 || !HEX.test(traceId)) return null
|
|
39
|
+
if (spanId === undefined || spanId.length !== 16 || !HEX.test(spanId)) return null
|
|
40
|
+
if (flagsStr === undefined || flagsStr.length !== 2 || !HEX.test(flagsStr)) return null
|
|
41
|
+
const flags = Number.parseInt(flagsStr, 16)
|
|
42
|
+
return {
|
|
43
|
+
version,
|
|
44
|
+
traceId: traceId.toLowerCase(),
|
|
45
|
+
spanId: spanId.toLowerCase(),
|
|
46
|
+
flags,
|
|
47
|
+
sampled: (flags & 0x01) === 0x01,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Random 32-hex trace id (crypto-backed where available). */
|
|
52
|
+
export function createTraceId(): string {
|
|
53
|
+
return randomHex(16)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Random 16-hex span id. */
|
|
57
|
+
export function createSpanId(): string {
|
|
58
|
+
return randomHex(8)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Generate `n` random bytes as lowercase hex. */
|
|
62
|
+
function randomHex(bytes: number): string {
|
|
63
|
+
const buf = new Uint8Array(bytes)
|
|
64
|
+
const c = globalThis.crypto
|
|
65
|
+
if (c?.getRandomValues) {
|
|
66
|
+
c.getRandomValues(buf)
|
|
67
|
+
} else {
|
|
68
|
+
for (let i = 0; i < bytes; i++) buf[i] = Math.floor(Math.random() * 256)
|
|
69
|
+
}
|
|
70
|
+
let out = ''
|
|
71
|
+
for (let i = 0; i < bytes; i++) {
|
|
72
|
+
out += (buf[i] ?? 0).toString(16).padStart(2, '0')
|
|
73
|
+
}
|
|
74
|
+
return out
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Serialize a trace context back to a `traceparent` header value. */
|
|
78
|
+
export function serializeTraceParent(ctx: TraceContext): string {
|
|
79
|
+
const flags = ctx.flags.toString(16).padStart(2, '0')
|
|
80
|
+
return `${ctx.version}-${ctx.traceId}-${ctx.spanId}-${flags}`
|
|
81
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/shared/uuid.ts — UUIDv7 generation (facade).
|
|
2
|
+
//
|
|
3
|
+
// UUIDv7 delegates to the runtime adapter's uuid (`src/runtime/uuid.ts`):
|
|
4
|
+
// `Bun.randomUUIDv7` on Bun (measured ~2x faster than the FFI-crossing rust
|
|
5
|
+
// path) and `crypto.randomUUID` (RFC 4122 v4) under Node.
|
|
6
|
+
|
|
7
|
+
import { runtimeUuid } from '../runtime/uuid'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generate a UUIDv7 (time-ordered) string, preferring Bun's native built-in.
|
|
11
|
+
* Under Node this falls back to `crypto.randomUUID` (v4).
|
|
12
|
+
*
|
|
13
|
+
* @returns A UUID string.
|
|
14
|
+
*/
|
|
15
|
+
export const uuidv7: () => string = runtimeUuid.uuidv7
|
package/Cargo.toml
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
[package]
|
|
2
|
-
name = "castrum"
|
|
3
|
-
# Keep in sync with package.json "version" (single source of truth: the npm package).
|
|
4
|
-
version = "0.7.0"
|
|
5
|
-
edition = "2021"
|
|
6
|
-
|
|
7
|
-
[lib]
|
|
8
|
-
name = "castrum"
|
|
9
|
-
crate-type = ["cdylib"]
|
|
10
|
-
path = "rust/lib.rs"
|
|
11
|
-
|
|
12
|
-
[dependencies]
|
|
13
|
-
napi = { version = "3", features = ["napi10"] }
|
|
14
|
-
napi-derive = "3"
|
|
15
|
-
|
|
16
|
-
serde = { version = "1", features = ["derive"] }
|
|
17
|
-
serde_json = "1"
|
|
18
|
-
sonic-rs = "0.5"
|
|
19
|
-
|
|
20
|
-
# default-features=false: drop resolve-http + tls-aws-lc-rs which pull in
|
|
21
|
-
# reqwest -> hyper/h2/tokio/rustls (large binary, attack surface). Format
|
|
22
|
-
# validators (email etc.) are built-in and NOT feature-gated in 0.48.
|
|
23
|
-
jsonschema = { version = "0.48", default-features = false, features = ["resolve-file"] }
|
|
24
|
-
json-patch = "4"
|
|
25
|
-
aws-lc-rs = "1"
|
|
26
|
-
base64 = "0.23"
|
|
27
|
-
crc32fast = "1"
|
|
28
|
-
httparse = "1"
|
|
29
|
-
mime_guess = "2"
|
|
30
|
-
phf = { version = "0.14", features = ["macros"] }
|
|
31
|
-
getrandom = "0.4"
|
|
32
|
-
memchr = "2"
|
|
33
|
-
simdutf8 = "0.1"
|
|
34
|
-
email_address = "0.2"
|
|
35
|
-
fast_chemail = { version = "0.9", optional = true }
|
|
36
|
-
rayon = "1"
|
|
37
|
-
# mimalloc is set as the Rust #[global_allocator] (see rust/lib.rs). We DELIBERATELY
|
|
38
|
-
# do NOT enable the "override" feature: it interposes libc malloc/free at the
|
|
39
|
-
# process level, which is an embedding hazard when this cdylib is dlopen'd into
|
|
40
|
-
# Bun (which itself embeds mimalloc -> symbol clashes). The Rust-level global
|
|
41
|
-
# allocator keeps most of the allocation win without replacing the host malloc.
|
|
42
|
-
mimalloc = { version = "0.1", features = ["local_dynamic_tls"] }
|
|
43
|
-
core_affinity = "0.8"
|
|
44
|
-
|
|
45
|
-
parking_lot = "0.12"
|
|
46
|
-
xxhash-rust = { version = "0.8", features = ["xxh3"] }
|
|
47
|
-
ipnet = "2"
|
|
48
|
-
lru = "0.12"
|
|
49
|
-
|
|
50
|
-
[build-dependencies]
|
|
51
|
-
napi-build = "2"
|
|
52
|
-
|
|
53
|
-
[features]
|
|
54
|
-
default = ["fast-email"]
|
|
55
|
-
fast-email = ["dep:fast_chemail"]
|
|
56
|
-
|
|
57
|
-
[profile.release]
|
|
58
|
-
opt-level = 3
|
|
59
|
-
lto = "fat"
|
|
60
|
-
codegen-units = 1
|
|
61
|
-
strip = "symbols"
|
|
62
|
-
debug = 0
|
|
63
|
-
incremental = false
|
|
64
|
-
# Safety: this is an embedded cdylib loaded into the Bun process. panic = "unwind"
|
|
65
|
-
# (the default) lets napi's catch_unwind convert Rust panics into JS exceptions
|
|
66
|
-
# instead of aborting the host process. overflow-checks = true catches integer
|
|
67
|
-
# overflow in release; the cost is negligible relative to the safety gain.
|
|
68
|
-
overflow-checks = true
|
|
69
|
-
debug-assertions = false
|
|
70
|
-
|
|
71
|
-
[profile.release.package."*"]
|
|
72
|
-
opt-level = 3
|
|
73
|
-
codegen-units = 1
|
package/bench.ts
DELETED